Merge "API feedback: make bubble pref an int def" into sc-dev
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
index e585d91..4357905 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
@@ -16,6 +16,7 @@
 
 package android.app.appsearch;
 
+import static android.app.appsearch.AppSearchResult.RESULT_INVALID_SCHEMA;
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
 import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
 
@@ -27,6 +28,7 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.util.ArraySet;
 
 import com.android.internal.infra.AndroidFuture;
 
@@ -39,8 +41,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 
 /**
@@ -55,23 +57,23 @@
     private final String mDatabaseName;
     private final int mUserId;
     private final File mMigratedFile;
-    private final Map<String, Integer> mCurrentVersionMap;
-    private final Map<String, Integer> mFinalVersionMap;
+    private final Set<String> mDestinationTypes;
     private boolean mAreDocumentsMigrated = false;
 
     AppSearchMigrationHelper(@NonNull IAppSearchManager service,
             @UserIdInt int userId,
-            @NonNull Map<String, Integer> currentVersionMap,
-            @NonNull Map<String, Integer> finalVersionMap,
             @NonNull String packageName,
-            @NonNull String databaseName) throws IOException {
+            @NonNull String databaseName,
+            @NonNull Set<AppSearchSchema> newSchemas) throws IOException {
         mService = Objects.requireNonNull(service);
-        mCurrentVersionMap = Objects.requireNonNull(currentVersionMap);
-        mFinalVersionMap = Objects.requireNonNull(finalVersionMap);
         mPackageName = Objects.requireNonNull(packageName);
         mDatabaseName = Objects.requireNonNull(databaseName);
         mUserId = userId;
         mMigratedFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
+        mDestinationTypes = new ArraySet<>(newSchemas.size());
+        for (AppSearchSchema newSchema : newSchemas) {
+            mDestinationTypes.add(newSchema.getSchemaType());
+        }
     }
 
     /**
@@ -87,7 +89,8 @@
      *     GenericDocument} to new version.
      */
     @WorkerThread
-    public void queryAndTransform(@NonNull String schemaType, @NonNull Migrator migrator)
+    public void queryAndTransform(@NonNull String schemaType, @NonNull Migrator migrator,
+            int currentVersion, int finalVersion)
             throws IOException, AppSearchException, InterruptedException, ExecutionException {
         File queryFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
         try (ParcelFileDescriptor fileDescriptor =
@@ -111,7 +114,7 @@
             if (!result.isSuccess()) {
                 throw new AppSearchException(result.getResultCode(), result.getErrorMessage());
             }
-            readAndTransform(queryFile, migrator);
+            readAndTransform(queryFile, migrator, currentVersion, finalVersion);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } finally {
@@ -173,8 +176,9 @@
      *
      * <p>Save migrated {@link GenericDocument}s to the {@link #mMigratedFile}.
      */
-    private void readAndTransform(@NonNull File file, @NonNull Migrator migrator)
-            throws IOException {
+    private void readAndTransform(@NonNull File file, @NonNull Migrator migrator,
+            int currentVersion, int finalVersion)
+            throws IOException, AppSearchException {
         try (DataInputStream inputStream = new DataInputStream(new FileInputStream(file));
              DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(
                      mMigratedFile, /*append=*/ true))) {
@@ -187,9 +191,6 @@
                     // Nothing wrong. We just finished reading.
                 }
 
-                int currentVersion = mCurrentVersionMap.get(document.getSchemaType());
-                int finalVersion = mFinalVersionMap.get(document.getSchemaType());
-
                 GenericDocument newDocument;
                 if (currentVersion < finalVersion) {
                     newDocument = migrator.onUpgrade(currentVersion, finalVersion, document);
@@ -197,6 +198,18 @@
                     // currentVersion == finalVersion case won't trigger migration and get here.
                     newDocument = migrator.onDowngrade(currentVersion, finalVersion, document);
                 }
+
+                if (!mDestinationTypes.contains(newDocument.getSchemaType())) {
+                    // we exit before the new schema has been set to AppSearch. So no
+                    // observable changes will be applied to stored schemas and documents.
+                    // And the temp file will be deleted at close(), which will be triggered at
+                    // the end of try-with-resources block of SearchSessionImpl.
+                    throw new AppSearchException(
+                            RESULT_INVALID_SCHEMA,
+                            "Receive a migrated document with schema type: "
+                                    + newDocument.getSchemaType()
+                                    + ". But the schema types doesn't exist in the request");
+                }
                 writeBundleToOutputStream(outputStream, newDocument.getBundle());
             }
             mAreDocumentsMigrated = true;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 4dd1b79..3677489 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -19,7 +19,6 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
-import android.app.appsearch.exceptions.AppSearchException;
 import android.app.appsearch.util.SchemaMigrationUtil;
 import android.os.Bundle;
 import android.os.ParcelableException;
@@ -647,8 +646,8 @@
                     new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
                     schemasPackageAccessibleBundles,
                     request.isForceOverride(),
-                    mUserId,
                     request.getVersion(),
+                    mUserId,
                     new IAppSearchResultCallback.Stub() {
                         public void onResult(AppSearchResult result) {
                             executor.execute(() -> {
@@ -661,7 +660,7 @@
                                             // Throw exception if there is any deleted types or
                                             // incompatible types. That's the only case we swallowed
                                             // in the AppSearchImpl#setSchema().
-                                            checkDeletedAndIncompatible(
+                                            SchemaMigrationUtil.checkDeletedAndIncompatible(
                                                     setSchemaResponse.getDeletedTypes(),
                                                     setSchemaResponse.getIncompatibleTypes());
                                         }
@@ -698,7 +697,7 @@
         workExecutor.execute(() -> {
             try {
                 // Migration process
-                // 1. Generate the current and the final version map.
+                // 1. Validate and retrieve all active migrators.
                 AndroidFuture<AppSearchResult<GetSchemaResponse>> getSchemaFuture =
                         new AndroidFuture<>();
                 getSchema(callbackExecutor, getSchemaFuture::complete);
@@ -709,11 +708,18 @@
                     return;
                 }
                 GetSchemaResponse getSchemaResponse = getSchemaResult.getResultValue();
-                Set<AppSearchSchema> currentSchemas = getSchemaResponse.getSchemas();
-                Map<String, Integer> currentVersionMap = SchemaMigrationUtil.buildVersionMap(
-                        currentSchemas, getSchemaResponse.getVersion());
-                Map<String, Integer> finalVersionMap = SchemaMigrationUtil.buildVersionMap(
-                        request.getSchemas(), request.getVersion());
+                int currentVersion = getSchemaResponse.getVersion();
+                int finalVersion = request.getVersion();
+                Map<String, Migrator> activeMigrators = SchemaMigrationUtil.getActiveMigrators(
+                        getSchemaResponse.getSchemas(), request.getMigrators(), currentVersion,
+                        finalVersion);
+
+                // No need to trigger migration if no migrator is active.
+                if (activeMigrators.isEmpty()) {
+                    setSchemaNoMigrations(request, schemaBundles, schemasPackageAccessibleBundles,
+                            callbackExecutor, callback);
+                    return;
+                }
 
                 // 2. SetSchema with forceOverride=false, to retrieve the list of
                 // incompatible/deleted types.
@@ -725,8 +731,8 @@
                         new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
                         schemasPackageAccessibleBundles,
                         /*forceOverride=*/ false,
-                        mUserId,
                         request.getVersion(),
+                        mUserId,
                         new IAppSearchResultCallback.Stub() {
                             public void onResult(AppSearchResult result) {
                                 setSchemaFuture.complete(result);
@@ -741,46 +747,27 @@
                 SetSchemaResponse setSchemaResponse =
                         new SetSchemaResponse(setSchemaResult.getResultValue());
 
-                // 1. If forceOverride is false, check that all incompatible types will be migrated.
+                // 3. If forceOverride is false, check that all incompatible types will be migrated.
                 // If some aren't we must throw an error, rather than proceeding and deleting those
                 // types.
                 if (!request.isForceOverride()) {
-                    Set<String> unmigratedTypes =
-                            SchemaMigrationUtil.getUnmigratedIncompatibleTypes(
-                                    setSchemaResponse.getIncompatibleTypes(),
-                                    request.getMigrators(),
-                                    currentVersionMap,
-                                    finalVersionMap);
-
-                    // check if there are any unmigrated types or deleted types. If there are, we
-                    // will throw an exception.
-                    // Since the force override is false, the schema will not have been set if there
-                    // are any incompatible or deleted types.
-                    checkDeletedAndIncompatible(
-                            setSchemaResponse.getDeletedTypes(), unmigratedTypes);
+                    SchemaMigrationUtil.checkDeletedAndIncompatibleAfterMigration(setSchemaResponse,
+                            activeMigrators.keySet());
                 }
 
-                try (AppSearchMigrationHelper migrationHelper =
-                             new AppSearchMigrationHelper(
-                                     mService, mUserId, currentVersionMap, finalVersionMap,
-                                     mPackageName, mDatabaseName)) {
-                    Map<String, Migrator> migratorMap = request.getMigrators();
+                try (AppSearchMigrationHelper migrationHelper = new AppSearchMigrationHelper(
+                        mService, mUserId, mPackageName, mDatabaseName, request.getSchemas())) {
 
-                    // 2. Trigger migration for all migrators.
+                    // 4. Trigger migration for all migrators.
                     // TODO(b/177266929) trigger migration for all types together rather than
                     //  separately.
-                    Set<String> migratedTypes = new ArraySet<>();
-                    for (Map.Entry<String, Migrator> entry : migratorMap.entrySet()) {
-                        String schemaType = entry.getKey();
-                        Migrator migrator = entry.getValue();
-                        if (SchemaMigrationUtil.shouldTriggerMigration(
-                                schemaType, migrator, currentVersionMap, finalVersionMap)) {
-                            migrationHelper.queryAndTransform(schemaType, migrator);
-                            migratedTypes.add(schemaType);
-                        }
+                    for (Map.Entry<String, Migrator> entry : activeMigrators.entrySet()) {
+                        migrationHelper.queryAndTransform(/*schemaType=*/ entry.getKey(),
+                                /*migrator=*/ entry.getValue(), currentVersion,
+                                finalVersion);
                     }
 
-                    // 3. SetSchema a second time with forceOverride=true if the first attempted
+                    // 5. SetSchema a second time with forceOverride=true if the first attempted
                     // failed.
                     if (!setSchemaResponse.getIncompatibleTypes().isEmpty()
                             || !setSchemaResponse.getDeletedTypes().isEmpty()) {
@@ -809,13 +796,16 @@
                             // error in the first setSchema call, all other errors will be thrown at
                             // the first time.
                             callbackExecutor.execute(() -> callback.accept(
-                                    AppSearchResult.newFailedResult(setSchemaResult)));
+                                    AppSearchResult.newFailedResult(setSchema2Result)));
                             return;
                         }
                     }
 
                     SetSchemaResponse.Builder responseBuilder = setSchemaResponse.toBuilder()
-                            .addMigratedTypes(migratedTypes);
+                            .addMigratedTypes(activeMigrators.keySet());
+
+                    // 6. Put all the migrated documents into the index, now that the new schema is
+                    // set.
                     AppSearchResult<SetSchemaResponse> putResult =
                             migrationHelper.putMigratedDocuments(responseBuilder);
                     callbackExecutor.execute(() -> callback.accept(putResult));
@@ -826,17 +816,4 @@
             }
         });
     }
-
-    /**  Checks the setSchema() call won't delete any types or has incompatible types. */
-    //TODO(b/177266929) move this method to util
-    private void checkDeletedAndIncompatible(Set<String> deletedTypes,
-            Set<String> incompatibleTypes)
-            throws AppSearchException {
-        if (!deletedTypes.isEmpty() || !incompatibleTypes.isEmpty()) {
-            String newMessage = "Schema is incompatible."
-                    + "\n  Deleted types: " + deletedTypes
-                    + "\n  Incompatible types: " + incompatibleTypes;
-            throw new AppSearchException(AppSearchResult.RESULT_INVALID_SCHEMA, newMessage);
-        }
-    }
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index a8ac27c..4d05ad7 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -40,6 +40,7 @@
      *     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.
+     * @param schemaVersion  The overall schema version number of the request.
      * @param userId Id of the calling user
      * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
      *     {@link AppSearchResult}&lt;{@link Bundle}&gt;, where the value are
@@ -52,8 +53,8 @@
         in List<String> schemasNotDisplayedBySystem,
         in Map<String, List<Bundle>> schemasPackageAccessibleBundles,
         boolean forceOverride,
-        in int userId,
         in int schemaVersion,
+        in int userId,
         in IAppSearchResultCallback callback);
 
     /**
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 1324451..5672bc7 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -297,11 +297,26 @@
         }
 
         /**
-         * Sets the {@link Migrator}.
+         * Sets the {@link Migrator} associated with the given SchemaType.
+         *
+         * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type
+         * from the current version number stored in AppSearch to the final version set via {@link
+         * #setVersion}.
+         *
+         * <p>A {@link Migrator} will be invoked if the current version number stored in AppSearch
+         * is different from the final version set via {@link #setVersion} and {@link
+         * Migrator#shouldMigrate} returns {@code true}.
+         *
+         * <p>The target schema type of the output {@link GenericDocument} of {@link
+         * Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link
+         * SetSchemaRequest}.
          *
          * @param schemaType The schema type to set migrator on.
-         * @param migrator The migrator translate a document from it's old version to a new
-         *     incompatible version.
+         * @param migrator The migrator translates a document from its current version to the final
+         *     version set via {@link #setVersion}.
+         * @see SetSchemaRequest.Builder#setVersion
+         * @see SetSchemaRequest.Builder#addSchemas
+         * @see AppSearchSession#setSchema
          */
         @NonNull
         @SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects.
@@ -313,10 +328,25 @@
         }
 
         /**
-         * Sets {@link Migrator}s.
+         * Sets a Map of {@link Migrator}s.
          *
-         * @param migrators A {@link Map} of migrators that translate a document from its old
-         *     version to a new incompatible version.
+         * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type
+         * from the current version number stored in AppSearch to the final version set via {@link
+         * #setVersion}.
+         *
+         * <p>A {@link Migrator} will be invoked if the current version number stored in AppSearch
+         * is different from the final version set via {@link #setVersion} and {@link
+         * Migrator#shouldMigrate} returns {@code true}.
+         *
+         * <p>The target schema type of the output {@link GenericDocument} of {@link
+         * Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link
+         * SetSchemaRequest}.
+         *
+         * @param migrators A {@link Map} of migrators that translate a document from it's current
+         *     version to the final version set via {@link #setVersion}.
+         * @see SetSchemaRequest.Builder#setVersion
+         * @see SetSchemaRequest.Builder#addSchemas
+         * @see AppSearchSession#setSchema
          */
         @NonNull
         public Builder setMigrators(@NonNull Map<String, Migrator> migrators) {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
index c9473bd..32d7e043 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
@@ -20,12 +20,11 @@
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSchema;
 import android.app.appsearch.Migrator;
+import android.app.appsearch.SetSchemaResponse;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.Log;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
@@ -36,84 +35,70 @@
  * @hide
  */
 public final class SchemaMigrationUtil {
-    private static final String TAG = "AppSearchMigrateUtil";
-
     private SchemaMigrationUtil() {}
 
-    /**
-     * Finds out which incompatible schema type won't be migrated by comparing its current and final
-     * version number.
-     */
+    /** Returns all active {@link Migrator}s that need to be triggered in this migration. */
     @NonNull
-    public static Set<String> getUnmigratedIncompatibleTypes(
-            @NonNull Set<String> incompatibleSchemaTypes,
+    public static Map<String, Migrator> getActiveMigrators(
+            @NonNull Set<AppSearchSchema> existingSchemas,
             @NonNull Map<String, Migrator> migrators,
-            @NonNull Map<String, Integer> currentVersionMap,
-            @NonNull Map<String, Integer> finalVersionMap)
-            throws AppSearchException {
-        Set<String> unmigratedSchemaTypes = new ArraySet<>();
-        for (String unmigratedSchemaType : incompatibleSchemaTypes) {
-            Integer currentVersion = currentVersionMap.get(unmigratedSchemaType);
-            Integer finalVersion = finalVersionMap.get(unmigratedSchemaType);
-            if (currentVersion == null) {
-                // impossible, we have done something wrong.
-                throw new AppSearchException(
-                        AppSearchResult.RESULT_UNKNOWN_ERROR,
-                        "Cannot find the current version number for schema type: "
-                                + unmigratedSchemaType);
-            }
-            if (finalVersion == null) {
-                // The schema doesn't exist in the SetSchemaRequest.
-                unmigratedSchemaTypes.add(unmigratedSchemaType);
-                continue;
-            }
-            // we don't have migrator or won't trigger migration for this schema type.
-            Migrator migrator = migrators.get(unmigratedSchemaType);
-            if (migrator == null
-                    || !migrator.shouldMigrate(currentVersion, finalVersion)) {
-                unmigratedSchemaTypes.add(unmigratedSchemaType);
+            int currentVersion,
+            int finalVersion) {
+        if (currentVersion == finalVersion) {
+            return Collections.emptyMap();
+        }
+        Set<String> existingTypes = new ArraySet<>(existingSchemas.size());
+        for (AppSearchSchema schema : existingSchemas) {
+            existingTypes.add(schema.getSchemaType());
+        }
+
+        Map<String, Migrator> activeMigrators = new ArrayMap<>();
+        for (Map.Entry<String, Migrator> entry : migrators.entrySet()) {
+            // The device contains the source type, and we should trigger migration for the type.
+            String schemaType = entry.getKey();
+            Migrator migrator = entry.getValue();
+            if (existingTypes.contains(schemaType)
+                    && migrator.shouldMigrate(currentVersion, finalVersion)) {
+                activeMigrators.put(schemaType, migrator);
             }
         }
-        return Collections.unmodifiableSet(unmigratedSchemaTypes);
+        return activeMigrators;
     }
 
     /**
-     * Triggers upgrade or downgrade migration for the given schema type if its version stored in
-     * AppSearch is different with the version in the request.
-     *
-     * @return {@code True} if we trigger the migration for the given type.
+     * Checks the setSchema() call won't delete any types or has incompatible types after all {@link
+     * Migrator} has been triggered..
      */
-    public static boolean shouldTriggerMigration(
-            @NonNull String schemaType,
-            @NonNull Migrator migrator,
-            @NonNull Map<String, Integer> currentVersionMap,
-            @NonNull Map<String, Integer> finalVersionMap)
+    public static void checkDeletedAndIncompatibleAfterMigration(
+            @NonNull SetSchemaResponse setSchemaResponse, @NonNull Set<String> activeMigrators)
             throws AppSearchException {
-        Integer currentVersion = currentVersionMap.get(schemaType);
-        Integer finalVersion = finalVersionMap.get(schemaType);
-        if (currentVersion == null) {
-            Log.d(TAG, "The SchemaType: " + schemaType + " not present in AppSearch.");
-            return false;
-        }
-        if (finalVersion == null) {
-            throw new AppSearchException(
-                    AppSearchResult.RESULT_INVALID_ARGUMENT,
-                    "Receive a migrator for schema type : "
-                            + schemaType
-                            + ", but the schema doesn't exist in the request.");
-        }
-        return migrator.shouldMigrate(currentVersion, finalVersion);
+        Set<String> unmigratedIncompatibleTypes =
+                new ArraySet<>(setSchemaResponse.getIncompatibleTypes());
+        unmigratedIncompatibleTypes.removeAll(activeMigrators);
+
+        Set<String> unmigratedDeletedTypes = new ArraySet<>(setSchemaResponse.getDeletedTypes());
+        unmigratedDeletedTypes.removeAll(activeMigrators);
+
+        // check if there are any unmigrated incompatible types or deleted types. If there
+        // are, we will getActiveMigratorsthrow an exception. That's the only case we
+        // swallowed in the AppSearchImpl#setSchema().
+        // Since the force override is false, the schema will not have been set if there are
+        // any incompatible or deleted types.
+        checkDeletedAndIncompatible(unmigratedDeletedTypes, unmigratedIncompatibleTypes);
     }
 
-    /** Builds a Map of SchemaType and its version of given set of {@link AppSearchSchema}. */
-    //TODO(b/182620003) remove this method once support migrate to another type
-    @NonNull
-    public static Map<String, Integer> buildVersionMap(
-            @NonNull Collection<AppSearchSchema> schemas, int version) {
-        Map<String, Integer> currentVersionMap = new ArrayMap<>(schemas.size());
-        for (AppSearchSchema currentSchema : schemas) {
-            currentVersionMap.put(currentSchema.getSchemaType(), version);
+    /** Checks the setSchema() call won't delete any types or has incompatible types. */
+    public static void checkDeletedAndIncompatible(
+            @NonNull Set<String> deletedTypes, @NonNull Set<String> incompatibleTypes)
+            throws AppSearchException {
+        if (deletedTypes.size() > 0 || incompatibleTypes.size() > 0) {
+            String newMessage =
+                    "Schema is incompatible."
+                            + "\n  Deleted types: "
+                            + deletedTypes
+                            + "\n  Incompatible types: "
+                            + incompatibleTypes;
+            throw new AppSearchException(AppSearchResult.RESULT_INVALID_SCHEMA, newMessage);
         }
-        return currentVersionMap;
     }
 }
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 877dacb..b46e85d 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -16,6 +16,7 @@
 package com.android.server.appsearch;
 
 import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
+import static android.os.UserHandle.USER_NULL;
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
@@ -34,7 +35,10 @@
 import android.app.appsearch.SearchSpec;
 import android.app.appsearch.SetSchemaResponse;
 import android.app.appsearch.StorageInfo;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
 import android.os.Bundle;
@@ -46,6 +50,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -63,14 +68,27 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 /** TODO(b/142567528): add comments when implement this class */
 public class AppSearchManagerService extends SystemService {
     private static final String TAG = "AppSearchManagerService";
+    private final Context mContext;
     private PackageManagerInternal mPackageManagerInternal;
     private ImplInstanceManager mImplInstanceManager;
     private UserManager mUserManager;
 
+    // Never call shutdownNow(). It will cancel the futures it's returned. And since
+    // Executor#execute won't return anything, we will hang forever waiting for the execution.
+    // AppSearch multi-thread execution is guarded by Read & Write Lock in AppSearchImpl, all
+    // mutate requests will need to gain write lock and query requests need to gain read lock.
+    private static final Executor EXECUTOR = new ThreadPoolExecutor(/*corePoolSize=*/1,
+            Runtime.getRuntime().availableProcessors(), /*keepAliveTime*/ 60L, TimeUnit.SECONDS,
+            new SynchronousQueue<Runnable>());
+
     // Cache of unlocked user ids so we don't have to query UserManager service each time. The
     // "locked" suffix refers to the fact that access to the field should be locked; unrelated to
     // the unlocked status of user ids.
@@ -79,14 +97,60 @@
 
     public AppSearchManagerService(Context context) {
         super(context);
+        mContext = context;
     }
 
     @Override
     public void onStart() {
         publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
-        mImplInstanceManager = ImplInstanceManager.getInstance(getContext());
-        mUserManager = getContext().getSystemService(UserManager.class);
+        mImplInstanceManager = ImplInstanceManager.getInstance(mContext);
+        mUserManager = mContext.getSystemService(UserManager.class);
+        registerReceivers();
+    }
+
+    private void registerReceivers() {
+        mContext.registerReceiverAsUser(new UserActionReceiver(), UserHandle.ALL,
+                new IntentFilter(Intent.ACTION_USER_REMOVED), /*broadcastPermission=*/ null,
+                /*scheduler=*/ null);
+    }
+
+    private class UserActionReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(@NonNull Context context, @NonNull Intent intent) {
+            switch (intent.getAction()) {
+                case Intent.ACTION_USER_REMOVED:
+                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+                    if (userId == USER_NULL) {
+                        Slog.e(TAG, "userId is missing in the intent: " + intent);
+                        return;
+                    }
+                    handleUserRemoved(userId);
+                    break;
+                default:
+                    Slog.e(TAG, "Received unknown intent: " + intent);
+            }
+        }
+    }
+
+    /**
+     * Handles user removed action.
+     *
+     * <p>Only need to clear the AppSearchImpl instance. The data of AppSearch is saved in the
+     * "credential encrypted" system directory of each user. That directory will be auto-deleted
+     * when a user is removed.
+     *
+     * @param userId The multi-user userId of the user that need to be removed.
+     *
+     * @see android.os.Environment#getDataSystemCeDirectory
+     */
+    private void handleUserRemoved(@UserIdInt int userId) {
+        try {
+            mImplInstanceManager.removeAppSearchImplForUser(userId);
+            Slog.i(TAG, "Removed AppSearchImpl instance for user: " + userId);
+        } catch (Throwable t) {
+            Slog.e(TAG, "Unable to remove data for user: " + userId, t);
+        }
     }
 
     @Override
@@ -105,8 +169,8 @@
                 @NonNull List<String> schemasNotDisplayedBySystem,
                 @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
                 boolean forceOverride,
-                @UserIdInt int userId,
                 int schemaVersion,
+                @UserIdInt int userId,
                 @NonNull IAppSearchResultCallback callback) {
             Preconditions.checkNotNull(packageName);
             Preconditions.checkNotNull(databaseName);
@@ -114,41 +178,41 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                verifyCallingPackage(callingUid, 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 =
-                        new ArrayMap<>(schemasPackageAccessibleBundles.size());
-                for (Map.Entry<String, List<Bundle>> entry :
-                        schemasPackageAccessibleBundles.entrySet()) {
-                    List<PackageIdentifier> packageIdentifiers =
-                            new ArrayList<>(entry.getValue().size());
-                    for (int i = 0; i < entry.getValue().size(); i++) {
-                        packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    verifyCallingPackage(callingUid, packageName);
+                    List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
+                    for (int i = 0; i < schemaBundles.size(); i++) {
+                        schemas.add(new AppSearchSchema(schemaBundles.get(i)));
                     }
-                    schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
+                    Map<String, List<PackageIdentifier>> schemasPackageAccessible =
+                            new ArrayMap<>(schemasPackageAccessibleBundles.size());
+                    for (Map.Entry<String, List<Bundle>> entry :
+                            schemasPackageAccessibleBundles.entrySet()) {
+                        List<PackageIdentifier> packageIdentifiers =
+                                new ArrayList<>(entry.getValue().size());
+                        for (int i = 0; i < entry.getValue().size(); i++) {
+                            packageIdentifiers.add(
+                                    new PackageIdentifier(entry.getValue().get(i)));
+                        }
+                        schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
+                    }
+                    AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    SetSchemaResponse setSchemaResponse = impl.setSchema(
+                            packageName,
+                            databaseName,
+                            schemas,
+                            schemasNotDisplayedBySystem,
+                            schemasPackageAccessible,
+                            forceOverride,
+                            schemaVersion);
+                    invokeCallbackOnResult(callback,
+                            AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle()));
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
                 }
-                AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId);
-                SetSchemaResponse setSchemaResponse = impl.setSchema(
-                        packageName,
-                        databaseName,
-                        schemas,
-                        schemasNotDisplayedBySystem,
-                        schemasPackageAccessible,
-                        forceOverride,
-                        schemaVersion);
-                invokeCallbackOnResult(callback,
-                        AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle()));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            });
         }
 
         @Override
@@ -162,20 +226,20 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                GetSchemaResponse response = impl.getSchema(packageName, databaseName);
-                invokeCallbackOnResult(
-                        callback, AppSearchResult.newSuccessfulResult(response.getBundle()));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    verifyCallingPackage(callingUid, packageName);
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    GetSchemaResponse response = impl.getSchema(packageName, databaseName);
+                    invokeCallbackOnResult(
+                            callback,
+                            AppSearchResult.newSuccessfulResult(response.getBundle()));
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
+                }
+            });
         }
 
         @Override
@@ -189,19 +253,19 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                List<String> namespaces = impl.getNamespaces(packageName, databaseName);
-                invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(namespaces));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    verifyCallingPackage(callingUid, packageName);
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    List<String> namespaces = impl.getNamespaces(packageName, databaseName);
+                    invokeCallbackOnResult(callback,
+                            AppSearchResult.newSuccessfulResult(namespaces));
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
+                }
+            });
         }
 
         @Override
@@ -217,31 +281,30 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                verifyCallingPackage(callingUid, packageName);
-                AppSearchBatchResult.Builder<String, Void> resultBuilder =
-                        new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                for (int i = 0; i < documentBundles.size(); i++) {
-                    GenericDocument document = new GenericDocument(documentBundles.get(i));
-                    try {
-                        // TODO(b/173451571): reduce burden of binder thread by enqueue request onto
-                        // a separate thread.
-                        impl.putDocument(packageName, databaseName, document, /*logger=*/ null);
-                        resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
-                    } catch (Throwable t) {
-                        resultBuilder.setResult(document.getUri(), throwableToFailedResult(t));
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    verifyCallingPackage(callingUid, packageName);
+                    AppSearchBatchResult.Builder<String, Void> resultBuilder =
+                            new AppSearchBatchResult.Builder<>();
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    for (int i = 0; i < documentBundles.size(); i++) {
+                        GenericDocument document = new GenericDocument(documentBundles.get(i));
+                        try {
+                            impl.putDocument(packageName, databaseName, document,
+                                    /*logger=*/ null);
+                            resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
+                        } catch (Throwable t) {
+                            resultBuilder.setResult(document.getUri(),
+                                    throwableToFailedResult(t));
+                        }
                     }
+                    invokeCallbackOnResult(callback, resultBuilder.build());
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
                 }
-                invokeCallbackOnResult(callback, resultBuilder.build());
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            });
         }
 
         @Override
@@ -260,38 +323,36 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                verifyCallingPackage(callingUid, packageName);
-                AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
-                        new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                for (int i = 0; i < uris.size(); i++) {
-                    String uri = uris.get(i);
-                    try {
-                        GenericDocument document =
-                                impl.getDocument(
-                                        packageName,
-                                        databaseName,
-                                        namespace,
-                                        uri,
-                                        typePropertyPaths);
-                        resultBuilder.setSuccess(uri, document.getBundle());
-                    } catch (Throwable t) {
-                        resultBuilder.setResult(uri, throwableToFailedResult(t));
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    verifyCallingPackage(callingUid, packageName);
+                    AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
+                            new AppSearchBatchResult.Builder<>();
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    for (int i = 0; i < uris.size(); i++) {
+                        String uri = uris.get(i);
+                        try {
+                            GenericDocument document =
+                                    impl.getDocument(
+                                            packageName,
+                                            databaseName,
+                                            namespace,
+                                            uri,
+                                            typePropertyPaths);
+                            resultBuilder.setSuccess(uri, document.getBundle());
+                        } catch (Throwable t) {
+                            resultBuilder.setResult(uri, throwableToFailedResult(t));
+                        }
                     }
+                    invokeCallbackOnResult(callback, resultBuilder.build());
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
                 }
-                invokeCallbackOnResult(callback, resultBuilder.build());
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            });
         }
 
-        // TODO(sidchhabra): Do this in a threadpool.
         @Override
         public void query(
                 @NonNull String packageName,
@@ -307,26 +368,25 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                SearchResultPage searchResultPage =
-                        impl.query(
-                                packageName,
-                                databaseName,
-                                queryExpression,
-                                new SearchSpec(searchSpecBundle));
-                invokeCallbackOnResult(
-                        callback,
-                        AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    verifyCallingPackage(callingUid, packageName);
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    SearchResultPage searchResultPage =
+                            impl.query(
+                                    packageName,
+                                    databaseName,
+                                    queryExpression,
+                                    new SearchSpec(searchSpecBundle));
+                    invokeCallbackOnResult(
+                            callback,
+                            AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
+                }
+            });
         }
 
         @Override
@@ -342,26 +402,25 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                SearchResultPage searchResultPage =
-                        impl.globalQuery(
-                                queryExpression,
-                                new SearchSpec(searchSpecBundle),
-                                packageName,
-                                callingUid);
-                invokeCallbackOnResult(
-                        callback,
-                        AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    verifyCallingPackage(callingUid, packageName);
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    SearchResultPage searchResultPage =
+                            impl.globalQuery(
+                                    queryExpression,
+                                    new SearchSpec(searchSpecBundle),
+                                    packageName,
+                                    callingUid);
+                    invokeCallbackOnResult(
+                            callback,
+                            AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
+                }  catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
+                }
+            });
         }
 
         @Override
@@ -372,39 +431,37 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
             // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
             // opened it
-            try {
-                verifyUserUnlocked(callingUserId);
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
-                invokeCallbackOnResult(
-                        callback,
-                        AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
+                    invokeCallbackOnResult(
+                            callback,
+                            AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
+                }
+            });
         }
 
         @Override
         public void invalidateNextPageToken(long nextPageToken, @UserIdInt int userId) {
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                impl.invalidateNextPageToken(nextPageToken);
-            } catch (Throwable t) {
-                Log.e(TAG, "Unable to invalidate the query page token", t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    impl.invalidateNextPageToken(nextPageToken);
+                } catch (Throwable t) {
+                    Log.e(TAG, "Unable to invalidate the query page token", t);
+                }
+            });
         }
 
         @Override
@@ -418,34 +475,34 @@
                 @NonNull IAppSearchResultCallback callback) {
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                // we don't need to append the file. The file is always brand new.
-                try (DataOutputStream outputStream = new DataOutputStream(
-                        new FileOutputStream(fileDescriptor.getFileDescriptor()))) {
-                    SearchResultPage searchResultPage = impl.query(
-                            packageName,
-                            databaseName,
-                            queryExpression,
-                            new SearchSpec(searchSpecBundle));
-                    while (!searchResultPage.getResults().isEmpty()) {
-                        for (int i = 0; i < searchResultPage.getResults().size(); i++) {
-                            AppSearchMigrationHelper.writeBundleToOutputStream(
-                                    outputStream, searchResultPage.getResults().get(i)
-                                            .getGenericDocument().getBundle());
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyCallingPackage(callingUid, packageName);
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    // we don't need to append the file. The file is always brand new.
+                    try (DataOutputStream outputStream = new DataOutputStream(
+                            new FileOutputStream(fileDescriptor.getFileDescriptor()))) {
+                        SearchResultPage searchResultPage = impl.query(
+                                packageName,
+                                databaseName,
+                                queryExpression,
+                                new SearchSpec(searchSpecBundle));
+                        while (!searchResultPage.getResults().isEmpty()) {
+                            for (int i = 0; i < searchResultPage.getResults().size(); i++) {
+                                AppSearchMigrationHelper.writeBundleToOutputStream(
+                                        outputStream, searchResultPage.getResults().get(i)
+                                                .getGenericDocument().getBundle());
+                            }
+                            searchResultPage = impl.getNextPage(
+                                    searchResultPage.getNextPageToken());
                         }
-                        searchResultPage = impl.getNextPage(searchResultPage.getNextPageToken());
                     }
+                    invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
                 }
-                invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            });
         }
 
         @Override
@@ -457,46 +514,46 @@
                 @NonNull IAppSearchResultCallback callback) {
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyCallingPackage(callingUid, packageName);
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
 
-                GenericDocument document;
-                ArrayList<Bundle> migrationFailureBundles = new ArrayList<>();
-                try (DataInputStream inputStream = new DataInputStream(
-                        new FileInputStream(fileDescriptor.getFileDescriptor()))) {
-                    while (true) {
-                        try {
-                            document = AppSearchMigrationHelper
-                                    .readDocumentFromInputStream(inputStream);
-                        } catch (EOFException e) {
-                            // nothing wrong, we just finish the reading.
-                            break;
-                        }
-                        try {
-                            impl.putDocument(packageName, databaseName, document, /*logger=*/ null);
-                        } catch (Throwable t) {
-                            migrationFailureBundles.add(
-                                    new SetSchemaResponse.MigrationFailure.Builder()
-                                            .setNamespace(document.getNamespace())
-                                            .setSchemaType(document.getSchemaType())
-                                            .setUri(document.getUri())
-                                            .setAppSearchResult(
-                                                    AppSearchResult.throwableToFailedResult(t))
-                                            .build().getBundle());
+                    GenericDocument document;
+                    ArrayList<Bundle> migrationFailureBundles = new ArrayList<>();
+                    try (DataInputStream inputStream = new DataInputStream(
+                            new FileInputStream(fileDescriptor.getFileDescriptor()))) {
+                        while (true) {
+                            try {
+                                document = AppSearchMigrationHelper
+                                        .readDocumentFromInputStream(inputStream);
+                            } catch (EOFException e) {
+                                // nothing wrong, we just finish the reading.
+                                break;
+                            }
+                            try {
+                                impl.putDocument(packageName, databaseName, document,
+                                        /*logger=*/ null);
+                            } catch (Throwable t) {
+                                migrationFailureBundles.add(
+                                        new SetSchemaResponse.MigrationFailure.Builder()
+                                                .setNamespace(document.getNamespace())
+                                                .setSchemaType(document.getSchemaType())
+                                                .setUri(document.getUri())
+                                                .setAppSearchResult(AppSearchResult
+                                                        .throwableToFailedResult(t))
+                                                .build().getBundle());
+                            }
                         }
                     }
+                    impl.persistToDisk();
+                    invokeCallbackOnResult(callback,
+                            AppSearchResult.newSuccessfulResult(migrationFailureBundles));
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
                 }
-                impl.persistToDisk();
-                invokeCallbackOnResult(callback,
-                        AppSearchResult.newSuccessfulResult(migrationFailureBundles));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            });
         }
 
         @Override
@@ -515,26 +572,25 @@
             Objects.requireNonNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
 
-                if (systemUsage) {
-                    // TODO(b/183031844): Validate that the call comes from the system
+                    if (systemUsage) {
+                        // TODO(b/183031844): Validate that the call comes from the system
+                    }
+
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    impl.reportUsage(
+                            packageName, databaseName, namespace, uri,
+                            usageTimeMillis, systemUsage);
+                    invokeCallbackOnResult(
+                            callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
                 }
-
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                impl.reportUsage(
-                        packageName, databaseName, namespace, uri,
-                        usageTimeMillis, systemUsage);
-                invokeCallbackOnResult(
-                        callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            });
         }
 
         @Override
@@ -551,29 +607,28 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                verifyCallingPackage(callingUid, packageName);
-                AppSearchBatchResult.Builder<String, Void> resultBuilder =
-                        new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                for (int i = 0; i < uris.size(); i++) {
-                    String uri = uris.get(i);
-                    try {
-                        impl.remove(packageName, databaseName, namespace, uri);
-                        resultBuilder.setSuccess(uri, /*result= */ null);
-                    } catch (Throwable t) {
-                        resultBuilder.setResult(uri, throwableToFailedResult(t));
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    verifyCallingPackage(callingUid, packageName);
+                    AppSearchBatchResult.Builder<String, Void> resultBuilder =
+                            new AppSearchBatchResult.Builder<>();
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    for (int i = 0; i < uris.size(); i++) {
+                        String uri = uris.get(i);
+                        try {
+                            impl.remove(packageName, databaseName, namespace, uri);
+                            resultBuilder.setSuccess(uri, /*result= */ null);
+                        } catch (Throwable t) {
+                            resultBuilder.setResult(uri, throwableToFailedResult(t));
+                        }
                     }
+                    invokeCallbackOnResult(callback, resultBuilder.build());
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
                 }
-                invokeCallbackOnResult(callback, resultBuilder.build());
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            });
         }
 
         @Override
@@ -591,23 +646,22 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                impl.removeByQuery(
-                        packageName,
-                        databaseName,
-                        queryExpression,
-                        new SearchSpec(searchSpecBundle));
-                invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    verifyCallingPackage(callingUid, packageName);
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    impl.removeByQuery(
+                            packageName,
+                            databaseName,
+                            queryExpression,
+                            new SearchSpec(searchSpecBundle));
+                    invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
+                }
+            });
         }
 
         @Override
@@ -621,38 +675,37 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                StorageInfo storageInfo = impl.getStorageInfoForDatabase(packageName, databaseName);
-                Bundle storageInfoBundle = storageInfo.getBundle();
-                invokeCallbackOnResult(
-                        callback, AppSearchResult.newSuccessfulResult(storageInfoBundle));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    verifyCallingPackage(callingUid, packageName);
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    StorageInfo storageInfo = impl.getStorageInfoForDatabase(packageName,
+                            databaseName);
+                    Bundle storageInfoBundle = storageInfo.getBundle();
+                    invokeCallbackOnResult(
+                            callback, AppSearchResult.newSuccessfulResult(storageInfoBundle));
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
+                }
+            });
         }
 
         @Override
         public void persistToDisk(@UserIdInt int userId) {
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(callingUserId);
-                impl.persistToDisk();
-            } catch (Throwable t) {
-                Log.e(TAG, "Unable to persist the data to disk", t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    AppSearchImpl impl =
+                            mImplInstanceManager.getAppSearchImpl(callingUserId);
+                    impl.persistToDisk();
+                } catch (Throwable t) {
+                    Log.e(TAG, "Unable to persist the data to disk", t);
+                }
+            });
         }
 
         @Override
@@ -660,16 +713,15 @@
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
             int callingUserId = handleIncomingUser(userId, callingUid);
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                verifyUserUnlocked(callingUserId);
-                mImplInstanceManager.getOrCreateAppSearchImpl(getContext(), callingUserId);
-                invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
-            } catch (Throwable t) {
-                invokeCallbackOnError(callback, t);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            EXECUTOR.execute(() -> {
+                try {
+                    verifyUserUnlocked(callingUserId);
+                    mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUserId);
+                    invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
+                } catch (Throwable t) {
+                    invokeCallbackOnError(callback, t);
+                }
+            });
         }
 
         private void verifyUserUnlocked(int callingUserId) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index cacf880..e82dd9a 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -95,6 +95,23 @@
         }
     }
 
+    /**
+     * Remove an instance of {@link AppSearchImpl} for the given user.
+     *
+     * <p>This method should only be called if {@link AppSearchManagerService} receives an
+     * ACTION_USER_REMOVED, which the instance of given user should be removed.
+     *
+     * <p>If the user is removed, the "credential encrypted" system directory where icing lives will
+     * be auto-deleted. So we shouldn't worry about persist data or close the AppSearchImpl.
+     *
+     * @param userId The multi-user userId of the user that need to be removed.
+     */
+    @NonNull
+    public void removeAppSearchImplForUser(@UserIdInt int userId) {
+        synchronized (mInstancesLocked) {
+            mInstancesLocked.remove(userId);
+        }
+    }
 
     /**
      * Gets an instance of AppSearchImpl for the given user.
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
index ca5b884..38cd7a8 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
+++ b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
@@ -4,6 +4,9 @@
       "name": "CtsAppSearchTestCases"
     },
     {
+      "name": "CtsAppSearchHostTestCases"
+    },
+    {
       "name": "FrameworksServicesTests",
       "options": [
         {
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 5b43ac3..5f8cbee 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
@@ -1127,6 +1127,40 @@
     }
 
     /**
+     * Remove all {@link AppSearchSchema}s and {@link GenericDocument}s under the given package.
+     *
+     * @param packageName The name of package to be removed.
+     * @throws AppSearchException if we cannot remove the data.
+     */
+    public void clearPackageData(@NonNull String packageName) throws AppSearchException {
+        mReadWriteLock.writeLock().lock();
+        try {
+            throwIfClosedLocked();
+
+            SchemaProto existingSchema = getSchemaProtoLocked();
+            SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
+
+            String prefix = createPackagePrefix(packageName);
+            for (int i = 0; i < existingSchema.getTypesCount(); i++) {
+                if (!existingSchema.getTypes(i).getSchemaType().startsWith(prefix)) {
+                    newSchemaBuilder.addTypes(existingSchema.getTypes(i));
+                }
+            }
+
+            // Apply schema, set force override to true to remove all schemas and documents under
+            // that package.
+            SetSchemaResultProto setSchemaResultProto =
+                    mIcingSearchEngineLocked.setSchema(
+                            newSchemaBuilder.build(), /*ignoreErrorsAndDeleteDocuments=*/ true);
+
+            // Determine whether it succeeded.
+            checkSuccess(setSchemaResultProto.getStatus());
+        } finally {
+            mReadWriteLock.writeLock().unlock();
+        }
+    }
+
+    /**
      * Clears documents and schema across all packages and databaseNames.
      *
      * <p>This method also clear all data in {@link VisibilityStore}, an {@link
@@ -1624,7 +1658,12 @@
 
     @NonNull
     static String createPrefix(@NonNull String packageName, @NonNull String databaseName) {
-        return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER;
+        return createPackagePrefix(packageName) + databaseName + DATABASE_DELIMITER;
+    }
+
+    @NonNull
+    private static String createPackagePrefix(@NonNull String packageName) {
+        return packageName + PACKAGE_DELIMITER;
     }
 
     /**
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 29bd541..e46c147 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ibbd3a92ad091f6911de652e2ba7e44f555a70a72
+I925ec12f4901c7759976c344ba3428210aada8ad
diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp
index 3c5082e..2518394 100644
--- a/apex/appsearch/testing/Android.bp
+++ b/apex/appsearch/testing/Android.bp
@@ -31,6 +31,7 @@
         "truth-prebuilt",
     ],
     visibility: [
+        "//cts/hostsidetests/appsearch",
         "//cts/tests/appsearch",
         "//vendor:__subpackages__",
     ],
diff --git a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
index 2f21ce3..a10cbbe 100644
--- a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
+++ b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
@@ -42,4 +42,5 @@
     AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
     long currentNetworkTimeMillis();
     boolean canScheduleExactAlarms();
+    int getConfigVersion();
 }
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 60f6475..e1bfde1 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -64,6 +64,16 @@
      */
     public static final int REASON_RESTRICTED_BUCKET =
             JobProtoEnums.STOP_REASON_RESTRICTED_BUCKET; // 6.
+    /**
+     * The app was uninstalled.
+     * @hide
+     */
+    public static  final int DEBUG_REASON_UNINSTALL = 7;
+    /**
+     * The app's data was cleared.
+     * @hide
+     */
+    public static  final int DEBUG_REASON_DATA_CLEARED = 8;
 
     /**
      * All the stop reason codes. This should be regarded as an immutable array at runtime.
@@ -187,8 +197,8 @@
      */
     public static final int STOP_REASON_APP_STANDBY = 12;
     /**
-     * The user stopped the job. This can happen either through force-stop, or via adb shell
-     * commands.
+     * The user stopped the job. This can happen either through force-stop, adb shell commands,
+     * or uninstalling.
      */
     public static final int STOP_REASON_USER = 13;
     /** The system is doing some processing that requires stopping this job. */
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
index 6ae91a0..6d2bd47 100644
--- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
@@ -37,7 +37,8 @@
     /**
      * Cancel the jobs for a given uid (e.g. when app data is cleared)
      */
-    void cancelJobsForUid(int uid, @JobParameters.StopReason int reason, String debugReason);
+    void cancelJobsForUid(int uid, @JobParameters.StopReason int reason, int debugReasonCode,
+            String debugReason);
 
     /**
      * These are for activity manager to communicate to use what is currently performing backups.
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 901daa7..08e1a95 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -538,6 +538,7 @@
         public long PRIORITY_ALARM_DELAY = DEFAULT_PRIORITY_ALARM_DELAY;
 
         private long mLastAllowWhileIdleWhitelistDuration = -1;
+        private int mVersion = 0;
 
         Constants() {
             updateAllowWhileIdleWhitelistDurationLocked();
@@ -546,6 +547,12 @@
             }
         }
 
+        public int getVersion() {
+            synchronized (mLock) {
+                return mVersion;
+            }
+        }
+
         public void start() {
             mInjector.registerDeviceConfigListener(this);
             onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER));
@@ -568,6 +575,7 @@
         public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
             boolean standbyQuotaUpdated = false;
             synchronized (mLock) {
+                mVersion++;
                 for (String name : properties.getKeyset()) {
                     if (name == null) {
                         continue;
@@ -749,6 +757,9 @@
 
             pw.increaseIndent();
 
+            pw.print("version", mVersion);
+            pw.println();
+
             pw.print(KEY_MIN_FUTURITY);
             pw.print("=");
             TimeUtils.formatDuration(MIN_FUTURITY, pw);
@@ -2219,6 +2230,13 @@
         }
 
         @Override
+        public int getConfigVersion() {
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.DUMP,
+                    "getConfigVersion");
+            return mConstants.getVersion();
+        }
+
+        @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
 
@@ -4698,6 +4716,10 @@
                         final String tz = getNextArgRequired();
                         getBinderService().setTimeZone(tz);
                         return 0;
+                    case "get-config-version":
+                        final int version = getBinderService().getConfigVersion();
+                        pw.println(version);
+                        return 0;
                     default:
                         return handleDefaultCommands(cmd);
                 }
@@ -4718,6 +4740,10 @@
             pw.println("    since the Epoch.");
             pw.println("  set-timezone TZ");
             pw.println("    Set the system timezone to TZ where TZ is an Olson id.");
+            pw.println("  get-config-version");
+            pw.println("    Returns an integer denoting the version of device_config keys the"
+                    + " service is sync'ed to. As long as this returns the same version, the values"
+                    + " of the config are guaranteed to remain the same.");
         }
     }
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 41b342c..8b9eca6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -747,12 +747,14 @@
                                                     + " in user " + userId);
                                         }
                                         synchronized (mLock) {
-                                            // By the time we get here, the process should have
-                                            // already been stopped, so the app wouldn't get the
-                                            // stop reason, so just put USER instead of UNINSTALL
-                                            // or DISABLED.
+                                            // There's no guarantee that the process has been
+                                            // stopped by the time we get here, but since this is
+                                            // a user-initiated action, it should be fine to just
+                                            // put USER instead of UNINSTALL or DISABLED.
                                             cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
-                                                    JobParameters.STOP_REASON_USER, "app disabled");
+                                                    JobParameters.STOP_REASON_USER,
+                                                    JobParameters.DEBUG_REASON_UNINSTALL,
+                                                    "app disabled");
                                         }
                                     }
                                 } catch (RemoteException|IllegalArgumentException e) {
@@ -783,25 +785,22 @@
                 } else {
                     Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
                 }
-            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                // If this is an outright uninstall rather than the first half of an
-                // app update sequence, cancel the jobs associated with the app.
-                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
-                    int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
-                    if (DEBUG) {
-                        Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
+            } else if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
+                int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                if (DEBUG) {
+                    Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
+                }
+                synchronized (mLock) {
+                    // There's no guarantee that the process has been stopped by the time we
+                    // get here, but since this is generally a user-initiated action, it should
+                    // be fine to just put USER instead of UNINSTALL or DISABLED.
+                    cancelJobsForPackageAndUidLocked(pkgName, uidRemoved,
+                            JobParameters.STOP_REASON_USER,
+                            JobParameters.DEBUG_REASON_UNINSTALL, "app uninstalled");
+                    for (int c = 0; c < mControllers.size(); ++c) {
+                        mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
                     }
-                    // By the time we get here, the process should have already
-                    // been stopped, so the app wouldn't get the stop reason,
-                    // so just put USER instead of UNINSTALL or DISABLED.
-                    synchronized (mLock) {
-                        cancelJobsForPackageAndUidLocked(pkgName, uidRemoved,
-                                JobParameters.STOP_REASON_USER, "app uninstalled");
-                        for (int c = 0; c < mControllers.size(); ++c) {
-                            mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
-                        }
-                        mDebuggableApps.remove(pkgName);
-                    }
+                    mDebuggableApps.remove(pkgName);
                 }
             } else if (Intent.ACTION_USER_ADDED.equals(action)) {
                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -849,7 +848,8 @@
                     }
                     synchronized (mLock) {
                         cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
-                                JobParameters.STOP_REASON_USER, "app force stopped");
+                                JobParameters.STOP_REASON_USER, JobParameters.REASON_CANCELED,
+                                "app force stopped");
                     }
                 }
             }
@@ -1038,7 +1038,7 @@
             if (toCancel != null) {
                 // Implicitly replaces the existing job record with the new instance
                 cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP,
-                        "job rescheduled by app");
+                        JobParameters.REASON_CANCELED, "job rescheduled by app");
             } else {
                 startTrackingJobLocked(jobStatus, null);
             }
@@ -1115,10 +1115,11 @@
         final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
         for (int i = 0; i < jobsForUser.size(); i++) {
             JobStatus toRemove = jobsForUser.get(i);
-            // By the time we get here, the process should have already been stopped, so the
-            // app wouldn't get the stop reason, so just put USER instead of UNINSTALL.
+            // There's no guarantee that the process has been stopped by the time we get here,
+            // but since this is a user-initiated action, it should be fine to just put USER
+            // instead of UNINSTALL or DISABLED.
             cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
-                    "user removed");
+                    JobParameters.DEBUG_REASON_UNINSTALL, "user removed");
         }
     }
 
@@ -1130,7 +1131,7 @@
     }
 
     private void cancelJobsForPackageAndUidLocked(String pkgName, int uid,
-            @JobParameters.StopReason int reason, String debugReason) {
+            @JobParameters.StopReason int reason, int debugReasonCode, String debugReason) {
         if ("android".equals(pkgName)) {
             Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
             return;
@@ -1139,7 +1140,7 @@
         for (int i = jobsForUid.size() - 1; i >= 0; i--) {
             final JobStatus job = jobsForUid.get(i);
             if (job.getSourcePackageName().equals(pkgName)) {
-                cancelJobImplLocked(job, null, reason, debugReason);
+                cancelJobImplLocked(job, null, reason, debugReasonCode, debugReason);
             }
         }
     }
@@ -1152,7 +1153,7 @@
      * @param uid Uid to check against for removal of a job.
      */
     public boolean cancelJobsForUid(int uid, @JobParameters.StopReason int reason,
-            String debugReason) {
+            int debugReasonCode, String debugReason) {
         if (uid == Process.SYSTEM_UID) {
             Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
             return false;
@@ -1161,9 +1162,9 @@
         boolean jobsCanceled = false;
         synchronized (mLock) {
             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
-            for (int i=0; i<jobsForUid.size(); i++) {
+            for (int i = 0; i < jobsForUid.size(); i++) {
                 JobStatus toRemove = jobsForUid.get(i);
-                cancelJobImplLocked(toRemove, null, reason, debugReason);
+                cancelJobImplLocked(toRemove, null, reason, debugReasonCode, debugReason);
                 jobsCanceled = true;
             }
         }
@@ -1185,8 +1186,9 @@
             toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
             if (toCancel != null) {
                 cancelJobImplLocked(toCancel, null, reason,
+                        JobParameters.REASON_CANCELED,
                         "cancel() called by app, callingUid=" + callingUid
-                        + " uid=" + uid + " jobId=" + jobId);
+                                + " uid=" + uid + " jobId=" + jobId);
             }
             return (toCancel != null);
         }
@@ -1199,7 +1201,7 @@
      * currently scheduled jobs.
      */
     private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob,
-            @JobParameters.StopReason int reason, String debugReason) {
+            @JobParameters.StopReason int reason, int debugReasonCode, String debugReason) {
         if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
         cancelled.unprepareLocked();
         stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
@@ -1208,8 +1210,7 @@
             mJobPackageTracker.noteNonpending(cancelled);
         }
         // Cancel if running.
-        stopJobOnServiceContextLocked(cancelled, reason, JobParameters.REASON_CANCELED,
-                debugReason);
+        stopJobOnServiceContextLocked(cancelled, reason, debugReasonCode, debugReason);
         // If this is a replacement, bring in the new version of the job
         if (incomingJob != null) {
             if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
@@ -1459,7 +1460,7 @@
                     Slog.v(TAG, "  replacing " + oldJob + " with " + newJob);
                 }
                 cancelJobImplLocked(oldJob, newJob, JobParameters.STOP_REASON_SYSTEM_PROCESSING,
-                        "deferred rtc calculation");
+                        JobParameters.REASON_CANCELED, "deferred rtc calculation");
             }
         }
     };
@@ -1482,7 +1483,7 @@
 
             // Register br for package removals and user removals.
             final IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
             filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
@@ -1786,15 +1787,26 @@
      * @param needsReschedule Whether the implementing class should reschedule this job.
      */
     @Override
-    public void onJobCompletedLocked(JobStatus jobStatus, int stopReason, boolean needsReschedule) {
+    public void onJobCompletedLocked(JobStatus jobStatus, int debugStopReason,
+            boolean needsReschedule) {
         if (DEBUG) {
-            Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
+            Slog.d(TAG, "Completed " + jobStatus + ", reason=" + debugStopReason
+                    + ", reschedule=" + needsReschedule);
         }
 
         mLastCompletedJobs[mLastCompletedJobIndex] = jobStatus;
         mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis();
         mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY;
 
+        if (debugStopReason == JobParameters.DEBUG_REASON_UNINSTALL
+                || debugStopReason == JobParameters.DEBUG_REASON_DATA_CLEARED) {
+            // The job should have already been cleared from the rest of the JS tracking. No need
+            // to go through all that flow again.
+            jobStatus.unprepareLocked();
+            reportActiveLocked();
+            return;
+        }
+
         // Intentionally not checking expedited job quota here. An app can't find out if it's run
         // out of quota when it asks JS to reschedule an expedited job. Instead, the rescheduled
         // EJ will just be demoted to a regular job if the app has no EJ quota left.
@@ -1805,8 +1817,8 @@
         final JobStatus rescheduledJob = needsReschedule
                 ? getRescheduleJobForFailureLocked(jobStatus) : null;
         if (rescheduledJob != null
-                && (stopReason == JobParameters.REASON_TIMEOUT
-                || stopReason == JobParameters.REASON_PREEMPT)) {
+                && (debugStopReason == JobParameters.REASON_TIMEOUT
+                || debugStopReason == JobParameters.REASON_PREEMPT)) {
             rescheduledJob.disallowRunInBatterySaverAndDoze();
         }
 
@@ -1911,6 +1923,7 @@
                         break;
                     case MSG_STOP_JOB:
                         cancelJobImplLocked((JobStatus) message.obj, null, message.arg1,
+                                JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
                                 "app no longer allowed to run");
                         break;
 
@@ -1927,7 +1940,7 @@
                         if (disabled) {
                             cancelJobsForUid(uid,
                                     JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
-                                    "uid gone");
+                                    JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, "uid gone");
                         }
                         synchronized (mLock) {
                             mDeviceIdleJobsController.setUidActiveLocked(uid, false);
@@ -1947,7 +1960,7 @@
                         if (disabled) {
                             cancelJobsForUid(uid,
                                     JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
-                                    "app uid idle");
+                                    JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, "app uid idle");
                         }
                         synchronized (mLock) {
                             mDeviceIdleJobsController.setUidActiveLocked(uid, false);
@@ -2409,8 +2422,8 @@
 
         @Override
         public void cancelJobsForUid(int uid, @JobParameters.StopReason int reason,
-                String debugReason) {
-            JobSchedulerService.this.cancelJobsForUid(uid, reason, debugReason);
+                int debugReasonCode, String debugReason) {
+            JobSchedulerService.this.cancelJobsForUid(uid, reason, debugReasonCode, debugReason);
         }
 
         @Override
@@ -2749,6 +2762,7 @@
             try {
                 JobSchedulerService.this.cancelJobsForUid(uid,
                         JobParameters.STOP_REASON_CANCELLED_BY_APP,
+                        JobParameters.REASON_CANCELED,
                         "cancelAll() called by app, callingUid=" + uid);
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -2969,7 +2983,7 @@
         if (!hasJobId) {
             pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
             if (!cancelJobsForUid(pkgUid, JobParameters.STOP_REASON_USER,
-                    "cancel shell command for package")) {
+                    JobParameters.REASON_CANCELED, "cancel shell command for package")) {
                 pw.println("No matching jobs found.");
             }
         } else {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index f30b75c..b4e167a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -244,7 +244,10 @@
                     getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
             uidStats.numReadyWithConnectivity--;
             if (jobStatus.madeActive != 0) {
-                uidStats.numRunning--;
+                // numRunning would be 0 if the UidStats object didn't exist before this method
+                // was called. getUidStats() handles logging, so just make sure we don't save a
+                // negative value.
+                uidStats.numRunning = Math.max(0, uidStats.numRunning - 1);
             }
             maybeRevokeStandbyExceptionLocked(jobStatus);
             postAdjustCallbacks();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 91189e4..158a0b9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -735,6 +735,7 @@
         mTempAllowlistCache.delete(uid);
         mTempAllowlistGraceCache.delete(uid);
         mTopAppCache.delete(uid);
+        mTopAppTrackers.delete(uid);
         mTopAppGraceCache.delete(uid);
     }
 
diff --git a/apex/media/framework/TEST_MAPPING b/apex/media/framework/TEST_MAPPING
index 70c9087..3d21914 100644
--- a/apex/media/framework/TEST_MAPPING
+++ b/apex/media/framework/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "CtsMediaParserTestCases"
     },
     {
-      "name": "CtsMediaParserHostSideTestCases"
+      "name": "CtsMediaParserHostTestCases"
     }
   ]
 }
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index 7df0b4b..ce68447 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -25,7 +25,7 @@
   }
 
   public static final class MediaTranscodeManager.TranscodingSession {
-    method public void addClientUid(int);
+    method public boolean addClientUid(int);
     method public void cancel();
     method @NonNull public java.util.List<java.lang.Integer> getClientUids();
     method public int getErrorCode();
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index a7de602..d7e9609 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -1361,8 +1361,6 @@
         private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE;
         @GuardedBy("mLock")
         private boolean mHasRetried = false;
-        @GuardedBy("mLock")
-        private @NonNull List<Integer> mClientUidList = new ArrayList<>();
         // The original request that associated with this session.
         private final TranscodingRequest mRequest;
 
@@ -1381,7 +1379,6 @@
             mListenerExecutor = executor;
             mListener = listener;
             mRequest = request;
-            mClientUidList.add(request.getClientUid());
         }
 
         /**
@@ -1532,17 +1529,31 @@
          * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could add the
          * uid. Note that the permission check happens on the service side upon starting the
          * transcoding. If the client does not have the permission, the transcoding will fail.
+         * @param uid  the additional client uid to be added.
+         * @return true if successfully added, false otherwise.
          */
-        public void addClientUid(int uid) {
+        public boolean addClientUid(int uid) {
             if (uid < 0) {
                 throw new IllegalArgumentException("Invalid Uid");
             }
-            synchronized (mLock) {
-                if (!mClientUidList.contains(uid)) {
-                    // see ag/14023202 for implementation
-                    mClientUidList.add(uid);
-                }
+
+            // Get the client interface.
+            ITranscodingClient client = mManager.getTranscodingClient();
+            if (client == null) {
+                Log.e(TAG, "Service is dead...");
+                return false;
             }
+
+            try {
+                if (!client.addClientUid(mSessionId, uid)) {
+                    Log.e(TAG, "Failed to add client uid");
+                    return false;
+                }
+            } catch (Exception ex) {
+                Log.e(TAG, "Failed to get client uids due to " + ex);
+                return false;
+            }
+            return true;
         }
 
         /**
@@ -1551,9 +1562,25 @@
          */
         @NonNull
         public List<Integer> getClientUids() {
-            synchronized (mLock) {
-                return mClientUidList;
+            List<Integer> uidList = new ArrayList<Integer>();
+
+            // Get the client interface.
+            ITranscodingClient client = mManager.getTranscodingClient();
+            if (client == null) {
+                Log.e(TAG, "Service is dead...");
+                return uidList;
             }
+
+            try {
+                int[] clientUids  = client.getClientUids(mSessionId);
+                for (int i : clientUids) {
+                    uidList.add(i);
+                }
+            } catch (Exception ex) {
+                Log.e(TAG, "Failed to get client uids due to " + ex);
+            }
+
+            return uidList;
         }
 
         /**
diff --git a/api/dump_api_shas.sh b/api/dump_api_shas.sh
new file mode 100755
index 0000000..c023b31
--- /dev/null
+++ b/api/dump_api_shas.sh
@@ -0,0 +1,56 @@
+#!/bin/bash -e
+# This script dumps the git SHAs of all commits inside api tracking directories.
+# It can used by tools wanting to track API changes, and the primary original
+# purpose is to verify verify all API change SHAs have been tracked by the
+# server-side API-council tools.
+#
+# The only argument is used to specify a git commit range to filter by.
+#
+# Example invocation (API changes between O and P):
+# frameworks/base/api/dump_api_shas.sh origin/oreo-dev..origin/pie-dev
+
+set -o pipefail
+
+eecho() { echo $@ >&2 ; }
+
+if [[ $1 == *..* ]]; then
+    exclude=${1/..*}
+    include=${1/*..}
+else
+    eecho No range or invalid range specified, defaulting to all commits from HEAD.
+    exclude=
+    include=HEAD
+fi
+
+eecho -n building queryview...
+{ source build/envsetup.sh && lunch aosp_arm && m queryview; } >/dev/null 2>&1 \
+  || { eecho failed; exit 1; }
+eecho "done"
+
+# This finds the directories where the dependant java_sdk_libs are defined
+bpdirs=$(
+  bazel query --config=queryview --output=package \
+    'kind(java_sdk_library, deps(//frameworks/base/api/..., 1))' 2>/dev/null
+  echo frameworks/base/core/api # Not a java_sdk_library.
+  echo frameworks/base/services/api # Not a java_sdk_library.
+)
+
+# Find relevant api subdirectories
+apidirs=$(
+  find $bpdirs -type f -name '*current.txt' -path '*/api/*' \
+    | xargs realpath --relative-to=$(pwd) | xargs dirname | sort | uniq
+)
+
+# Dump sorted SHAs of commits in these directories
+{ for d in $apidirs; do
+    ( cd $d
+      eecho inspecting $d
+      exclude_arg=$(test -n "$exclude" && {
+        git rev-parse -q --verify $exclude > /dev/null && echo "--not $exclude" \
+          || eecho "$d has no revision $exclude, including all commits"; } || true)
+      for f in $(find . -name '*current.txt'); do
+        git --no-pager log --pretty=format:%H --no-merges --follow $include $exclude_arg -- $f
+        echo # No trailing newline with --no-pager
+      done
+    )
+done; } | sort | uniq
diff --git a/config/hiddenapi-force-blocked.txt b/boot/hiddenapi/hiddenapi-force-blocked.txt
similarity index 100%
rename from config/hiddenapi-force-blocked.txt
rename to boot/hiddenapi/hiddenapi-force-blocked.txt
diff --git a/config/hiddenapi-max-target-o.txt b/boot/hiddenapi/hiddenapi-max-target-o.txt
similarity index 100%
rename from config/hiddenapi-max-target-o.txt
rename to boot/hiddenapi/hiddenapi-max-target-o.txt
diff --git a/config/hiddenapi-max-target-p.txt b/boot/hiddenapi/hiddenapi-max-target-p.txt
similarity index 100%
rename from config/hiddenapi-max-target-p.txt
rename to boot/hiddenapi/hiddenapi-max-target-p.txt
diff --git a/config/hiddenapi-max-target-q.txt b/boot/hiddenapi/hiddenapi-max-target-q.txt
similarity index 100%
rename from config/hiddenapi-max-target-q.txt
rename to boot/hiddenapi/hiddenapi-max-target-q.txt
diff --git a/config/hiddenapi-max-target-r-loprio.txt b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
similarity index 100%
rename from config/hiddenapi-max-target-r-loprio.txt
rename to boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
diff --git a/config/hiddenapi-unsupported-packages.txt b/boot/hiddenapi/hiddenapi-unsupported-packages.txt
similarity index 100%
rename from config/hiddenapi-unsupported-packages.txt
rename to boot/hiddenapi/hiddenapi-unsupported-packages.txt
diff --git a/config/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt
similarity index 100%
rename from config/hiddenapi-unsupported.txt
rename to boot/hiddenapi/hiddenapi-unsupported.txt
diff --git a/core/api/current.txt b/core/api/current.txt
index bdd0f17..8a642e6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1182,7 +1182,7 @@
     field public static final int reqNavigation = 16843306; // 0x101022a
     field public static final int reqTouchScreen = 16843303; // 0x1010227
     field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
-    field public static final int requestOptimizedExternalStorageAccess;
+    field public static final int requestRawExternalStorageAccess;
     field public static final int requireDeviceScreenOn;
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
@@ -8552,8 +8552,8 @@
     ctor public AppWidgetProviderInfo(android.os.Parcel);
     method public android.appwidget.AppWidgetProviderInfo clone();
     method public int describeContents();
+    method @NonNull public android.content.pm.ActivityInfo getActivityInfo();
     method public final android.os.UserHandle getProfile();
-    method @NonNull public android.content.pm.ActivityInfo getProviderInfo();
     method @Nullable public final CharSequence loadDescription(@NonNull android.content.Context);
     method public final android.graphics.drawable.Drawable loadIcon(@NonNull android.content.Context, int);
     method public final String loadLabel(android.content.pm.PackageManager);
@@ -18826,10 +18826,10 @@
   public final class DeviceProductInfo implements android.os.Parcelable {
     method public int describeContents();
     method public int getConnectionToSinkType();
-    method public int getManufactureWeek();
-    method public int getManufactureYear();
+    method @IntRange(from=0xffffffff, to=53) public int getManufactureWeek();
+    method @IntRange(from=0xffffffff) public int getManufactureYear();
     method @NonNull public String getManufacturerPnpId();
-    method public int getModelYear();
+    method @IntRange(from=0xffffffff) public int getModelYear();
     method @Nullable public String getName();
     method @NonNull public String getProductId();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -20332,6 +20332,7 @@
     field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
     field public static final int ENCODING_DTS = 7; // 0x7
     field public static final int ENCODING_DTS_HD = 8; // 0x8
+    field public static final int ENCODING_DTS_UHD = 27; // 0x1b
     field public static final int ENCODING_E_AC3 = 6; // 0x6
     field public static final int ENCODING_E_AC3_JOC = 18; // 0x12
     field public static final int ENCODING_IEC61937 = 13; // 0xd
@@ -35478,8 +35479,8 @@
 
   public static final class SimPhonebookContract.SimRecords {
     method @NonNull public static android.net.Uri getContentUri(int, int);
-    method @WorkerThread public static int getEncodedNameLength(@NonNull android.content.ContentResolver, @NonNull String);
-    method @NonNull public static android.net.Uri getItemUri(int, int, int);
+    method @IntRange(from=0) @WorkerThread public static int getEncodedNameLength(@NonNull android.content.ContentResolver, @NonNull String);
+    method @NonNull public static android.net.Uri getItemUri(int, int, @IntRange(from=1) int);
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
     field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
     field public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
@@ -41921,42 +41922,42 @@
   @Deprecated public class PhoneStateListener {
     ctor @Deprecated public PhoneStateListener();
     ctor @Deprecated public PhoneStateListener(@NonNull java.util.concurrent.Executor);
-    method @Deprecated public void onActiveDataSubscriptionIdChanged(int);
-    method @Deprecated public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int);
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
-    method @Deprecated public void onCallForwardingIndicatorChanged(boolean);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean);
     method @Deprecated @RequiresPermission(value=android.Manifest.permission.READ_PHONE_STATE, conditional=true) public void onCallStateChanged(int, String);
-    method @Deprecated public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
-    method @Deprecated public void onCellLocationChanged(android.telephony.CellLocation);
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(android.telephony.CellLocation);
     method @Deprecated public void onDataActivity(int);
     method @Deprecated public void onDataConnectionStateChanged(int);
     method @Deprecated public void onDataConnectionStateChanged(int, int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
-    method @Deprecated public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
-    method @Deprecated public void onMessageWaitingIndicatorChanged(boolean);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
-    method @Deprecated public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
     method @Deprecated public void onServiceStateChanged(android.telephony.ServiceState);
     method @Deprecated public void onSignalStrengthChanged(int);
     method @Deprecated public void onSignalStrengthsChanged(android.telephony.SignalStrength);
     method @Deprecated public void onUserMobileDataStateChanged(boolean);
-    field @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
+    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
+    field @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
-    field @Deprecated public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
+    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
     field @Deprecated public static final int LISTEN_CALL_STATE = 32; // 0x20
-    field @Deprecated public static final int LISTEN_CELL_INFO = 1024; // 0x400
-    field @Deprecated public static final int LISTEN_CELL_LOCATION = 16; // 0x10
+    field @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int LISTEN_CELL_INFO = 1024; // 0x400
+    field @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int LISTEN_CELL_LOCATION = 16; // 0x10
     field @Deprecated public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80
     field @Deprecated public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40
     field @Deprecated public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000
-    field @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
+    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
-    field @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
+    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
     field @Deprecated public static final int LISTEN_NONE = 0; // 0x0
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
+    field @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
     field @Deprecated public static final int LISTEN_SERVICE_STATE = 1; // 0x1
     field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
     field @Deprecated public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
@@ -42455,10 +42456,6 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int);
   }
 
-  public static interface TelephonyCallback.AlwaysReportedSignalStrengthListener {
-    method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
-  }
-
   public static interface TelephonyCallback.BarringInfoListener {
     method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
   }
@@ -42480,7 +42477,7 @@
   }
 
   public static interface TelephonyCallback.CellInfoListener {
-    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>);
+    method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>);
   }
 
   public static interface TelephonyCallback.CellLocationListener {
@@ -42488,15 +42485,15 @@
   }
 
   public static interface TelephonyCallback.DataActivationStateListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int);
+    method public void onDataActivationStateChanged(int);
   }
 
   public static interface TelephonyCallback.DataActivityListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int);
+    method public void onDataActivity(int);
   }
 
   public static interface TelephonyCallback.DataConnectionStateListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int);
+    method public void onDataConnectionStateChanged(int, int);
   }
 
   public static interface TelephonyCallback.DisplayInfoListener {
@@ -42528,15 +42525,15 @@
   }
 
   public static interface TelephonyCallback.ServiceStateListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState);
+    method public void onServiceStateChanged(@NonNull android.telephony.ServiceState);
   }
 
   public static interface TelephonyCallback.SignalStrengthsListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
+    method public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
   }
 
   public static interface TelephonyCallback.UserMobileDataStateListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean);
+    method public void onUserMobileDataStateChanged(boolean);
   }
 
   public final class TelephonyDisplayInfo implements android.os.Parcelable {
@@ -50825,6 +50822,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
     field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+    field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
     field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
@@ -52860,7 +52858,7 @@
     method @NonNull public android.view.translation.TranslationResponse.Builder setFinalResponse(boolean);
     method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int, @NonNull android.view.translation.TranslationResponseValue);
     method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationResponseValues(@NonNull android.util.SparseArray<android.view.translation.TranslationResponseValue>);
-    method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationStatus(int);
+    method @Deprecated @NonNull public android.view.translation.TranslationResponse.Builder setTranslationStatus(int);
     method @NonNull public android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int, @NonNull android.view.translation.ViewTranslationResponse);
     method @NonNull public android.view.translation.TranslationResponse.Builder setViewTranslationResponses(@NonNull android.util.SparseArray<android.view.translation.ViewTranslationResponse>);
   }
@@ -55495,6 +55493,7 @@
 
   public class RemoteViews implements android.view.LayoutInflater.Filter android.os.Parcelable {
     ctor public RemoteViews(String, int);
+    ctor public RemoteViews(@NonNull String, @LayoutRes int, @IdRes int);
     ctor public RemoteViews(android.widget.RemoteViews, android.widget.RemoteViews);
     ctor public RemoteViews(@NonNull java.util.Map<android.util.SizeF,android.widget.RemoteViews>);
     ctor public RemoteViews(android.widget.RemoteViews);
@@ -55568,7 +55567,6 @@
     method public void setTextViewText(@IdRes int, CharSequence);
     method public void setTextViewTextSize(@IdRes int, int, float);
     method public void setUri(@IdRes int, String, android.net.Uri);
-    method public void setViewId(@IdRes int);
     method public void setViewLayoutHeight(@IdRes int, float, int);
     method public void setViewLayoutHeightDimen(@IdRes int, @DimenRes int);
     method public void setViewLayoutMargin(@IdRes int, int, float, int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 869d790..5379468 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -65,10 +65,6 @@
     field public static final String TEST_NETWORK_SERVICE = "test_network";
   }
 
-  public class Intent implements java.lang.Cloneable android.os.Parcelable {
-    field public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
-  }
-
 }
 
 package android.content.pm {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 930c298..0772478 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -28,6 +28,7 @@
     field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
     field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
     field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
+    field public static final String ASSOCIATE_COMPANION_DEVICES = "android.permission.ASSOCIATE_COMPANION_DEVICES";
     field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
     field public static final String BACKUP = "android.permission.BACKUP";
     field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
@@ -68,6 +69,7 @@
     field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
     field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
     field @Deprecated public static final String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED";
+    field public static final String BYPASS_ROLE_QUALIFICATION = "android.permission.BYPASS_ROLE_QUALIFICATION";
     field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
     field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
     field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
@@ -1923,6 +1925,7 @@
     method public boolean disableBLE();
     method public boolean enableBLE();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void generateLocalOobData(int, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OobDataCallback);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public long getDiscoveryEndMillis();
     method public boolean isBleScanAlwaysAvailable();
     method public boolean isLeEnabled();
@@ -1934,12 +1937,18 @@
     field public static final int ACTIVE_DEVICE_ALL = 2; // 0x2
     field public static final int ACTIVE_DEVICE_AUDIO = 0; // 0x0
     field public static final int ACTIVE_DEVICE_PHONE_CALL = 1; // 0x1
+    field public static final int OOB_ERROR_ADAPTER_DISABLED = 2; // 0x2
+    field public static final int OOB_ERROR_ANOTHER_ACTIVE_REQUEST = 1; // 0x1
+    field public static final int OOB_ERROR_UNKNOWN = 0; // 0x0
   }
 
   public static interface BluetoothAdapter.OnMetadataChangedListener {
     method public void onMetadataChanged(@NonNull android.bluetooth.BluetoothDevice, int, @Nullable byte[]);
   }
 
+  public static interface BluetoothAdapter.OobDataCallback {
+  }
+
   public final class BluetoothDevice implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean canBondWithoutDialog();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess();
@@ -2220,7 +2229,7 @@
 package android.companion {
 
   public final class CompanionDeviceManager {
-    method @RequiresPermission("android.permission.ASSOCIATE_COMPANION_DEVICES") public boolean associate(@NonNull String, @NonNull android.net.MacAddress);
+    method @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES) public boolean associate(@NonNull String, @NonNull android.net.MacAddress);
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, int);
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
   }
@@ -2500,7 +2509,7 @@
 package android.content.pm {
 
   public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
-    method @Nullable public Boolean hasRequestOptimizedExternalStorageAccess();
+    method @Nullable public Boolean hasRequestRawExternalStorageAccess();
     method public boolean isEncryptionAware();
     method public boolean isInstantApp();
     method public boolean isOem();
@@ -3005,12 +3014,19 @@
   }
 
   public static final class FontFamilyUpdateRequest.Font {
-    ctor public FontFamilyUpdateRequest.Font(@NonNull String, @NonNull android.graphics.fonts.FontStyle, @NonNull java.util.List<android.graphics.fonts.FontVariationAxis>);
     method @NonNull public java.util.List<android.graphics.fonts.FontVariationAxis> getAxes();
+    method @IntRange(from=0) public int getIndex();
     method @NonNull public String getPostScriptName();
     method @NonNull public android.graphics.fonts.FontStyle getStyle();
   }
 
+  public static final class FontFamilyUpdateRequest.Font.Builder {
+    ctor public FontFamilyUpdateRequest.Font.Builder(@NonNull String, @NonNull android.graphics.fonts.FontStyle);
+    method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.Font build();
+    method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.Font.Builder setAxes(@NonNull java.util.List<android.graphics.fonts.FontVariationAxis>);
+    method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.Font.Builder setIndex(@IntRange(from=0) int);
+  }
+
   public static final class FontFamilyUpdateRequest.FontFamily {
     method @NonNull public java.util.List<android.graphics.fonts.FontFamilyUpdateRequest.Font> getFonts();
     method @NonNull public String getName();
@@ -7921,12 +7937,18 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
     method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnStateCallback(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback);
     field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
   }
 
+  public static interface NfcAdapter.ControllerAlwaysOnStateCallback {
+    method public void onStateChanged(boolean);
+  }
+
   public static interface NfcAdapter.NfcUnlockHandler {
     method public boolean onUnlockAttempted(android.nfc.Tag);
   }
@@ -9418,7 +9440,7 @@
   }
 
   public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
-    method @Nullable public int[] getAttestationIds();
+    method @NonNull public int[] getAttestationIds();
     method public int getNamespace();
   }
 
@@ -11364,15 +11386,15 @@
   }
 
   @Deprecated public class PhoneStateListener {
-    method @Deprecated public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
-    method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
-    method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
-    method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
     method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
-    method @Deprecated public void onRadioPowerStateChanged(int);
-    method @Deprecated public void onSrvccStateChanged(int);
-    method @Deprecated public void onVoiceActivationStateChanged(int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int);
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
@@ -11721,14 +11743,14 @@
   public class TelephonyCallback {
     field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35; // 0x23
-    field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa
+    field public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa
     field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20
     field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b
     field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a
     field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
     field public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
     field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
-    field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
+    field @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
     field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
     field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13
     field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8
@@ -11783,7 +11805,7 @@
   }
 
   public static interface TelephonyCallback.PhoneCapabilityListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
+    method public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
   }
 
   public static interface TelephonyCallback.PreciseCallStateListener {
@@ -13594,10 +13616,11 @@
     method public int describeContents();
     method @Nullable public String getCallIdParameter();
     method @NonNull public byte[] getContent();
-    method @NonNull public byte[] getEncodedMessage();
+    method @Deprecated @NonNull public byte[] getEncodedMessage();
     method @NonNull public String getHeaderSection();
     method @NonNull public String getStartLine();
     method @NonNull public String getViaBranchParameter();
+    method @NonNull public byte[] toEncodedMessage();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR;
   }
@@ -14149,7 +14172,7 @@
   public final class DistanceMeasurement implements android.os.Parcelable {
     method public int describeContents();
     method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
-    method public double getErrorMeters();
+    method @FloatRange(from=0.0) public double getErrorMeters();
     method public double getMeters();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.uwb.DistanceMeasurement> CREATOR;
@@ -14158,8 +14181,8 @@
   public static final class DistanceMeasurement.Builder {
     ctor public DistanceMeasurement.Builder();
     method @NonNull public android.uwb.DistanceMeasurement build();
-    method @NonNull public android.uwb.DistanceMeasurement.Builder setConfidenceLevel(double);
-    method @NonNull public android.uwb.DistanceMeasurement.Builder setErrorMeters(double);
+    method @NonNull public android.uwb.DistanceMeasurement.Builder setConfidenceLevel(@FloatRange(from=0.0, to=1.0) double);
+    method @NonNull public android.uwb.DistanceMeasurement.Builder setErrorMeters(@FloatRange(from=0.0) double);
     method @NonNull public android.uwb.DistanceMeasurement.Builder setMeters(double);
   }
 
@@ -14218,7 +14241,7 @@
     method public void onStartFailed(int, @NonNull android.os.PersistableBundle);
     method public void onStarted(@NonNull android.os.PersistableBundle);
     method public void onStopFailed(int, @NonNull android.os.PersistableBundle);
-    method public void onStopped();
+    method public void onStopped(int, @NonNull android.os.PersistableBundle);
     field public static final int REASON_BAD_PARAMETERS = 3; // 0x3
     field public static final int REASON_GENERIC_ERROR = 4; // 0x4
     field public static final int REASON_LOCAL_REQUEST = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a2634da..906524e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -233,6 +233,8 @@
     field public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";
     field public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME = "fg_service_state_settle_time";
     field public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
+    field public static final String OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition";
+    field public static final String OPSTR_ACTIVITY_RECOGNITION_SOURCE = "android:activity_recognition_source";
     field public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls";
     field public static final String OPSTR_PHONE_CALL_CAMERA = "android:phone_call_camera";
     field public static final String OPSTR_PHONE_CALL_MICROPHONE = "android:phone_call_microphone";
@@ -281,6 +283,11 @@
     method public abstract void onHomeVisibilityChanged(boolean);
   }
 
+  public class KeyguardManager {
+    method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean checkLock(int, @Nullable byte[]);
+    method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]);
+  }
+
   public class Notification implements android.os.Parcelable {
     method public boolean shouldShowForegroundImmediately();
   }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 36c66e5..db42803 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -794,6 +794,9 @@
     /** @hide Flag for registerUidObserver: report uid cached state has changed. */
     public static final int UID_OBSERVER_CACHED = 1<<4;
 
+    /** @hide Flag for registerUidObserver: report uid capability has changed. */
+    public static final int UID_OBSERVER_CAPABILITY = 1<<5;
+
     /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: normal free-to-run operation. */
     public static final int APP_START_MODE_NORMAL = 0;
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e596e7c..e50432e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -236,12 +236,6 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
-final class RemoteServiceException extends AndroidRuntimeException {
-    public RemoteServiceException(String msg) {
-        super(msg);
-    }
-}
-
 /**
  * This manages the execution of the main thread in an
  * application process, scheduling and executing activities,
@@ -1274,8 +1268,9 @@
             sendMessage(H.DISPATCH_PACKAGE_BROADCAST, packages, cmd);
         }
 
-        public void scheduleCrash(String msg) {
-            sendMessage(H.SCHEDULE_CRASH, msg);
+        @Override
+        public void scheduleCrash(String msg, int typeId) {
+            sendMessage(H.SCHEDULE_CRASH, msg, typeId);
         }
 
         public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken,
@@ -1892,6 +1887,17 @@
         }
     }
 
+    private void throwRemoteServiceException(String message, int typeId) {
+        // Use a switch to ensure all the type IDs are unique.
+        switch (typeId) {
+            case ForegroundServiceDidNotStartInTimeException.TYPE_ID: // 1
+                throw new ForegroundServiceDidNotStartInTimeException(message);
+            case RemoteServiceException.TYPE_ID: // 0
+            default:
+                throw new RemoteServiceException(message);
+        }
+    }
+
     class H extends Handler {
         public static final int BIND_APPLICATION        = 110;
         @UnsupportedAppUsage
@@ -2105,7 +2111,8 @@
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case SCHEDULE_CRASH:
-                    throw new RemoteServiceException((String)msg.obj);
+                    throwRemoteServiceException((String) msg.obj, msg.arg1);
+                    break;
                 case DUMP_HEAP:
                     handleDumpHeap((DumpHeapData) msg.obj);
                     break;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 436007ca..8e2626a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1225,9 +1225,19 @@
     /** @hide */
     public static final int OP_UWB_RANGING = AppProtoEnums.APP_OP_UWB_RANGING;
 
+    /**
+     * Activity recognition being accessed by an activity recognition source, which
+     * is a component that already has access since it is the one that detects
+     * activity recognition.
+     *
+     * @hide
+     */
+    public static final int OP_ACTIVITY_RECOGNITION_SOURCE =
+            AppProtoEnums.APP_OP_ACTIVITY_RECOGNITION_SOURCE;
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 113;
+    public static final int _NUM_OP = 114;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1478,6 +1488,7 @@
     public static final String OPSTR_USE_BIOMETRIC = "android:use_biometric";
 
     /** @hide Recognize physical activity. */
+    @TestApi
     public static final String OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition";
 
     /** @hide Financial app read sms. */
@@ -1643,6 +1654,17 @@
     /** @hide */
     public static final String OPSTR_UWB_RANGING = "android:uwb_ranging";
 
+    /**
+     * Activity recognition being accessed by an activity recognition source, which
+     * is a component that already has access since it is the one that detects
+     * activity recognition.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String OPSTR_ACTIVITY_RECOGNITION_SOURCE =
+            "android:activity_recognition_source";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1853,6 +1875,7 @@
             OP_MANAGE_MEDIA,                    // MANAGE_MEDIA
             OP_BLUETOOTH_CONNECT,               // OP_BLUETOOTH_CONNECT
             OP_UWB_RANGING,                     // OP_UWB_RANGING
+            OP_ACTIVITY_RECOGNITION_SOURCE      // OP_ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
@@ -1972,6 +1995,7 @@
             OPSTR_MANAGE_MEDIA,
             OPSTR_BLUETOOTH_CONNECT,
             OPSTR_UWB_RANGING,
+            OPSTR_ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
@@ -2091,7 +2115,8 @@
             "COARSE_LOCATION_SOURCE",
             "MANAGE_MEDIA",
             "BLUETOOTH_CONNECT",
-            "UWB_RANGING"
+            "UWB_RANGING",
+            "ACTIVITY_RECOGNITION_SOURCE"
     };
 
     /**
@@ -2213,6 +2238,7 @@
             Manifest.permission.MANAGE_MEDIA,
             Manifest.permission.BLUETOOTH_CONNECT,
             Manifest.permission.UWB_RANGING,
+            null, // no permission for OP_ACTIVITY_RECOGNITION_SOURCE,
     };
 
     /**
@@ -2334,6 +2360,7 @@
             null, // MANAGE_MEDIA
             null, // BLUETOOTH_CONNECT
             null, // UWB_RANGING
+            null, // ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
@@ -2454,6 +2481,7 @@
             null, // MANAGE_MEDIA
             null, // BLUETOOTH_CONNECT
             null, // UWB_RANGING
+            null  // ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
@@ -2573,6 +2601,7 @@
             AppOpsManager.MODE_DEFAULT, // MANAGE_MEDIA
             AppOpsManager.MODE_ALLOWED, // BLUETOOTH_CONNECT
             AppOpsManager.MODE_ALLOWED, // UWB_RANGING
+            AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
@@ -2696,6 +2725,7 @@
             false, // MANAGE_MEDIA
             false, // BLUETOOTH_CONNECT
             false, // UWB_RANGING
+            false, // ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
diff --git a/core/java/android/app/ForegroundServiceDidNotStartInTimeException.java b/core/java/android/app/ForegroundServiceDidNotStartInTimeException.java
new file mode 100644
index 0000000..364291e
--- /dev/null
+++ b/core/java/android/app/ForegroundServiceDidNotStartInTimeException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+/**
+ * Exception used to crash an app process when it didn't call {@link Service#startForeground}
+ * in time after the service was started with
+ * {@link android.content.Context#startForegroundService}.
+ *
+ * @hide
+ */
+public class ForegroundServiceDidNotStartInTimeException extends RemoteServiceException {
+    /** The type ID passed to {@link IApplicationThread#scheduleCrash}. */
+    public static final int TYPE_ID = 1;
+
+    public ForegroundServiceDidNotStartInTimeException(String msg) {
+        super(msg);
+    }
+}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 1b8eb8a..f9279da 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -321,6 +321,8 @@
     boolean isTopActivityImmersive();
     void crashApplication(int uid, int initialPid, in String packageName, int userId,
             in String message, boolean force);
+    void crashApplicationWithType(int uid, int initialPid, in String packageName, int userId,
+            in String message, boolean force, int exceptionTypeId);
     /** @deprecated -- use getProviderMimeTypeAsync */
     @UnsupportedAppUsage(maxTargetSdk = 29, publicAlternatives =
             "Use {@link android.content.ContentResolver#getType} public API instead.")
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index b5294d5..78e7ce8 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -106,7 +106,7 @@
     void scheduleOnNewActivityOptions(IBinder token, in Bundle options);
     void scheduleSuicide();
     void dispatchPackageBroadcast(int cmd, in String[] packages);
-    void scheduleCrash(in String msg);
+    void scheduleCrash(in String msg, int typeId);
     void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, in String path,
             in ParcelFileDescriptor fd, in RemoteCallback finishCallback);
     void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix,
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 4326c2d..dc71a32 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -24,6 +24,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManager.PasswordComplexity;
 import android.app.admin.PasswordMetrics;
@@ -51,6 +52,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
 import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.VerifyCredentialResponse;
 
 import java.nio.charset.Charset;
 import java.util.Arrays;
@@ -696,14 +698,15 @@
     }
 
     private boolean checkInitialLockMethodUsage() {
-        if (mContext.checkCallingOrSelfPermission(Manifest.permission.SET_INITIAL_LOCK)
-                != PackageManager.PERMISSION_GRANTED) {
+        if (!hasPermission(Manifest.permission.SET_INITIAL_LOCK)) {
             throw new SecurityException("Requires SET_INITIAL_LOCK permission.");
         }
-        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
-            return false;
-        }
-        return true;
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+    }
+
+    private boolean hasPermission(String permission) {
+        return PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
+                permission);
     }
 
     /**
@@ -792,38 +795,14 @@
             Log.e(TAG, "Password is not valid, rejecting call to setLock");
             return false;
         }
-        boolean success = false;
+        boolean success;
         try {
-            switch (lockType) {
-                case PASSWORD:
-                    CharSequence passwordStr = new String(password, Charset.forName("UTF-8"));
-                    lockPatternUtils.setLockCredential(
-                            LockscreenCredential.createPassword(passwordStr),
-                            /* savedPassword= */ LockscreenCredential.createNone(),
-                            userId);
-                    success = true;
-                    break;
-                case PIN:
-                    CharSequence pinStr = new String(password);
-                    lockPatternUtils.setLockCredential(
-                            LockscreenCredential.createPin(pinStr),
-                            /* savedPassword= */ LockscreenCredential.createNone(),
-                            userId);
-                    success = true;
-                    break;
-                case PATTERN:
-                    List<LockPatternView.Cell> pattern =
-                            LockPatternUtils.byteArrayToPattern(password);
-                    lockPatternUtils.setLockCredential(
-                            LockscreenCredential.createPattern(pattern),
-                            /* savedPassword= */ LockscreenCredential.createNone(),
-                            userId);
-                    pattern.clear();
-                    success = true;
-                    break;
-                default:
-                    Log.e(TAG, "Unknown lock type, returning a failure");
-            }
+            LockscreenCredential credential = createLockscreenCredential(
+                    lockType, password);
+            success = lockPatternUtils.setLockCredential(
+                    credential,
+                    /* savedPassword= */ LockscreenCredential.createNone(),
+                    userId);
         } catch (Exception e) {
             Log.e(TAG, "Save lock exception", e);
             success = false;
@@ -832,4 +811,81 @@
         }
         return success;
     }
+
+    /**
+     * Set the lockscreen password to {@code newPassword} after validating the current password
+     * against {@code currentPassword}.
+     * <p>If no password is currently set, {@code currentPassword} should be set to {@code null}.
+     * <p>To clear the current password, {@code newPassword} should be set to {@code null}.
+     *
+     * @return {@code true} if password successfully set.
+     *
+     * @throws IllegalArgumentException if {@code newLockType} or {@code currentLockType}
+     * is invalid.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS,
+            Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE
+    })
+    public boolean setLock(@LockTypes int newLockType, @Nullable byte[] newPassword,
+            @LockTypes int currentLockType, @Nullable byte[] currentPassword) {
+        final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+        final int userId = mContext.getUserId();
+        LockscreenCredential currentCredential = createLockscreenCredential(
+                currentLockType, currentPassword);
+        LockscreenCredential newCredential = createLockscreenCredential(
+                newLockType, newPassword);
+        return lockPatternUtils.setLockCredential(newCredential, currentCredential, userId);
+    }
+
+    /**
+     * Verifies the current lock credentials against {@code password}.
+     * <p>To check if no password is set, {@code password} should be set to {@code null}.
+     *
+     * @return {@code true} if credentials match
+     *
+     * @throws IllegalArgumentException if {@code lockType} is invalid.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS,
+            Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE
+    })
+    public boolean checkLock(@LockTypes int lockType, @Nullable byte[] password) {
+        final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+        final LockscreenCredential credential = createLockscreenCredential(
+                lockType, password);
+        final VerifyCredentialResponse response = lockPatternUtils.verifyCredential(
+                credential, mContext.getUserId(), /* flags= */ 0);
+        if (response == null) {
+            return false;
+        }
+        return response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK;
+    }
+
+    private LockscreenCredential createLockscreenCredential(
+            @LockTypes int lockType, @Nullable byte[] password) {
+        if (password == null) {
+            return LockscreenCredential.createNone();
+        }
+        switch (lockType) {
+            case PASSWORD:
+                CharSequence passwordStr = new String(password, Charset.forName("UTF-8"));
+                return LockscreenCredential.createPassword(passwordStr);
+            case PIN:
+                CharSequence pinStr = new String(password);
+                return LockscreenCredential.createPin(pinStr);
+            case PATTERN:
+                List<LockPatternView.Cell> pattern =
+                        LockPatternUtils.byteArrayToPattern(password);
+                return LockscreenCredential.createPattern(pattern);
+            default:
+                throw new IllegalArgumentException("Unknown lock type " + lockType);
+        }
+    }
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3de78f6..fa35025 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5360,12 +5360,8 @@
             contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor);
             // Use different highlighted colors for conversations' unread count
             if (p.mHighlightExpander) {
-                pillColor = getAccentTertiaryColor(p);
-                // TODO(b/183710694): The accent tertiary is currently too bright in dark mode, so
-                //  we need to pick a contrasting color.
-                textColor = ColorUtils.setAlphaComponent(
-                        ContrastColorUtil.resolvePrimaryColor(mContext, pillColor, mInNightMode),
-                        0xFF);
+                textColor = getBackgroundColor(p);
+                pillColor = getAccentColor(p);
             }
             contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor);
             contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
@@ -6296,23 +6292,6 @@
         }
 
         /**
-         * Gets the tertiary accent color for colored UI elements. If we're tinting with the theme
-         * accent, this comes from the tertiary system accent palette, otherwise this would be
-         * identical to {@link #getSmallIconColor(StandardTemplateParams)}.
-         */
-        private @ColorInt int getAccentTertiaryColor(StandardTemplateParams p) {
-            if (isColorized(p)) {
-                return getPrimaryTextColor(p);
-            }
-            int color = obtainThemeColor(com.android.internal.R.attr.colorAccentTertiary,
-                    COLOR_INVALID);
-            if (color != COLOR_INVALID) {
-                return color;
-            }
-            return getContrastColor(p);
-        }
-
-        /**
          * Gets the theme's error color, or the primary text color for colorized notifications.
          */
         private @ColorInt int getErrorColor(StandardTemplateParams p) {
diff --git a/core/java/android/app/RemoteServiceException.java b/core/java/android/app/RemoteServiceException.java
new file mode 100644
index 0000000..4b32463
--- /dev/null
+++ b/core/java/android/app/RemoteServiceException.java
@@ -0,0 +1,38 @@
+/*
+ * 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 android.util.AndroidRuntimeException;
+
+/**
+ * Exception used by {@link ActivityThread} to crash an app process.
+ *
+ * @hide
+ */
+public class RemoteServiceException extends AndroidRuntimeException {
+    /**
+     * The type ID passed to {@link IApplicationThread#scheduleCrash}.
+     *
+     * Assign a unique ID to each subclass. See the above method for the numbers that are already
+     * taken.
+     */
+    public static final int TYPE_ID = 0;
+
+    public RemoteServiceException(String msg) {
+        super(msg);
+    }
+}
diff --git a/core/java/android/app/time/LocationTimeZoneManager.java b/core/java/android/app/time/LocationTimeZoneManager.java
index 066aada..f506f12 100644
--- a/core/java/android/app/time/LocationTimeZoneManager.java
+++ b/core/java/android/app/time/LocationTimeZoneManager.java
@@ -50,31 +50,6 @@
     public static final String SHELL_COMMAND_STOP = "stop";
 
     /**
-     * A shell command that can put providers into different modes. Takes effect next time the
-     * service is started.
-     */
-    public static final String SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE =
-            "set_provider_mode_override";
-
-    /**
-     * The default provider mode.
-     * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
-     */
-    public static final String PROVIDER_MODE_OVERRIDE_NONE = "none";
-
-    /**
-     * The "simulated" provider mode.
-     * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
-     */
-    public static final String PROVIDER_MODE_OVERRIDE_SIMULATED = "simulated";
-
-    /**
-     * The "disabled" provider mode (equivalent to there being no provider configured).
-     * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
-     */
-    public static final String PROVIDER_MODE_OVERRIDE_DISABLED = "disabled";
-
-    /**
      * A shell command that tells the service to record state information during tests. The next
      * argument value is "true" or "false".
      */
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 1cbb2fb..3db1885 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -479,7 +479,7 @@
      * Returns the broadcast receiver that is providing this widget.
      */
     @NonNull
-    public ActivityInfo getProviderInfo() {
+    public ActivityInfo getActivityInfo() {
         return providerInfo;
     }
 
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 5446deb..79fd807 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -28,6 +28,7 @@
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.app.PropertyInvalidatedCache;
+import android.bluetooth.BluetoothDevice.Transport;
 import android.bluetooth.BluetoothProfile.ConnectionPolicy;
 import android.bluetooth.le.BluetoothLeAdvertiser;
 import android.bluetooth.le.BluetoothLeScanner;
@@ -3046,6 +3047,168 @@
         return false;
     }
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "OOB_ERROR_" }, value = {
+        OOB_ERROR_UNKNOWN,
+        OOB_ERROR_ANOTHER_ACTIVE_REQUEST,
+        OOB_ERROR_ADAPTER_DISABLED
+    })
+    public @interface OobError {}
+
+    /**
+     * An unknown error has occurred in the controller, stack, or callback pipeline.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int OOB_ERROR_UNKNOWN = 0;
+
+    /**
+     * If another application has already requested {@link OobData} then another fetch will be
+     * disallowed until the callback is removed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int OOB_ERROR_ANOTHER_ACTIVE_REQUEST = 1;
+
+    /**
+     * The adapter is currently disabled, please enable it.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int OOB_ERROR_ADAPTER_DISABLED = 2;
+
+    /**
+     * Provides callback methods for receiving {@link OobData} from the host stack, as well as an
+     * error interface in order to allow the caller to determine next steps based on the {@link
+     * ErrorCode}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface OobDataCallback {
+        /**
+         * Handles the {@link OobData} received from the host stack.
+         *
+         * @param transport - whether the {@link OobData} is generated for LE or Classic.
+         * @param oobData - data generated in the host stack(LE) or controller (Classic)
+         *
+         * @hide
+         */
+        void onOobData(@Transport int transport, @Nullable OobData oobData);
+
+        /**
+         * Provides feedback when things don't go as expected.
+         *
+         * @param errorCode - the code descibing the type of error that occurred.
+         *
+         * @hide
+         */
+        void onError(@OobError int errorCode);
+    }
+
+    /**
+     * Wraps an AIDL interface around an {@link OobDataCallback} interface.
+     *
+     * @see {@link IBluetoothOobDataCallback} for interface definition.
+     *
+     * @hide
+     */
+    public class WrappedOobDataCallback extends IBluetoothOobDataCallback.Stub {
+        private final OobDataCallback mCallback;
+        private final Executor mExecutor;
+
+        /**
+         * @param callback - object to receive {@link OobData} must be a non null argument
+         *
+         * @throws NullPointerException if the callback is null.
+         */
+        WrappedOobDataCallback(@NonNull OobDataCallback callback,
+                @NonNull @CallbackExecutor Executor executor) {
+            Preconditions.checkNotNull(callback);
+            Preconditions.checkNotNull(executor);
+            mCallback = callback;
+            mExecutor = executor;
+        }
+        /**
+         * Wrapper function to relay to the {@link OobDataCallback#onOobData}
+         *
+         * @param transport - whether the {@link OobData} is generated for LE or Classic.
+         * @param oobData - data generated in the host stack(LE) or controller (Classic)
+         *
+         * @hide
+         */
+        public void onOobData(@Transport int transport, OobData oobData) {
+            mExecutor.execute(new Runnable() {
+                public void run() {
+                    mCallback.onOobData(transport, oobData);
+                }
+            });
+        }
+        /**
+         * Wrapper function to relay to the {@link OobDataCallback#onError}
+         *
+         * @param errorCode - the code descibing the type of error that occurred.
+         *
+         * @hide
+         */
+        public void onError(@OobError int errorCode) {
+            mExecutor.execute(new Runnable() {
+                public void run() {
+                    mCallback.onError(errorCode);
+                }
+            });
+        }
+    }
+
+    /**
+     * Fetches a secret data value that can be used for a secure and simple pairing experience.
+     *
+     * <p>This is the Local Out of Band data the comes from the
+     *
+     * <p>This secret is the local Out of Band data.  This data is used to securely and quickly
+     * pair two devices with minimal user interaction.
+     *
+     * <p>For example, this secret can be transferred to a remote device out of band (meaning any
+     * other way besides using bluetooth).  Once the remote device finds this device using the
+     * information given in the data, such as the PUBLIC ADDRESS, the remote device could then
+     * connect to this device using this secret when the pairing sequenece asks for the secret.
+     * This device will respond by automatically accepting the pairing due to the secret being so
+     * trustworthy.
+     *
+     * @param transport - provide type of transport (e.g. LE or Classic).
+     * @param callback - target object to receive the {@link OobData} value.
+     *
+     * @throws NullPointerException if callback is null.
+     * @throws IllegalArgumentException if the transport is not valid.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void generateLocalOobData(@Transport int transport,
+            @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) {
+        if (transport != BluetoothDevice.TRANSPORT_BREDR && transport
+                != BluetoothDevice.TRANSPORT_LE) {
+            throw new IllegalArgumentException("Invalid transport '" + transport + "'!");
+        }
+        Preconditions.checkNotNull(callback);
+        if (!isEnabled()) {
+            Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!");
+            callback.onError(OOB_ERROR_ADAPTER_DISABLED);
+        } else {
+            try {
+                mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback,
+                        executor));
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+    }
+
     /**
      * Enable control of the Bluetooth Adapter for a single application.
      *
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index a96c14f..0c208fd 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -952,6 +952,21 @@
     @SystemApi
     public static final int ACCESS_REJECTED = 2;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = { "TRANSPORT_" },
+        value = {
+            /** Allow host to automatically select a transport (dual-mode only) */
+            TRANSPORT_AUTO,
+            /** Use Classic or BR/EDR transport.*/
+            TRANSPORT_BREDR,
+            /** Use Low Energy transport.*/
+            TRANSPORT_LE,
+        }
+    )
+    public @interface Transport {}
+
     /**
      * No preference of physical transport for GATT connections to remote dual-mode devices
      */
@@ -1084,6 +1099,10 @@
         public void onBrEdrDown() {
             if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state");
         }
+
+        public void onOobData(@Transport int transport, OobData oobData) {
+            if (DBG) Log.d(TAG, "onOobData: got data");
+        }
     };
 
     /**
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 2ce7156..0116db0 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -375,6 +375,14 @@
      * Calling app must check for feature presence of
      * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} before calling this API.
      *
+     * For Bluetooth LE devices this is based on scanning for device with the given address.
+     * For Bluetooth classic devices this is triggered when the device connects/disconnects.
+     * WiFi devices are not supported.
+     *
+     * If a Bluetooth LE device wants to use a rotating mac address, it is recommended to use
+     * Resolvable Private Address, and ensure the device is bonded to the phone so that android OS
+     * is able to resolve the address.
+     *
      * @param deviceAddress a previously-associated companion device's address
      *
      * @throws DeviceNotAssociatedException if the given device was not previously associated
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index c851519..b13bf09 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -21,15 +21,12 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.AppGlobals;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
-import android.os.RemoteException;
-import android.os.UserHandle;
 import android.permission.PermissionManager;
 import android.util.ArraySet;
 
@@ -93,6 +90,8 @@
 // TODO: Codegen applies method level annotations to argument vs the generated member (@SystemApi)
 // TODO: Codegen doesn't properly read/write IBinder members
 // TODO: Codegen doesn't properly handle Set arguments
+// TODO: Codegen requires @SystemApi annotations on fields which breaks
+//      android.signature.cts.api.AnnotationTest (need to update the test)
 // @DataClass(genEqualsHashCode = true, genConstructor = false, genBuilder = true)
 public final class AttributionSource implements Parcelable {
     /**
@@ -153,8 +152,6 @@
      *
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
     @DataClass.ParcelWith(RenouncedPermissionsParcelling.class)
     private @Nullable Set<String> mRenouncedPermissions = null;
 
@@ -516,7 +513,7 @@
         private @Nullable String mPackageName;
         private @Nullable String mAttributionTag;
         private @Nullable IBinder mToken;
-        private @SystemApi @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) @Nullable Set<String> mRenouncedPermissions;
+        private @Nullable Set<String> mRenouncedPermissions;
         private @Nullable AttributionSource mNext;
 
         private long mBuilderFieldsSet = 0L;
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index cadbd60..11adfa3 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -51,6 +51,25 @@
  */
 @SystemService(Context.CLIPBOARD_SERVICE)
 public class ClipboardManager extends android.text.ClipboardManager {
+
+    /**
+     * DeviceConfig property, within the clipboard namespace, that determines whether notifications
+     * are shown when an app accesses clipboard. This may be overridden by a user-controlled
+     * setting.
+     *
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS =
+            "show_access_notifications";
+
+    /**
+     * Default value for the DeviceConfig property that determines whether notifications are shown
+     * when an app accesses clipboard.
+     *
+     * @hide
+     */
+    public static final boolean DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
+
     private final Context mContext;
     private final Handler mHandler;
     private final IClipboard mService;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2c77372..183e73c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2401,14 +2401,6 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED";
     /**
-     * Clear DNS Cache Action: This is broadcast when networks have changed and old
-     * DNS entries should be tossed.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
-    /**
      * Alarm Changed Action: This is broadcast when the AlarmClock
      * application's alarm is set or unset.  It is used by the
      * AlarmClock application and the StatusBar service.
@@ -6569,6 +6561,10 @@
      * any affinities needed to have that task in the proper state (either
      * moving activities to or from it), or simply resetting that task to
      * its initial state if needed.
+     *
+     * @see android.R.attr#allowTaskReparenting
+     * @see android.R.attr#clearTaskOnLaunch
+     * @see android.R.attr#finishOnTaskLaunch
      */
     public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0x00200000;
     /**
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 01b554a..1735aa2 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -5,6 +5,7 @@
 per-file IntentFilter.java = patb@google.com
 per-file Intent.java = toddke@google.com
 per-file Intent.java = patb@google.com
+per-file Intent.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS
 per-file ContentCaptureOptions* = file:/core/java/android/service/contentcapture/OWNERS
 per-file LocusId* = file:/core/java/android/service/contentcapture/OWNERS
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 049bfe7..5089f30 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -1066,11 +1066,25 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             if (selfAccess) {
-                return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(),
-                        resolvedAttributionSource.getPackageName(),
-                        /*startIfModeDefault*/ false,
-                        resolvedAttributionSource.getAttributionTag(),
-                        message);
+                // If the datasource is not in a trusted platform component then in would not
+                // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
+                // an app is exposing runtime permission protected data but cannot blame others
+                // in a trusted way which would not properly show in permission usage UIs.
+                // As a fallback we note a proxy op that blames the app and the datasource.
+                try {
+                    return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(),
+                            resolvedAttributionSource.getPackageName(),
+                            /*startIfModeDefault*/ false,
+                            resolvedAttributionSource.getAttributionTag(),
+                            message);
+                } catch (SecurityException e) {
+                    Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
+                            + " platform defined runtime permission "
+                            + AppOpsManager.opToPermission(op) + " while not having "
+                            + Manifest.permission.UPDATE_APP_OPS_STATS);
+                    return appOpsManager.startProxyOpNoThrow(op, attributionSource, message,
+                            skipProxyOperation);
+                }
             } else {
                 return appOpsManager.startProxyOpNoThrow(op, resolvedAttributionSource, message,
                         skipProxyOperation);
@@ -1082,10 +1096,24 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             if (selfAccess) {
-                return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(),
-                        resolvedAttributionSource.getPackageName(),
-                        resolvedAttributionSource.getAttributionTag(),
-                        message);
+                // If the datasource is not in a trusted platform component then in would not
+                // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
+                // an app is exposing runtime permission protected data but cannot blame others
+                // in a trusted way which would not properly show in permission usage UIs.
+                // As a fallback we note a proxy op that blames the app and the datasource.
+                try {
+                    return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(),
+                            resolvedAttributionSource.getPackageName(),
+                            resolvedAttributionSource.getAttributionTag(),
+                            message);
+                } catch (SecurityException e) {
+                    Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
+                            + " platform defined runtime permission "
+                            + AppOpsManager.opToPermission(op) + " while not having "
+                            + Manifest.permission.UPDATE_APP_OPS_STATS);
+                    return appOpsManager.noteProxyOpNoThrow(op, attributionSource, message,
+                            skipProxyOperation);
+                }
             } else {
                 return appOpsManager.noteProxyOpNoThrow(op, resolvedAttributionSource, message,
                         skipProxyOperation);
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6badf0e0..6ad204e 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1436,11 +1436,11 @@
     private @NativeHeapZeroInitialized int nativeHeapZeroInitialized = ZEROINIT_DEFAULT;
 
     /**
-     * If {@code true} this app requests optimized external storage access.
+     * If {@code true} this app requests raw external storage access.
      * The request may not be honored due to policy or other reasons.
      */
     @Nullable
-    private Boolean requestOptimizedExternalStorageAccess;
+    private Boolean requestRawExternalStorageAccess;
 
     /**
      * Represents the default policy. The actual policy used will depend on other properties of
@@ -1598,9 +1598,9 @@
             if (nativeHeapZeroInitialized != ZEROINIT_DEFAULT) {
                 pw.println(prefix + "nativeHeapZeroInitialized=" + nativeHeapZeroInitialized);
             }
-            if (requestOptimizedExternalStorageAccess != null) {
-                pw.println(prefix + "requestOptimizedExternalStorageAccess="
-                        + requestOptimizedExternalStorageAccess);
+            if (requestRawExternalStorageAccess != null) {
+                pw.println(prefix + "requestRawExternalStorageAccess="
+                        + requestRawExternalStorageAccess);
             }
         }
         super.dumpBack(pw, prefix);
@@ -1829,7 +1829,7 @@
         gwpAsanMode = orig.gwpAsanMode;
         memtagMode = orig.memtagMode;
         nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
-        requestOptimizedExternalStorageAccess = orig.requestOptimizedExternalStorageAccess;
+        requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess;
     }
 
     public String toString() {
@@ -1918,7 +1918,7 @@
         dest.writeInt(gwpAsanMode);
         dest.writeInt(memtagMode);
         dest.writeInt(nativeHeapZeroInitialized);
-        sForBoolean.parcel(requestOptimizedExternalStorageAccess, dest, parcelableFlags);
+        sForBoolean.parcel(requestRawExternalStorageAccess, dest, parcelableFlags);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -2004,7 +2004,7 @@
         gwpAsanMode = source.readInt();
         memtagMode = source.readInt();
         nativeHeapZeroInitialized = source.readInt();
-        requestOptimizedExternalStorageAccess = sForBoolean.unparcel(source);
+        requestRawExternalStorageAccess = sForBoolean.unparcel(source);
     }
 
     /**
@@ -2121,10 +2121,10 @@
     /**
      * @return
      * <ul>
-     * <li>{@code true} if this app requested optimized external storage access
-     * <li>{@code false} if this app requests to disable optimized external storage access.
+     * <li>{@code true} if this app requested raw external storage access
+     * <li>{@code false} if this app requests to disable raw external storage access.
      * <li>{@code null} if the app didn't specify
-     * {@link android.R.styleable#AndroidManifestApplication_requestOptimizedExternalStorageAccess}
+     * {@link android.R.styleable#AndroidManifestApplication_requestRawExternalStorageAccess}
      * in its manifest file.
      * </ul>
      *
@@ -2132,8 +2132,8 @@
      */
     @SystemApi
     @Nullable
-    public Boolean hasRequestOptimizedExternalStorageAccess() {
-        return requestOptimizedExternalStorageAccess;
+    public Boolean hasRequestRawExternalStorageAccess() {
+        return requestRawExternalStorageAccess;
     }
 
     /**
@@ -2421,8 +2421,8 @@
         nativeHeapZeroInitialized = value;
     }
     /** {@hide} */
-    public void setRequestOptimizedExternalStorageAccess(@Nullable Boolean value) {
-        requestOptimizedExternalStorageAccess = value;
+    public void setRequestRawExternalStorageAccess(@Nullable Boolean value) {
+        requestRawExternalStorageAccess = value;
     }
 
     /** {@hide} */
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 1c65e00..8dcba7f 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -259,8 +259,8 @@
     ParsingPackage setNativeHeapZeroInitialized(
             @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized);
 
-    ParsingPackage setRequestOptimizedExternalStorageAccess(
-            @Nullable Boolean requestOptimizedExternalStorageAccess);
+    ParsingPackage setRequestRawExternalStorageAccess(
+            @Nullable Boolean requestRawExternalStorageAccess);
 
     ParsingPackage setCrossProfile(boolean crossProfile);
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 97e1b54..ea7135e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -400,7 +400,7 @@
 
     @Nullable
     @DataClass.ParcelWith(ForBoolean.class)
-    private Boolean requestOptimizedExternalStorageAccess;
+    private Boolean requestRawExternalStorageAccess;
 
     // TODO(chiuwinson): Non-null
     @Nullable
@@ -1086,7 +1086,7 @@
         appInfo.setGwpAsanMode(gwpAsanMode);
         appInfo.setMemtagMode(memtagMode);
         appInfo.setNativeHeapZeroInitialized(nativeHeapZeroInitialized);
-        appInfo.setRequestOptimizedExternalStorageAccess(requestOptimizedExternalStorageAccess);
+        appInfo.setRequestRawExternalStorageAccess(requestRawExternalStorageAccess);
         appInfo.setBaseCodePath(mBaseApkPath);
         appInfo.setBaseResourcePath(mBaseApkPath);
         appInfo.setCodePath(mPath);
@@ -1223,7 +1223,7 @@
         dest.writeMap(this.mProperties);
         dest.writeInt(this.memtagMode);
         dest.writeInt(this.nativeHeapZeroInitialized);
-        sForBoolean.parcel(this.requestOptimizedExternalStorageAccess, dest, flags);
+        sForBoolean.parcel(this.requestRawExternalStorageAccess, dest, flags);
     }
 
     public ParsingPackageImpl(Parcel in) {
@@ -1348,7 +1348,7 @@
         this.mProperties = in.readHashMap(boot);
         this.memtagMode = in.readInt();
         this.nativeHeapZeroInitialized = in.readInt();
-        this.requestOptimizedExternalStorageAccess = sForBoolean.unparcel(in);
+        this.requestRawExternalStorageAccess = sForBoolean.unparcel(in);
         assignDerivedFields();
     }
 
@@ -2131,8 +2131,8 @@
 
     @Nullable
     @Override
-    public Boolean hasRequestOptimizedExternalStorageAccess() {
-        return requestOptimizedExternalStorageAccess;
+    public Boolean hasRequestRawExternalStorageAccess() {
+        return requestRawExternalStorageAccess;
     }
 
     @Override
@@ -2586,8 +2586,8 @@
     }
 
     @Override
-    public ParsingPackageImpl setRequestOptimizedExternalStorageAccess(@Nullable Boolean value) {
-        requestOptimizedExternalStorageAccess = value;
+    public ParsingPackageImpl setRequestRawExternalStorageAccess(@Nullable Boolean value) {
+        requestRawExternalStorageAccess = value;
         return this;
     }
     @Override
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index cfd828e..4d4cc1a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -905,7 +905,7 @@
     int getNativeHeapZeroInitialized();
 
     @Nullable
-    Boolean hasRequestOptimizedExternalStorageAccess();
+    Boolean hasRequestRawExternalStorageAccess();
 
     // TODO(b/135203078): Hide and enforce going through PackageInfoUtils
     ApplicationInfo toAppInfoWithoutState();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 4e7bd70..a1ffc0c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -2019,9 +2019,9 @@
                         v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
             }
             if (sa.hasValue(
-                    R.styleable.AndroidManifestApplication_requestOptimizedExternalStorageAccess)) {
-                pkg.setRequestOptimizedExternalStorageAccess(sa.getBoolean(R.styleable
-                                .AndroidManifestApplication_requestOptimizedExternalStorageAccess,
+                    R.styleable.AndroidManifestApplication_requestRawExternalStorageAccess)) {
+                pkg.setRequestRawExternalStorageAccess(sa.getBoolean(R.styleable
+                                .AndroidManifestApplication_requestRawExternalStorageAccess,
                         false));
             }
         } finally {
diff --git a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
index fbc951e..bfbcfd8 100644
--- a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
@@ -23,6 +23,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -157,6 +158,61 @@
      */
     public static final class Font {
 
+        /**
+         * Builds a {@link Font}.
+         */
+        public static final class Builder {
+            private final@NonNull  String mPostScriptName;
+            private final @NonNull FontStyle mStyle;
+            private @NonNull List<FontVariationAxis> mAxes = Collections.emptyList();
+            private @IntRange(from = 0) int mIndex = 0;
+
+            /**
+             * Construct a {@link Font.Builder}
+             *
+             * @param postScriptName The PostScript name of the font file to use. PostScript name is
+             *                       in Name ID 6 field in 'name' table, as specified by OpenType
+             *                       specification.
+             * @param style          The style for this font.
+             */
+            public Builder(@NonNull String postScriptName, @NonNull FontStyle style) {
+                Objects.requireNonNull(postScriptName);
+                Preconditions.checkStringNotEmpty(postScriptName);
+                Objects.requireNonNull(style);
+                mPostScriptName = postScriptName;
+                mStyle = style;
+            }
+
+            /**
+             * A list of {@link FontVariationAxis} to specify axis tags and values for variable
+             * fonts.
+             */
+            public @NonNull Builder setAxes(@NonNull List<FontVariationAxis> axes) {
+                Objects.requireNonNull(axes);
+                Preconditions.checkCollectionElementsNotNull(axes, "axes");
+                mAxes = axes;
+                return this;
+            }
+
+            /**
+             * Sets font collection index for the Font.
+             *
+             * @see {@link android.R.attr#ttcIndex}.
+             */
+            public @NonNull Builder setIndex(@IntRange(from = 0) int index) {
+                Preconditions.checkArgumentNonnegative(index);
+                mIndex = index;
+                return this;
+            }
+
+            /**
+             * Build a {@link Font} instance.
+             */
+            public @NonNull Font build() {
+                return new Font(mPostScriptName, mStyle, mIndex, mAxes);
+            }
+        }
+
         @NonNull
         private final String mPostScriptName;
         @NonNull
@@ -164,6 +220,8 @@
         @NonNull
         private final List<FontVariationAxis> mAxes;
 
+        private final @IntRange(from = 0) int mIndex;
+
         /**
          * Constructs a FontStyleVariation.
          *
@@ -176,18 +234,15 @@
          *                       Name ID 6 field in 'name' table, as specified by OpenType
          *                       specification.
          * @param style          The style for this font.
+         * @param index          The index of the font in the collection.
          * @param axes           A list of {@link FontVariationAxis} to specify axis tags and values
          *                       for variable fonts.
          */
-        public Font(@NonNull String postScriptName, @NonNull FontStyle style,
-                @NonNull List<FontVariationAxis> axes) {
-            Objects.requireNonNull(postScriptName);
-            Preconditions.checkStringNotEmpty(postScriptName);
-            Objects.requireNonNull(style);
-            Objects.requireNonNull(axes);
-            Preconditions.checkCollectionElementsNotNull(axes, "axes");
+        private Font(@NonNull String postScriptName, @NonNull FontStyle style,
+                @IntRange(from = 0) int index, @NonNull List<FontVariationAxis> axes) {
             mPostScriptName = postScriptName;
             mStyle = style;
+            mIndex = index;
             mAxes = axes;
         }
 
@@ -207,6 +262,7 @@
             return mStyle;
         }
 
+
         /**
          * Returns the list of {@link FontVariationAxis}.
          */
@@ -217,12 +273,9 @@
 
         /**
          * Returns the index of collection
-         *
-         * TODO(183752879): Make font index configurable and make this SystemApi.
-         * @hide
          */
         public @IntRange(from = 0) int getIndex() {
-            return 0;
+            return mIndex;
         }
     }
 
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 365dea6..3c11d8e 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -575,7 +575,6 @@
                 && rate > CAPPED_SAMPLING_RATE_LEVEL
                 && mIsPackageDebuggable
                 && !mHasHighSamplingRateSensorsPermission) {
-            Compatibility.reportChange(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION);
             throw new SecurityException("To use the sampling rate level " + rate
                     + ", app needs to declare the normal permission"
                     + " HIGH_SAMPLING_RATE_SENSORS.");
@@ -787,7 +786,6 @@
                     && rateUs < CAPPED_SAMPLING_PERIOD_US
                     && mManager.mIsPackageDebuggable
                     && !mManager.mHasHighSamplingRateSensorsPermission) {
-                Compatibility.reportChange(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION);
                 throw new SecurityException("To use the sampling rate of " + rateUs
                         + " microseconds, app needs to declare the normal permission"
                         + " HIGH_SAMPLING_RATE_SENSORS.");
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
index 9457d8f1..11c426a 100644
--- a/core/java/android/hardware/display/DeviceProductInfo.java
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -17,6 +17,7 @@
 package android.hardware.display;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
@@ -118,6 +119,7 @@
      * @return Model year of the device. Return -1 if not available. Typically,
      * one of model year or manufacture year is available.
      */
+    @IntRange(from = -1)
     public int getModelYear()  {
         return mModelYear != null ? mModelYear : -1;
     }
@@ -126,6 +128,7 @@
      * @return The year of manufacture, or -1 it is not available. Typically,
      * one of model year or manufacture year is available.
      */
+    @IntRange(from = -1)
     public int getManufactureYear()  {
         if (mManufactureDate == null) {
             return -1;
@@ -134,9 +137,10 @@
     }
 
     /**
-     * @return The week of manufacture, or -1 it is not available. Typically,
-     * not present if model year is available.
+     * @return The week of manufacture which ranges from 1 to 53, or -1 it is not available.
+     * Typically, it is not present if model year is available.
      */
+    @IntRange(from = -1, to = 53)
     public int getManufactureWeek() {
         if (mManufactureDate == null) {
             return -1;
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index cdc219a..983a43a 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -391,8 +391,9 @@
             }
 
             final int numListeners = mDisplayListeners.size();
+            DisplayInfo info = getDisplayInfo(displayId);
             for (int i = 0; i < numListeners; i++) {
-                mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
+                mDisplayListeners.get(i).sendDisplayEvent(displayId, event, info);
             }
             if (event == EVENT_DISPLAY_CHANGED && mDispatchNativeCallbacks) {
                 // Choreographer only supports a single display, so only dispatch refresh rate
@@ -894,6 +895,8 @@
         public final DisplayListener mListener;
         public long mEventsMask;
 
+        private final DisplayInfo mDisplayInfo = new DisplayInfo();
+
         DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper,
                 @EventsMask long eventsMask) {
             super(looper, null, true /*async*/);
@@ -901,8 +904,8 @@
             mEventsMask = eventsMask;
         }
 
-        public void sendDisplayEvent(int displayId, @DisplayEvent int event) {
-            Message msg = obtainMessage(event, displayId, 0);
+        public void sendDisplayEvent(int displayId, @DisplayEvent int event, DisplayInfo info) {
+            Message msg = obtainMessage(event, displayId, 0, info);
             sendMessage(msg);
         }
 
@@ -924,7 +927,11 @@
                     break;
                 case EVENT_DISPLAY_CHANGED:
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
-                        mListener.onDisplayChanged(msg.arg1);
+                        DisplayInfo newInfo = (DisplayInfo) msg.obj;
+                        if (newInfo != null && !newInfo.equals(mDisplayInfo)) {
+                            mDisplayInfo.copyFrom(newInfo);
+                            mListener.onDisplayChanged(msg.arg1);
+                        }
                     }
                     break;
                 case EVENT_DISPLAY_REMOVED:
diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
index f13326b..d06bc1d 100644
--- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
+++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
@@ -84,7 +84,6 @@
      *         of the result
      */
     public void oneTouchPlay(OneTouchPlayCallback callback) {
-        // TODO: Use PendingResult.
         try {
             mService.oneTouchPlay(getCallbackWrapper(callback));
         } catch (RemoteException e) {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 632eb15..ec83c4e 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -772,6 +772,12 @@
         return DebugUtils.flagsToString(ConnectivityManager.class, "BLOCKED_", blockedReasons);
     }
 
+    /** @hide */
+    @NonNull
+    public static String allowedReasonsToString(int allowedReasons) {
+        return DebugUtils.flagsToString(NetworkPolicyManager.class, "ALLOWED_", allowedReasons);
+    }
+
     /**
      * Register a {@link NetworkPolicyCallback} to listen for changes to network blocked status
      * of apps.
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 11445e9..d5cc01a 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -24,6 +24,7 @@
 import android.nfc.TechListParcel;
 import android.nfc.IAppCallback;
 import android.nfc.INfcAdapterExtras;
+import android.nfc.INfcControllerAlwaysOnStateCallback;
 import android.nfc.INfcTag;
 import android.nfc.INfcCardEmulation;
 import android.nfc.INfcFCardEmulation;
@@ -75,4 +76,6 @@
     boolean setControllerAlwaysOn(boolean value);
     boolean isControllerAlwaysOn();
     boolean isControllerAlwaysOnSupported();
+    void registerControllerAlwaysOnStateCallback(in INfcControllerAlwaysOnStateCallback callback);
+    void unregisterControllerAlwaysOnStateCallback(in INfcControllerAlwaysOnStateCallback callback);
 }
diff --git a/core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl b/core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl
new file mode 100644
index 0000000..1e4fdd7
--- /dev/null
+++ b/core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.nfc;
+
+/**
+ * @hide
+ */
+oneway interface INfcControllerAlwaysOnStateCallback {
+  /**
+   * Called whenever the controller always on state changes
+   *
+   * @param isEnabled true if the state is enabled, false otherwise
+   */
+  void onControllerAlwaysOnStateChanged(boolean isEnabled);
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index eed2c77..bbf802c 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
@@ -47,6 +48,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Represents the local NFC adapter.
@@ -65,6 +67,8 @@
 public final class NfcAdapter {
     static final String TAG = "NFC";
 
+    private final NfcControllerAlwaysOnStateListener mControllerAlwaysOnStateListener;
+
     /**
      * Intent to start an activity when a tag with NDEF payload is discovered.
      *
@@ -350,22 +354,6 @@
             "android.nfc.extra.HANDOVER_TRANSFER_STATUS";
 
     /** @hide */
-    public static final String ACTION_ALWAYS_ON_STATE_CHANGED =
-            "android.nfc.action.ALWAYS_ON_STATE_CHANGED";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_ALWAYS_ON_STATE_CHANGED}
-     * intents to request the current power state. Possible values are:
-     * {@link #STATE_OFF},
-     * {@link #STATE_TURNING_ON},
-     * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF},
-     * @hide
-     */
-    public static final String EXTRA_ALWAYS_ON_STATE =
-            "android.nfc.extra.ALWAYS_ON_STATE";
-
-    /** @hide */
     public static final int HANDOVER_TRANSFER_STATUS_SUCCESS = 0;
     /** @hide */
     public static final int HANDOVER_TRANSFER_STATUS_FAILURE = 1;
@@ -430,6 +418,22 @@
     }
 
     /**
+     * A callback to be invoked when NFC controller always on state changes.
+     * <p>Register your {@code ControllerAlwaysOnStateCallback} implementation with {@link
+     * NfcAdapter#registerControllerAlwaysOnStateCallback} and disable it with {@link
+     * NfcAdapter#unregisterControllerAlwaysOnStateCallback}.
+     * @see #registerControllerAlwaysOnStateCallback
+     * @hide
+     */
+    @SystemApi
+    public interface ControllerAlwaysOnStateCallback {
+        /**
+         * Called on NFC controller always on state changes
+         */
+        void onStateChanged(boolean isEnabled);
+    }
+
+    /**
      * A callback to be invoked when the system successfully delivers your {@link NdefMessage}
      * to another device.
      * @see #setOnNdefPushCompleteCallback
@@ -744,6 +748,7 @@
         mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, INfcUnlockHandler>();
         mTagRemovedListener = null;
         mLock = new Object();
+        mControllerAlwaysOnStateListener = new NfcControllerAlwaysOnStateListener(getService());
     }
 
     /**
@@ -2239,14 +2244,16 @@
     /**
      * Sets NFC controller always on feature.
      * <p>This API is for the NFCC internal state management. It allows to discriminate
-     * the controller function from the NFC function by keeping the NFC Controller on without
+     * the controller function from the NFC function by keeping the NFC controller on without
      * any NFC RF enabled if necessary.
-     * <p>This call is asynchronous. Listen for {@link #ACTION_ALWAYS_ON_STATE_CHANGED}
-     * broadcasts to find out when the operation is complete.
-     * <p>If this returns true, then either NFCC is already on, or
-     * a {@link #ACTION_ALWAYS_ON_STATE_CHANGED} broadcast will be sent to indicate
-     * a state transition.
-     * If this returns false, then there is some problem that prevents an attempt to turn NFCC on.
+     * <p>This call is asynchronous. Register a callback {@link #ControllerAlwaysOnStateCallback}
+     * by {@link #registerControllerAlwaysOnStateCallback} to find out when the operation is
+     * complete.
+     * <p>If this returns true, then either NFCC always on state has been set based on the value,
+     * or a {@link ControllerAlwaysOnStateCallback#onStateChanged(boolean)} will be invoked to
+     * indicate the state change.
+     * If this returns false, then there is some problem that prevents an attempt to turn NFCC
+     * always on.
      * @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is
      * disabled), if false the NFCC will follow completely the Nfc adapter state.
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
@@ -2284,7 +2291,6 @@
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
      * @hide
      */
-
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
     public boolean isControllerAlwaysOn() {
@@ -2313,7 +2319,6 @@
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
      * @hide
      */
-
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
     public boolean isControllerAlwaysOnSupported() {
@@ -2337,4 +2342,39 @@
             return false;
         }
     }
+
+    /**
+     * Register a {@link ControllerAlwaysOnStateCallback} to listen for NFC controller always on
+     * state changes
+     * <p>The provided callback will be invoked by the given {@link Executor}.
+     *
+     * @param executor an {@link Executor} to execute given callback
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public void registerControllerAlwaysOnStateCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull ControllerAlwaysOnStateCallback callback) {
+        mControllerAlwaysOnStateListener.register(executor, callback);
+    }
+
+    /**
+     * Unregister the specified {@link ControllerAlwaysOnStateCallback}
+     * <p>The same {@link ControllerAlwaysOnStateCallback} object used when calling
+     * {@link #registerControllerAlwaysOnStateCallback(Executor, ControllerAlwaysOnStateCallback)}
+     * must be used.
+     *
+     * <p>Callbacks are automatically unregistered when application process goes away
+     *
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public void unregisterControllerAlwaysOnStateCallback(
+            @NonNull ControllerAlwaysOnStateCallback callback) {
+        mControllerAlwaysOnStateListener.unregister(callback);
+    }
 }
diff --git a/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java b/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java
new file mode 100644
index 0000000..69a9ec7
--- /dev/null
+++ b/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java
@@ -0,0 +1,120 @@
+/*
+ * 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.nfc;
+
+import android.annotation.NonNull;
+import android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class NfcControllerAlwaysOnStateListener extends INfcControllerAlwaysOnStateCallback.Stub {
+    private static final String TAG = "NfcControllerAlwaysOnStateListener";
+
+    private final INfcAdapter mAdapter;
+
+    private final Map<ControllerAlwaysOnStateCallback, Executor> mCallbackMap = new HashMap<>();
+
+    private boolean mCurrentState = false;
+    private boolean mIsRegistered = false;
+
+    public NfcControllerAlwaysOnStateListener(@NonNull INfcAdapter adapter) {
+        mAdapter = adapter;
+    }
+
+    /**
+     * Register a {@link ControllerAlwaysOnStateCallback} with this
+     * {@link NfcControllerAlwaysOnStateListener}
+     *
+     * @param executor an {@link Executor} to execute given callback
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     */
+    public void register(@NonNull Executor executor,
+            @NonNull ControllerAlwaysOnStateCallback callback) {
+        synchronized (this) {
+            if (mCallbackMap.containsKey(callback)) {
+                return;
+            }
+
+            mCallbackMap.put(callback, executor);
+            if (!mIsRegistered) {
+                try {
+                    mAdapter.registerControllerAlwaysOnStateCallback(this);
+                    mIsRegistered = true;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to register ControllerAlwaysOnStateListener");
+                }
+            }
+        }
+    }
+
+    /**
+     * Unregister the specified {@link ControllerAlwaysOnStateCallback}
+     *
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     */
+    public void unregister(@NonNull ControllerAlwaysOnStateCallback callback) {
+        synchronized (this) {
+            if (!mCallbackMap.containsKey(callback)) {
+                return;
+            }
+
+            mCallbackMap.remove(callback);
+
+            if (mCallbackMap.isEmpty() && mIsRegistered) {
+                try {
+                    mAdapter.unregisterControllerAlwaysOnStateCallback(this);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to unregister ControllerAlwaysOnStateListener");
+                }
+                mIsRegistered = false;
+            }
+        }
+    }
+
+    private void sendCurrentState(@NonNull ControllerAlwaysOnStateCallback callback) {
+        synchronized (this) {
+            Executor executor = mCallbackMap.get(callback);
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                executor.execute(() -> callback.onStateChanged(
+                        mCurrentState));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @Override
+    public void onControllerAlwaysOnStateChanged(boolean isEnabled) {
+        synchronized (this) {
+            mCurrentState = isEnabled;
+            for (ControllerAlwaysOnStateCallback cb : mCallbackMap.keySet()) {
+                sendCurrentState(cb);
+            }
+        }
+    }
+}
+
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index d7bb226..ba6fc6e 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -53,6 +53,11 @@
                          in PerUidReadTimeouts[] perUidReadTimeouts);
 
     /**
+     * PM/system is done with this storage, ok to increase timeouts.
+     */
+    void onInstallationComplete(int storageId);
+
+    /**
      * Bind-mounts a path under a storage to a full path. Can be permanent or temporary.
      */
     const int BIND_TEMPORARY = 0;
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 2a42b98..6e25968 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -205,16 +205,26 @@
     /**
      * Resets the states and unbinds storage instances for an installation session.
      */
-    public void cleanUp() {
-        if (mDefaultStorage == null) {
-            return;
+    public void cleanUpAndMarkComplete() {
+        IncrementalStorage defaultStorage = cleanUp();
+        if (defaultStorage != null) {
+            defaultStorage.onInstallationComplete();
+        }
+    }
+
+    private IncrementalStorage cleanUp() {
+        IncrementalStorage defaultStorage = mDefaultStorage;
+        mInheritedStorage = null;
+        mDefaultStorage = null;
+        if (defaultStorage == null) {
+            return null;
         }
 
         try {
             mIncrementalManager.unregisterLoadingProgressCallbacks(mStageDir.getAbsolutePath());
-            mDefaultStorage.unBind(mStageDir.getAbsolutePath());
+            defaultStorage.unBind(mStageDir.getAbsolutePath());
         } catch (IOException ignored) {
         }
-        mDefaultStorage = null;
+        return defaultStorage;
     }
 }
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 7cf0144..c19e29f 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -398,7 +398,7 @@
     }
 
     /**
-     * Iinitializes and starts the DataLoader.
+     * Initializes and starts the DataLoader.
      * This makes sure all install-time parameters are applied.
      * Does not affect persistent DataLoader params.
      * @return True if start request was successfully queued.
@@ -419,6 +419,18 @@
         }
     }
 
+    /**
+     * Marks the completion of installation.
+     */
+    public void onInstallationComplete() {
+        try {
+            mService.onInstallationComplete(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+
     private static final int UUID_BYTE_SIZE = 16;
 
     /**
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 751e9aa..1a40f06 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -35,6 +35,7 @@
 import android.app.PropertyInvalidatedCache;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
+import android.content.AttributionSource;
 import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
@@ -42,9 +43,7 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
-import android.location.LocationManager;
 import android.media.AudioManager;
-import android.content.AttributionSource;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
@@ -52,8 +51,8 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.os.UserHandle;
-import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
@@ -104,6 +103,20 @@
     public static final long CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS = 146211400;
 
     /**
+     * The time to wait in between refreshing the exempted indicator role packages
+     */
+    private static final long EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS = 15000;
+
+    private static long sLastIndicatorUpdateTime = -1;
+
+    private static final int[] EXEMPTED_ROLES = {R.string.config_systemAmbientAudioIntelligence,
+        R.string.config_systemUiIntelligence, R.string.config_systemAudioIntelligence,
+        R.string.config_systemNotificationIntelligence, R.string.config_systemTextIntelligence,
+        R.string.config_systemVisualIntelligence};
+
+    private static final String[] INDICATOR_EXEMPTED_PACKAGES = new String[EXEMPTED_ROLES.length];
+
+    /**
      * Note: Changing this won't do anything on its own - you should also change the filtering in
      * {@link #shouldTraceGrant}.
      *
@@ -873,21 +886,31 @@
     }
 
     /**
-     * Check if this package/op combination is exempted from indicators
-     * @return
+     * Determine if a package should be shown in indicators. Only a select few roles, and the
+     * system app itself, are hidden. These values are updated at most every 15 seconds.
      * @hide
      */
-    public static boolean isSpecialCaseShownIndicator(@NonNull Context context,
+    public static boolean shouldShowPackageForIndicatorCached(@NonNull Context context,
             @NonNull String packageName) {
-
-        if (packageName.equals(SYSTEM_PKG)) {
+        if (SYSTEM_PKG.equals(packageName)) {
             return false;
         }
+        long now = SystemClock.elapsedRealtime();
+        if (sLastIndicatorUpdateTime == -1
+                || (now - sLastIndicatorUpdateTime) > EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS) {
+            sLastIndicatorUpdateTime = now;
+            for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
+                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 DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled",
-                false)
-                || packageName.equals(context.getString(R.string.config_systemSpeechRecognizer))
-                || context.getSystemService(LocationManager.class).isProviderPackage(packageName);
+        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 2d6fa3c..53ba259 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -26,8 +26,6 @@
 import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE;
 import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO;
 import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
-import static android.app.AppOpsManager.opToPermission;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
 import static android.media.AudioSystem.MODE_IN_COMMUNICATION;
 import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
 
@@ -284,10 +282,6 @@
                         continue;
                     }
 
-                    if (!shouldShowPermissionsHub() && !isUserSensitive(packageName, user, op)) {
-                        continue;
-                    }
-
                     boolean isRunning = attrOpEntry.isRunning()
                             || lastAccessTime >= runningThreshold;
 
@@ -302,7 +296,7 @@
                     OpUsage usage = new OpUsage(packageName, attributionTag, op, uid,
                             lastAccessTime, isRunning, proxyUsage);
 
-                    Integer packageAttr = usage.getPackageAttrHash();
+                    Integer packageAttr = usage.getPackageIdHash();
                     if (!usages.containsKey(permGroupName)) {
                         ArrayMap<Integer, OpUsage> map = new ArrayMap<>();
                         map.put(packageAttr, usage);
@@ -342,19 +336,19 @@
         }
 
         ArrayMap<Integer, OpUsage> allUsages = new ArrayMap<>();
-        // map of uid -> most recent non-proxy-related usage for that uid.
+        // map of packageName and uid hash -> most recent non-proxy-related usage for that uid.
         ArrayMap<Integer, OpUsage> mostRecentUsages = new ArrayMap<>();
-        // set of all uids involved in a proxy usage
-        ArraySet<Integer> proxyUids = new ArraySet<>();
+        // set of all packages involved in a proxy usage
+        ArraySet<Integer> proxyPackages = new ArraySet<>();
         // map of usage -> list of proxy app labels
         ArrayMap<OpUsage, ArrayList<CharSequence>> proxyLabels = new ArrayMap<>();
         // map of usage.proxy hash -> usage hash, telling us if a usage is a proxy
         ArrayMap<Integer, OpUsage> proxies = new ArrayMap<>();
         for (int i = 0; i < usages.size(); i++) {
             OpUsage usage = usages.get(i);
-            allUsages.put(usage.getPackageAttrHash(), usage);
+            allUsages.put(usage.getPackageIdHash(), usage);
             if (usage.proxy != null) {
-                proxies.put(usage.proxy.getPackageAttrHash(), usage);
+                proxies.put(usage.proxy.getPackageIdHash(), usage);
             }
         }
 
@@ -365,25 +359,27 @@
                 continue;
             }
 
-            int usageAttr = usage.getPackageAttrHash();
+            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) {
                 proxyLabels.put(usage, new ArrayList<>());
-                proxyUids.add(usage.uid);
+                proxyPackages.add(usage.getPackageIdHash());
             }
             // If this usage is not by the system, and is more recent than the next-most recent
-            // for it's uid, save it.
-            if (!usage.packageName.equals(SYSTEM_PKG) && (!mostRecentUsages.containsKey(usage.uid)
-                    || usage.lastAccessTime > mostRecentUsages.get(usage.uid).lastAccessTime)) {
-                mostRecentUsages.put(usage.uid, usage);
+            // for it's uid and package name, save it.
+            int usageId = usage.getPackageIdHash();
+            OpUsage lastMostRecent = mostRecentUsages.get(usageId);
+            if (shouldShowPackage(usage.packageName) && (lastMostRecent == null
+                    || usage.lastAccessTime > lastMostRecent.lastAccessTime)) {
+                mostRecentUsages.put(usageId, usage);
             }
         }
 
         // get all the proxy labels
         for (int numStart = 0; numStart < proxyLabels.size(); numStart++) {
             OpUsage start = proxyLabels.keyAt(numStart);
-            // Remove any non-proxy usage for the starting uid
-            mostRecentUsages.remove(start.uid);
+            // Remove any non-proxy usage for the starting package
+            mostRecentUsages.remove(start.getPackageIdHash());
             OpUsage currentUsage = proxyLabels.keyAt(numStart);
             ArrayList<CharSequence> proxyLabelList = proxyLabels.get(currentUsage);
             if (currentUsage == null || proxyLabelList == null) {
@@ -393,14 +389,13 @@
             int maxUsages = allUsages.size();
             while (currentUsage.proxy != null) {
 
-                if (allUsages.containsKey(currentUsage.proxy.getPackageAttrHash())) {
-                    currentUsage = allUsages.get(currentUsage.proxy.getPackageAttrHash());
+                if (allUsages.containsKey(currentUsage.proxy.getPackageIdHash())) {
+                    currentUsage = allUsages.get(currentUsage.proxy.getPackageIdHash());
                 } else {
                     // We are missing the proxy usage. This may be because it's a one-step trusted
                     // proxy. Check if we should show the proxy label, and show it, if so.
                     OpUsage proxy = currentUsage.proxy;
-                    if (PermissionManager.isSpecialCaseShownIndicator(mContext, proxy.packageName)
-                            || isUserSensitive(proxy.packageName, proxy.getUser(), proxy.op)) {
+                    if (shouldShowPackage(proxy.packageName)) {
                         currentUsage = proxy;
                         // We've effectively added one usage, so increment the max number of usages
                         maxUsages++;
@@ -409,17 +404,16 @@
                     }
                 }
 
-
                 if (currentUsage == null || iterNum == maxUsages
-                        || currentUsage.getPackageAttrHash() == start.getPackageAttrHash()) {
+                        || currentUsage.getPackageIdHash() == start.getPackageIdHash()) {
                     // We have an invalid state, or a cycle, so break
                     break;
                 }
 
-                proxyUids.add(currentUsage.uid);
+                proxyPackages.add(currentUsage.getPackageIdHash());
                 // Don't add an app label for the main app, or the system app
                 if (!currentUsage.packageName.equals(start.packageName)
-                        && !currentUsage.packageName.equals(SYSTEM_PKG)) {
+                        && shouldShowPackage(currentUsage.packageName)) {
                     try {
                         PackageManager userPkgManager =
                                 getUserContext(currentUsage.getUser()).getPackageManager();
@@ -440,26 +434,17 @@
                     proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList));
         }
 
-        for (int uid : mostRecentUsages.keySet()) {
-            if (!proxyUids.contains(uid)) {
-                usagesAndLabels.put(mostRecentUsages.get(uid), null);
+        for (int packageHash : mostRecentUsages.keySet()) {
+            if (!proxyPackages.contains(packageHash)) {
+                usagesAndLabels.put(mostRecentUsages.get(packageHash), null);
             }
         }
 
         return usagesAndLabels;
     }
 
-    private boolean isUserSensitive(String packageName, UserHandle user, String op) {
-        if (op.equals(OPSTR_PHONE_CALL_CAMERA) || op.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
-            return true;
-        }
-
-        if (opToPermission(op) == null) {
-            return false;
-        }
-
-        int permFlags = mPkgManager.getPermissionFlags(opToPermission(op), packageName, user);
-        return (permFlags & FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
+    private boolean shouldShowPackage(String packageName) {
+        return PermissionManager.shouldShowPackageForIndicatorCached(mContext, packageName);
     }
 
     /**
@@ -490,8 +475,8 @@
             return UserHandle.getUserHandleForUid(uid);
         }
 
-        public int getPackageAttrHash() {
-            return Objects.hash(packageName, attributionTag, uid);
+        public int getPackageIdHash() {
+            return Objects.hash(packageName, uid);
         }
 
         @Override
diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java
index 030b863..fb89eb0 100644
--- a/core/java/android/provider/SimPhonebookContract.java
+++ b/core/java/android/provider/SimPhonebookContract.java
@@ -17,13 +17,14 @@
 package android.provider;
 
 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN;
-import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN_PATH_SEGMENT;
 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN;
-import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN_PATH_SEGMENT;
 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN;
-import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.PATH_SEGMENT_EF_ADN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.PATH_SEGMENT_EF_FDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.PATH_SEGMENT_EF_SDN;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.WorkerThread;
@@ -78,11 +79,11 @@
     public static String getEfUriPath(@ElementaryFiles.EfType int efType) {
         switch (efType) {
             case EF_ADN:
-                return EF_ADN_PATH_SEGMENT;
+                return PATH_SEGMENT_EF_ADN;
             case EF_FDN:
-                return EF_FDN_PATH_SEGMENT;
+                return PATH_SEGMENT_EF_FDN;
             case EF_SDN:
-                return EF_SDN_PATH_SEGMENT;
+                return PATH_SEGMENT_EF_SDN;
             default:
                 throw new IllegalArgumentException("Unsupported EfType " + efType);
         }
@@ -109,9 +110,9 @@
      * the phone number can contain at most {@link ElementaryFiles#PHONE_NUMBER_MAX_LENGTH}
      * characters. The {@link SimRecords#NAME} column can contain at most
      * {@link ElementaryFiles#NAME_MAX_LENGTH} bytes when it is encoded for storage on the SIM.
-     * Encoding is done internally and so the name should be provided unencoded but the number of
-     * bytes required to encode it will vary depending on the characters it contains. This length
-     * can be determined by calling
+     * Encoding is done internally and so the name should be provided to these provider APIs as a
+     * Java String but the number of bytes required to encode it for storage will vary depending on
+     * the characters it contains. This length can be determined by calling
      * {@link SimRecords#getEncodedNameLength(ContentResolver, String)}.
      * </p>
      * <h3>Operations </h3>
@@ -308,7 +309,8 @@
          */
         @NonNull
         public static Uri getItemUri(
-                int subscriptionId, @ElementaryFiles.EfType int efType, int recordNumber) {
+                int subscriptionId, @ElementaryFiles.EfType int efType,
+                @IntRange(from = 1) int recordNumber) {
             // Elementary file record indices are 1-based.
             Preconditions.checkArgument(recordNumber > 0, "Invalid recordNumber");
 
@@ -332,6 +334,7 @@
          * @see ElementaryFiles#NAME_MAX_LENGTH
          */
         @WorkerThread
+        @IntRange(from = 0)
         public static int getEncodedNameLength(
                 @NonNull ContentResolver resolver, @NonNull String name) {
             Objects.requireNonNull(name);
@@ -442,12 +445,27 @@
          * methods operating on this Uri will throw UnsupportedOperationException
          */
         public static final int EF_SDN = 3;
-        /** @hide */
-        public static final String EF_ADN_PATH_SEGMENT = "adn";
-        /** @hide */
-        public static final String EF_FDN_PATH_SEGMENT = "fdn";
-        /** @hide */
-        public static final String EF_SDN_PATH_SEGMENT = "sdn";
+        /**
+         * The Uri path segment used to target the ADN elementary file for SimPhonebookProvider
+         * content operations.
+         *
+         * @hide
+         */
+        public static final String PATH_SEGMENT_EF_ADN = "adn";
+        /**
+         * The Uri path segment used to target the FDN elementary file for SimPhonebookProvider
+         * content operations.
+         *
+         * @hide
+         */
+        public static final String PATH_SEGMENT_EF_FDN = "fdn";
+        /**
+         * The Uri path segment used to target the SDN elementary file for SimPhonebookProvider
+         * content operations.
+         *
+         * @hide
+         */
+        public static final String PATH_SEGMENT_EF_SDN = "sdn";
         /** The MIME type of CONTENT_URI providing a directory of ADN-like elementary files. */
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
         /** The MIME type of a CONTENT_URI subdirectory of a single ADN-like elementary file. */
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 38945f5..f3a8b5d 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -362,6 +362,18 @@
 
         /**
          * Used to determine the currently configured default SMS package.
+         * <p>
+         * As of Android 11 apps will need specific permission to query other packages. To use
+         * this method an app must include in their AndroidManifest:
+         * <queries>
+         *   <intent>
+         *     <action android:name="android.provider.Telephony.SMS_DELIVER"/>
+         *   </intent>
+         * </queries>
+         * Which will allow them to query packages which declare intent filters that include
+         * the {@link android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION} intent.
+         * </p>
+         *
          * @param context context of the requesting application
          * @return package name for the default SMS package or null
          */
diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java
index a9348c6..a2b22e8 100644
--- a/core/java/android/service/timezone/TimeZoneProviderService.java
+++ b/core/java/android/service/timezone/TimeZoneProviderService.java
@@ -50,35 +50,49 @@
  *
  * <p>Once stopped or failed, providers are required to stop generating callbacks.
  *
- * <p>Provider discovery:
- *
- * <p>You must declare the service in your manifest file with the
- * {@link android.Manifest.permission#BIND_TIME_ZONE_PROVIDER_SERVICE} permission,
- * and include an intent filter with the necessary action indicating what type of provider it is.
- *
- * <p>Device configuration can influence how {@link TimeZoneProviderService}s are discovered.
- * In one mode, there can be multiple {@link TimeZoneProviderService}s configured with the same
- * action, and the one with the highest "serviceVersion" metadata will be used.
- *
- * <p>{@link TimeZoneProviderService}s may be deployed into processes that run once-per-user
- * or once-per-device (i.e. they service multiple users). The "serviceIsMultiuser" metadata must
- * be set accordingly.
- *
  * <p>Provider types:
  *
  * <p>Android supports up to two <em>location-derived</em> time zone providers. These are called the
- * "primary" and "secondary" location time zone provider. The primary location time zone provider is
- * started first and will be used until it becomes uncertain or fails, at which point the secondary
- * provider will be started.
+ * "primary" and "secondary" location time zone providers. When a location-derived time zone is
+ * required, the primary location time zone provider is started first and used until it becomes
+ * uncertain or fails, at which point the secondary provider will be started. The secondary will be
+ * started and stopped as needed.
  *
- * <p>Location-derived time zone providers are configured using {@link
- * #PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} and {@link
- * #SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} intent-filter actions respectively.
- * Besides declaring the android:permission attribute mentioned above, the application supplying a
- * location provider must be granted the {@link
+ * <p>Provider discovery:
+ *
+ * <p>Each provider is optional and can be disabled. When enabled, a provider's package name must
+ * be explicitly configured in the system server, see {@code
+ * config_primaryLocationTimeZoneProviderPackageName} and {@code
+ * config_secondaryLocationTimeZoneProviderPackageName} for details.
+ *
+ * <p>You must declare the service in the AndroidManifest of the app hosting the provider with the
+ * {@link android.Manifest.permission#BIND_TIME_ZONE_PROVIDER_SERVICE} permission,
+ * and include an intent filter with the necessary action indicating that it is the primary
+ * provider ({@link #PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE}) or the secondary
+ * provider ({@link #SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE}).
+ *
+ * <p>Besides declaring the android:permission attribute mentioned above, the application supplying
+ * a location provider must be granted the {@link
  * android.Manifest.permission#INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE} permission to be
  * accepted by the system server.
  *
+ * <p>{@link TimeZoneProviderService}s may be deployed into processes that run once-per-user
+ * or once-per-device (i.e. they service multiple users). See serviceIsMultiuser metadata below for
+ * configuration details.
+ *
+ * <p>The service may specify metadata on its capabilities:
+ *
+ * <ul>
+ *     <li>
+ *         "serviceIsMultiuser": A boolean property, indicating if the service wishes to take
+ *         responsibility for handling changes to the current user on the device. If true, the
+ *         service will always be bound from the system user. If false, the service will always be
+ *         bound from the current user. If the current user changes, the old binding will be
+ *         released, and a new binding established under the new user. Assumed to be false if not
+ *         specified.
+ *     </li>
+ * </ul>
+ *
  * <p>For example:
  * <pre>
  *   &lt;uses-permission
@@ -86,7 +100,7 @@
  *
  * ...
  *
- *     &lt;service android:name=".FooTimeZoneProviderService"
+ *     &lt;service android:name=".ExampleTimeZoneProviderService"
  *             android:exported="true"
  *             android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"&gt;
  *         &lt;intent-filter&gt;
@@ -94,7 +108,6 @@
  *             android:name="android.service.timezone.SecondaryLocationTimeZoneProviderService"
  *             /&gt;
  *         &lt;/intent-filter&gt;
- *         &lt;meta-data android:name="serviceVersion" android:value="1" /&gt;
  *         &lt;meta-data android:name="serviceIsMultiuser" android:value="true" /&gt;
  *     &lt;/service&gt;
  * </pre>
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 78e5eab..7e8622a 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -2427,9 +2427,8 @@
                     @Override
                     public void onError(String errorInfo) {
                         Log.w(TAG, "System TTS connection error: " + errorInfo);
-                        // The connection was not established successfully - handle as
-                        // disconnection: clear the state and notify the user.
-                        onServiceDisconnected(/* componentName= */ null);
+                        // There is an error connecting to the engine - notify the listener.
+                        dispatchOnInit(ERROR);
                     }
                 });
 
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 49065aa..a1ffe34 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -110,6 +110,7 @@
      * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public static final int LISTEN_MESSAGE_WAITING_INDICATOR                = 0x00000004;
 
     /**
@@ -123,6 +124,7 @@
      * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public static final int LISTEN_CALL_FORWARDING_INDICATOR                = 0x00000008;
 
     /**
@@ -141,6 +143,7 @@
      * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public static final int LISTEN_CELL_LOCATION                            = 0x00000010;
 
     /**
@@ -194,7 +197,7 @@
      * @see #onSignalStrengthsChanged
      *
      * @hide
-     * @deprecated Use {@link TelephonyCallback.AlwaysReportedSignalStrengthListener}
+     * @deprecated Use TelephonyManager#setSignalStrengthUpdateRequest
      * instead.
      */
     @Deprecated
@@ -204,13 +207,18 @@
     /**
      * Listen for changes to observed cell info.
      *
-     * Listening to this event requires the {@link Manifest.permission#ACCESS_FINE_LOCATION}
+     * Listening to this event requires the {@link Manifest.permission#READ_PHONE_STATE} and
+     * {@link Manifest.permission#ACCESS_FINE_LOCATION}
      * permission.
      *
      * @see #onCellInfoChanged
      * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead.
      */
     @Deprecated
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public static final int LISTEN_CELL_INFO = 0x00000400;
 
     /**
@@ -261,7 +269,7 @@
      *
      * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
      *
-     * @see #onServiceStateChanged(ServiceState)
+     * @see #onSrvccStateChanged
      * @hide
      * @deprecated Use {@link TelephonyCallback.SrvccStateListener} instead.
      */
@@ -376,6 +384,7 @@
      *  @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000;
 
     /**
@@ -399,6 +408,7 @@
      * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
 
     /**
@@ -487,7 +497,10 @@
      * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead.
      */
     @Deprecated
-    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000;
 
     /**
@@ -503,7 +516,10 @@
      * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead.
      */
     @Deprecated
-    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public static final int LISTEN_BARRING_INFO = 0x80000000;
 
     /*
@@ -650,6 +666,7 @@
      * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public void onMessageWaitingIndicatorChanged(boolean mwi) {
         // default implementation empty
     }
@@ -666,6 +683,7 @@
      * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public void onCallForwardingIndicatorChanged(boolean cfi) {
         // default implementation empty
     }
@@ -682,6 +700,7 @@
      * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public void onCellLocationChanged(CellLocation location) {
         // default implementation empty
     }
@@ -801,6 +820,10 @@
      * @param cellInfo is the list of currently visible cells.
      * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead.
      */
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     @Deprecated
     public void onCellInfoChanged(List<CellInfo> cellInfo) {
         // default implementation empty
@@ -875,14 +898,14 @@
      * subId. Otherwise, this callback applies to
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
-     * <p>Requires permission {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
      * or the calling app has carrier privileges
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @param dataConnectionState {@link PreciseDataConnectionState}
      * @deprecated Use {@link TelephonyCallback.PreciseDataConnectionStateListener} instead.
      */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
     @Deprecated
     public void onPreciseDataConnectionStateChanged(
             @NonNull PreciseDataConnectionState dataConnectionState) {
@@ -924,6 +947,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void onSrvccStateChanged(@SrvccState int srvccState) {
         // default implementation empty
     }
@@ -944,6 +968,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void onVoiceActivationStateChanged(@SimActivationState int state) {
         // default implementation empty
     }
@@ -1026,6 +1051,7 @@
      * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public void onEmergencyNumberListChanged(
             @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
         // default implementation empty
@@ -1043,6 +1069,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
         // default implementation empty
     }
@@ -1068,6 +1095,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
             int subscriptionId) {
         // Default implementation for backwards compatibility
@@ -1086,6 +1114,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
         // default implementation empty
     }
@@ -1158,6 +1187,7 @@
      * @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public void onActiveDataSubscriptionIdChanged(int subId) {
         // default implementation empty
     }
@@ -1178,6 +1208,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void onCallAttributesChanged(@NonNull CallAttributes callAttributes) {
         // default implementation empty
     }
@@ -1199,6 +1230,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void onRadioPowerStateChanged(@RadioPowerState int state) {
         // default implementation empty
     }
@@ -1253,6 +1285,10 @@
      * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead.
      */
     @Deprecated
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
             int domain, int causeCode, int additionalCauseCode) {
         // default implementation empty
@@ -1269,6 +1305,10 @@
      * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead.
      */
     @Deprecated
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public void onBarringInfoChanged(@NonNull BarringInfo barringInfo) {
         // default implementation empty
     }
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 1ab6e0f..1a25c8b 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -218,10 +218,9 @@
      * even in some situations such as the screen of the device is off.
      *
      * @hide
-     * @see AlwaysReportedSignalStrengthListener#onSignalStrengthsChanged
+     * @see TelephonyManager#setSignalStrengthUpdateRequest
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
     public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10;
 
     /**
@@ -231,8 +230,10 @@
      * @see CellInfoListener#onCellInfoChanged
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-    public static final int EVENT_CELL_INFO_CHANGED = 11;
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })    public static final int EVENT_CELL_INFO_CHANGED = 11;
 
     /**
      * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
@@ -369,9 +370,10 @@
 
     /**
      * Event for changes to active data subscription ID. Active data subscription is
-     * the current subscription used to setup Cellular Internet data. For example,
-     * it could be the current active opportunistic subscription in use, or the
-     * subscription user selected as default data subscription in DSDS mode.
+     * the current subscription used to setup Cellular Internet data. The data is only active on the
+     * subscription at a time, even it is multi-SIM mode. For example, it could be the current
+     * active opportunistic subscription in use, or the subscription user selected as default data
+     * subscription in DSDS mode.
      *
      * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
      * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
@@ -668,8 +670,7 @@
          * @see ServiceState#STATE_OUT_OF_SERVICE
          * @see ServiceState#STATE_POWER_OFF
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onServiceStateChanged(@NonNull ServiceState serviceState);
+        void onServiceStateChanged(@NonNull ServiceState serviceState);
     }
 
     /**
@@ -687,7 +688,7 @@
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onMessageWaitingIndicatorChanged(boolean mwi);
+        void onMessageWaitingIndicatorChanged(boolean mwi);
     }
 
     /**
@@ -706,7 +707,7 @@
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onCallForwardingIndicatorChanged(boolean cfi);
+        void onCallForwardingIndicatorChanged(boolean cfi);
     }
 
     /**
@@ -724,7 +725,7 @@
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          */
         @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-        public void onCellLocationChanged(@NonNull CellLocation location);
+        void onCellLocationChanged(@NonNull CellLocation location);
     }
 
     /**
@@ -753,7 +754,7 @@
          * @param state the current call state
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onCallStateChanged(@Annotation.CallState int state);
+        void onCallStateChanged(@Annotation.CallState int state);
     }
 
     /**
@@ -777,9 +778,8 @@
          * @see TelephonyManager#DATA_CONNECTED
          * @see TelephonyManager#DATA_SUSPENDED
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataConnectionStateChanged(@TelephonyManager.DataState int state,
-            @Annotation.NetworkType int networkType);
+        void onDataConnectionStateChanged(@TelephonyManager.DataState int state,
+                @Annotation.NetworkType int networkType);
     }
 
     /**
@@ -802,8 +802,7 @@
          * @see TelephonyManager#DATA_ACTIVITY_INOUT
          * @see TelephonyManager#DATA_ACTIVITY_DORMANT
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataActivity(@Annotation.DataActivityType int direction);
+        void onDataActivity(@Annotation.DataActivityType int direction);
     }
 
     /**
@@ -820,27 +819,7 @@
          * subscription ID. Otherwise, this callback applies to
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
-    }
-
-    /**
-     * Interface for network signal strengths callback which always reported from modem.
-     */
-    public interface AlwaysReportedSignalStrengthListener {
-        /**
-         * Callback always invoked from modem when network signal strengths changes on the
-         * registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers TelephonyCallback by
-         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
-        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
+        void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
     }
 
     /**
@@ -860,8 +839,11 @@
          *
          * @param cellInfo is the list of currently visible cells.
          */
-        @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-        public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo);
+        @RequiresPermission(allOf = {
+                Manifest.permission.READ_PHONE_STATE,
+                Manifest.permission.ACCESS_FINE_LOCATION
+        })
+        void onCellInfoChanged(@NonNull List<CellInfo> cellInfo);
     }
 
     /**
@@ -884,7 +866,7 @@
          * @param callState {@link PreciseCallState}
          */
         @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPreciseCallStateChanged(@NonNull PreciseCallState callState);
+        void onPreciseCallStateChanged(@NonNull PreciseCallState callState);
     }
 
     /**
@@ -905,8 +887,8 @@
          * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
          */
         @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause,
-            @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
+        void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause,
+                @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
     }
 
     /**
@@ -926,7 +908,7 @@
          * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
          */
         @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo);
+        void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo);
     }
 
     /**
@@ -952,7 +934,7 @@
          * @param dataConnectionState {@link PreciseDataConnectionState}
          */
         @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPreciseDataConnectionStateChanged(
+        void onPreciseDataConnectionStateChanged(
             @NonNull PreciseDataConnectionState dataConnectionState);
     }
 
@@ -976,7 +958,7 @@
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          */
         @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onSrvccStateChanged(@Annotation.SrvccState int srvccState);
+        void onSrvccStateChanged(@Annotation.SrvccState int srvccState);
     }
 
     /**
@@ -1000,7 +982,7 @@
          * @param state is the current SIM voice activation state
          */
         @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onVoiceActivationStateChanged(@Annotation.SimActivationState int state);
+        void onVoiceActivationStateChanged(@Annotation.SimActivationState int state);
 
     }
 
@@ -1021,8 +1003,7 @@
          *
          * @param state is the current SIM data activation state
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataActivationStateChanged(@Annotation.SimActivationState int state);
+        void onDataActivationStateChanged(@Annotation.SimActivationState int state);
     }
 
     /**
@@ -1043,8 +1024,7 @@
          * @param enabled indicates whether the current user mobile data state is enabled or
          *                disabled.
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onUserMobileDataStateChanged(boolean enabled);
+        void onUserMobileDataStateChanged(boolean enabled);
     }
 
     /**
@@ -1058,7 +1038,7 @@
          *
          * @param telephonyDisplayInfo The display information.
          */
-        public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
+        void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
     }
 
     /**
@@ -1089,8 +1069,8 @@
          *                            empty.
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onEmergencyNumberListChanged(
-            @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList);
+        void onEmergencyNumberListChanged(@NonNull Map<Integer,
+                List<EmergencyNumber>> emergencyNumberList);
     }
 
     /**
@@ -1118,8 +1098,8 @@
          *                              {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
          */
         @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-        public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
-            int subscriptionId);
+        void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+                int subscriptionId);
     }
 
     /**
@@ -1139,8 +1119,8 @@
          * @param subscriptionId      The subscription ID used to send the emergency sms.
          */
         @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-        public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
-            int subscriptionId);
+        void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+                int subscriptionId);
     }
 
     /**
@@ -1156,8 +1136,7 @@
          *
          * @param capability the new phone capability
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability);
+        void onPhoneCapabilityChanged(@NonNull PhoneCapability capability);
     }
 
     /**
@@ -1168,13 +1147,14 @@
          * Callback invoked when active data subscription ID changes.
          * Note, this callback triggers regardless of registered subscription.
          *
-         * @param subId current subscription used to setup Cellular Internet data.
+         * @param subId current subscription used to setup Cellular Internet data. The data is
+         *              only active on the subscription at a time, even it is multi-SIM mode.
          *              For example, it could be the current active opportunistic subscription
          *              in use, or the subscription user selected as default data subscription in
          *              DSDS mode.
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onActiveDataSubscriptionIdChanged(int subId);
+        void onActiveDataSubscriptionIdChanged(int subId);
     }
 
     /**
@@ -1197,7 +1177,7 @@
          * @param state the modem radio power state
          */
         @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onRadioPowerStateChanged(@Annotation.RadioPowerState int state);
+        void onRadioPowerStateChanged(@Annotation.RadioPowerState int state);
     }
 
     /**
@@ -1221,7 +1201,7 @@
          * @param active If the carrier network change is or shortly will be active,
          *               {@code true} indicate that showing alternative UI, {@code false} otherwise.
          */
-        public void onCarrierNetworkChange(boolean active);
+        void onCarrierNetworkChange(boolean active);
     }
 
     /**
@@ -1263,9 +1243,8 @@
                 Manifest.permission.READ_PRECISE_PHONE_STATE,
                 Manifest.permission.ACCESS_FINE_LOCATION
         })
-        public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
-            @NonNull String chosenPlmn, @NetworkRegistrationInfo.Domain int domain, int causeCode,
-            int additionalCauseCode);
+        void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
+                @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode);
     }
 
     /**
@@ -1303,8 +1282,7 @@
          * long type value}.
          */
         @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        void onAllowedNetworkTypesChanged(
-                @TelephonyManager.AllowedNetworkTypesReason int reason,
+        void onAllowedNetworkTypesChanged(@TelephonyManager.AllowedNetworkTypesReason int reason,
                 @TelephonyManager.NetworkTypeBitMask long allowedNetworkType);
     }
 
@@ -1348,7 +1326,7 @@
                 Manifest.permission.READ_PRECISE_PHONE_STATE,
                 Manifest.permission.ACCESS_FINE_LOCATION
         })
-        public void onBarringInfoChanged(@NonNull BarringInfo barringInfo);
+        void onBarringInfoChanged(@NonNull BarringInfo barringInfo);
     }
 
     /**
@@ -1361,7 +1339,7 @@
          * @param configs List of the current {@link PhysicalChannelConfig}s
          */
         @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs);
+        void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs);
     }
 
     /**
@@ -1379,8 +1357,7 @@
          *                See {@link TelephonyManager.DataEnabledReason}.
          */
         @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onDataEnabledChanged(boolean enabled,
-            @TelephonyManager.DataEnabledReason int reason);
+        void onDataEnabledChanged(boolean enabled, @TelephonyManager.DataEnabledReason int reason);
     }
 
     /**
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 1ec12fe..340fa40 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -894,10 +894,6 @@
             eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED);
         }
 
-        if (telephonyCallback instanceof TelephonyCallback.AlwaysReportedSignalStrengthListener) {
-            eventList.add(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
-        }
-
         if (telephonyCallback instanceof TelephonyCallback.CellInfoListener) {
             eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED);
         }
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index 78c4739..117d75e 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -16,15 +16,9 @@
 
 package android.util;
 
-import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.Formatter;
-import java.util.Locale;
-
 /**
  * API for sending log output to the {@link Log#LOG_ID_SYSTEM} buffer.
  *
@@ -34,12 +28,6 @@
  */
 public final class Slog {
 
-    @GuardedBy("Slog.class")
-    private static StringBuilder sMessageBuilder;
-
-    @GuardedBy("Slog.class")
-    private static Formatter sFormatter;
-
     private Slog() {
     }
 
@@ -53,24 +41,6 @@
                 msg + '\n' + Log.getStackTraceString(tr));
     }
 
-    /**
-     * Logs a {@link Log.VERBOSE} message.
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     *
-     * @deprecated use {@code com.android.server.utils.SLogF} instead.
-     */
-    @Deprecated
-    public static void v(String tag, String format, @Nullable Object... args) {
-        if (!Log.isLoggable(tag, Log.VERBOSE)) return;
-
-        v(tag, getMessage(format, args));
-    }
-
     @UnsupportedAppUsage
     public static int d(String tag, String msg) {
         return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg);
@@ -82,24 +52,6 @@
                 msg + '\n' + Log.getStackTraceString(tr));
     }
 
-    /**
-     * Logs a {@link Log.DEBUG} message.
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     *
-     * @deprecated use {@code com.android.server.utils.SLogF} instead.
-     */
-    @Deprecated
-    public static void d(String tag, String format, @Nullable Object... args) {
-        if (!Log.isLoggable(tag, Log.DEBUG)) return;
-
-        d(tag, getMessage(format, args));
-    }
-
     @UnsupportedAppUsage
     public static int i(String tag, String msg) {
         return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);
@@ -110,24 +62,6 @@
                 msg + '\n' + Log.getStackTraceString(tr));
     }
 
-    /**
-     * Logs a {@link Log.INFO} message.
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     *
-     * @deprecated use {@code com.android.server.utils.SLogF} instead.
-     */
-    @Deprecated
-    public static void i(String tag, String format, @Nullable Object... args) {
-        if (!Log.isLoggable(tag, Log.INFO)) return;
-
-        i(tag, getMessage(format, args));
-    }
-
     @UnsupportedAppUsage
     public static int w(String tag, String msg) {
         return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, msg);
@@ -143,42 +77,6 @@
         return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, Log.getStackTraceString(tr));
     }
 
-    /**
-     * Logs a {@link Log.WARN} message.
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     *
-     * @deprecated use {@code com.android.server.utils.SLogF} instead.
-     */
-    @Deprecated
-    public static void w(String tag, String format, @Nullable Object... args) {
-        if (!Log.isLoggable(tag, Log.WARN)) return;
-
-        w(tag, getMessage(format, args));
-    }
-
-    /**
-     * Logs a {@link Log.WARN} message with an exception
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     *
-     * @deprecated use {@code com.android.server.utils.SLogF} instead.
-     */
-    @Deprecated
-    public static void w(String tag, Exception exception, String format, @Nullable Object... args) {
-        if (!Log.isLoggable(tag, Log.WARN)) return;
-
-        w(tag, getMessage(format, args), exception);
-    }
-
     @UnsupportedAppUsage
     public static int e(String tag, String msg) {
         return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag, msg);
@@ -191,42 +89,6 @@
     }
 
     /**
-     * Logs a {@link Log.ERROR} message.
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     *
-     * @deprecated use {@code com.android.server.utils.SLogF} instead.
-     */
-    @Deprecated
-    public static void e(String tag, String format, @Nullable Object... args) {
-        if (!Log.isLoggable(tag, Log.ERROR)) return;
-
-        e(tag, getMessage(format, args));
-    }
-
-    /**
-     * Logs a {@link Log.ERROR} message with an exception
-     *
-     * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
-     * enabled for the given {@code tag}, but the compiler will still create an intermediate array
-     * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
-     * calling this method in a critical path, make sure to explicitly do the check before calling
-     * it.
-     *
-     * @deprecated use {@code com.android.server.utils.SLogF} instead.
-     */
-    @Deprecated
-    public static void e(String tag, Exception exception, String format, @Nullable Object... args) {
-        if (!Log.isLoggable(tag, Log.ERROR)) return;
-
-        e(tag, getMessage(format, args), exception);
-    }
-
-    /**
      * Like {@link Log#wtf(String, String)}, but will never cause the caller to crash, and
      * will always be handled asynchronously.  Primarily for use by coding running within
      * the system process.
@@ -237,27 +99,6 @@
     }
 
     /**
-     * Logs a {@code wtf} message.
-     *
-     * @deprecated use {@code com.android.server.utils.SLogF} instead.
-     */
-    @Deprecated
-    public static void wtf(String tag, String format, @Nullable Object... args) {
-        wtf(tag, getMessage(format, args));
-    }
-
-    /**
-     * Logs a {@code wtf} message with an exception.
-     *
-     * @deprecated use {@code com.android.server.utils.SLogF} instead.
-     */
-    @Deprecated
-    public static void wtf(String tag, Exception exception, String format,
-            @Nullable Object... args) {
-        wtf(tag, getMessage(format, args), exception);
-    }
-
-    /**
      * Like {@link #wtf(String, String)}, but does not output anything to the log.
      */
     public static void wtfQuiet(String tag, String msg) {
@@ -297,18 +138,4 @@
     public static int println(int priority, String tag, String msg) {
         return Log.println_native(Log.LOG_ID_SYSTEM, priority, tag, msg);
     }
-
-    private static String getMessage(String format, @Nullable Object... args) {
-        synchronized (Slog.class) {
-            if (sMessageBuilder == null) {
-                // Lazy load so they're not created if not used by the process
-                sMessageBuilder = new StringBuilder();
-                sFormatter = new Formatter(sMessageBuilder, Locale.ENGLISH);
-            }
-            sFormatter.format(format, args);
-            String message = sMessageBuilder.toString();
-            sMessageBuilder.setLength(0);
-            return message;
-        }
-    }
 }
diff --git a/core/java/android/uwb/DistanceMeasurement.java b/core/java/android/uwb/DistanceMeasurement.java
index 2a9bbdf..9856553 100644
--- a/core/java/android/uwb/DistanceMeasurement.java
+++ b/core/java/android/uwb/DistanceMeasurement.java
@@ -60,6 +60,7 @@
      *
      * @return error of distance measurement in meters
      */
+    @FloatRange(from = 0.0)
     public double getErrorMeters() {
         return mErrorMeters;
     }
@@ -162,7 +163,7 @@
          * @throws IllegalArgumentException if error is negative or NaN
          */
         @NonNull
-        public Builder setErrorMeters(double errorMeters) {
+        public Builder setErrorMeters(@FloatRange(from = 0.0) double errorMeters) {
             if (Double.isNaN(errorMeters) || errorMeters < 0.0) {
                 throw new IllegalArgumentException(
                         "errorMeters must be >= 0.0 and not NaN: " + errorMeters);
@@ -178,7 +179,8 @@
          * @throws IllegalArgumentException if confidence level is not in the range of [0.0, 1.0]
          */
         @NonNull
-        public Builder setConfidenceLevel(double confidenceLevel) {
+        public Builder setConfidenceLevel(
+                @FloatRange(from = 0.0, to = 1.0) double confidenceLevel) {
             if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
                 throw new IllegalArgumentException(
                         "confidenceLevel must be in the range [0.0, 1.0]: " + confidenceLevel);
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index 4036892..30da248 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -160,14 +160,4 @@
    * closed.
    */
   const int RANGING_SESSION_CLOSE_THRESHOLD_MS = 3000; // Value TBD
-
-  /**
-   * Ranging scheduling time unit (RSTU) for High Rate Pulse (HRP) PHY
-   */
-  const int HIGH_RATE_PULSE_CHIRPS_PER_RSTU = 416;
-
-  /**
-   * Ranging scheduling time unit (RSTU) for Low Rate Pulse (LRP) PHY
-   */
-  const int LOW_RATE_PULSE_CHIRPS_PER_RSTU = 1;
 }
diff --git a/core/java/android/uwb/IUwbRangingCallbacks.aidl b/core/java/android/uwb/IUwbRangingCallbacks.aidl
index f71f3ff..f15debb 100644
--- a/core/java/android/uwb/IUwbRangingCallbacks.aidl
+++ b/core/java/android/uwb/IUwbRangingCallbacks.aidl
@@ -92,9 +92,13 @@
    * Called when the ranging session has been stopped
    *
    * @param sessionHandle the session the callback is being invoked for
+   * @param reason the reason the session was stopped
+   * @param parameters protocol specific parameters
    */
 
-  void onRangingStopped(in SessionHandle sessionHandle);
+  void onRangingStopped(in SessionHandle sessionHandle,
+                        RangingChangeReason reason,
+                        in PersistableBundle parameters);
 
   /**
    * Called when a ranging session fails to stop
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index 5c7f0f5..ff8b912 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -171,7 +171,8 @@
     }
 
     @Override
-    public void onRangingStopped(SessionHandle sessionHandle) {
+    public void onRangingStopped(SessionHandle sessionHandle, @RangingChangeReason int reason,
+            PersistableBundle params) {
         synchronized (this) {
             if (!hasSession(sessionHandle)) {
                 Log.w(TAG, "onRangingStopped - received unexpected SessionHandle: "
@@ -180,7 +181,7 @@
             }
 
             RangingSession session = mRangingSessionTable.get(sessionHandle);
-            session.onRangingStopped();
+            session.onRangingStopped(convertToReason(reason), params);
         }
     }
 
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
index 52ec5bd..345b69d 100644
--- a/core/java/android/uwb/RangingSession.java
+++ b/core/java/android/uwb/RangingSession.java
@@ -191,8 +191,11 @@
 
         /**
          * Invoked when a request to stop the session succeeds
+         *
+         * @param reason reason for the session stop
+         * @param parameters protocol specific parameters related to the stop reason
          */
-        void onStopped();
+        void onStopped(@Reason int reason, @NonNull PersistableBundle parameters);
 
         /**
          * Invoked when a request to stop the session fails
@@ -434,14 +437,15 @@
     /**
      * @hide
      */
-    public void onRangingStopped() {
+    public void onRangingStopped(@Callback.Reason int reason,
+            @NonNull PersistableBundle params) {
         if (mState == State.CLOSED) {
             Log.w(TAG, "onRangingStopped invoked for a closed session");
             return;
         }
 
         mState = State.IDLE;
-        executeCallback(() -> mCallback.onStopped());
+        executeCallback(() -> mCallback.onStopped(reason, params));
     }
 
     /**
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 36be9f8..8e5f905 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -377,7 +377,7 @@
                 && ownerUid == other.ownerUid
                 && Objects.equals(ownerPackageName, other.ownerPackageName)
                 && removeMode == other.removeMode
-                && refreshRateOverride == other.refreshRateOverride
+                && getRefreshRate() == other.getRefreshRate()
                 && brightnessMinimum == other.brightnessMinimum
                 && brightnessMaximum == other.brightnessMaximum
                 && brightnessDefault == other.brightnessDefault
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c001ec9..c201e3b 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1131,7 +1131,7 @@
                 continue;
             }
             final InsetsSourceControl control = consumer.getControl();
-            if (control != null) {
+            if (control != null && control.getLeash() != null) {
                 controls.put(consumer.getType(), new InsetsSourceControl(control));
                 typesReady |= toPublicType(consumer.getType());
             } else if (animationType == ANIMATION_TYPE_SHOW) {
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index bc50dbe..8e50fed 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -33,6 +33,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.util.Log;
 import android.util.imetracing.ImeTracing;
@@ -240,10 +241,6 @@
         mHasWindowFocus = false;
     }
 
-    boolean hasWindowFocus() {
-        return mHasWindowFocus;
-    }
-
     boolean hasViewFocusWhenWindowFocusGain() {
         return mHasViewFocusWhenWindowFocusGain;
     }
@@ -366,7 +363,16 @@
     protected void setRequestedVisible(boolean requestedVisible) {
         if (mRequestedVisible != requestedVisible) {
             mRequestedVisible = requestedVisible;
-            mIsAnimationPending = false;
+
+            // We need an animation later if the leash of a real control (which has an insets hint)
+            // is not ready. The !mIsAnimationPending check is in case that the requested visibility
+            // is changed twice before playing the animation -- we don't need an animation in this
+            // case.
+            mIsAnimationPending = !mIsAnimationPending
+                    && mSourceControl != null
+                    && mSourceControl.getLeash() == null
+                    && !Insets.NONE.equals(mSourceControl.getInsetsHint());
+
             mController.onRequestedVisibilityChanged(this);
             if (DEBUG) Log.d(TAG, "setRequestedVisible: " + requestedVisible);
         }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 82106b0..2b96a14 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -33,7 +33,6 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.RenderNode;
@@ -670,10 +669,16 @@
             if (mClipSurfaceToBounds && mClipBounds != null) {
                 mTmpRect.intersect(mClipBounds);
             }
-            canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
-                    mCornerRadius, mCornerRadius, mRoundedViewportPaint);
+            canvas.punchHole(
+                    mTmpRect.left,
+                    mTmpRect.top,
+                    mTmpRect.right,
+                    mTmpRect.bottom,
+                    mCornerRadius,
+                    mCornerRadius
+            );
         } else {
-            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+            canvas.punchHole(0f, 0f, getWidth(), getHeight(), 0f, 0f);
         }
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index ab46170..1c15bbc 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -627,7 +627,7 @@
 
     /**
      * Integer argument specifying the end index of the requested text location data. Must be
-     * positive.
+     * positive and no larger than {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}.
      *
      * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
      */
@@ -635,6 +635,11 @@
             "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
 
     /**
+     * The maximum allowed length of the requested text location data.
+     */
+    public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000;
+
+    /**
      * Key used to request extra data for the rendering information.
      * The key requests that a {@link AccessibilityNodeInfo.ExtraRenderingInfo} be added to this
      * info. This request is made with {@link #refreshWithExtraData(String, Bundle)} without
@@ -1038,6 +1043,14 @@
      * recycled).
      */
     public boolean refreshWithExtraData(String extraDataKey, Bundle args) {
+        // limits the text location length to make sure the rectangle array allocation avoids
+        // the binder transaction failure and OOM crash.
+        if (args.getInt(EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH, -1)
+                > EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH) {
+            args.putInt(EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH,
+                    EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH);
+        }
+
         args.putString(EXTRA_DATA_REQUESTED_KEY, extraDataKey);
         return refresh(args, true);
     }
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
index d0ab004..1cb6825 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
@@ -60,4 +60,11 @@
      */
     void onPerformScaleAction(int displayId, float scale);
 
+    /**
+     * Called when the accessibility action is performed.
+     *
+     * @param displayId The logical display id.
+     */
+    void onAccessibilityActionPerformed(int displayId);
+
 }
diff --git a/core/java/android/view/translation/TranslationResponse.java b/core/java/android/view/translation/TranslationResponse.java
index 35040f1..f9b7012 100644
--- a/core/java/android/view/translation/TranslationResponse.java
+++ b/core/java/android/view/translation/TranslationResponse.java
@@ -78,6 +78,13 @@
     abstract static class BaseBuilder {
 
         /**
+         * @deprecated Use {@link Builder#Builder(int)}.
+         * @hide
+         */
+        @Deprecated
+        public abstract Builder setTranslationStatus(@TranslationStatus int value);
+
+        /**
          * Adds {@link TranslationResponseValue} to be translated. The input
          * TranslationResponseValue format should match those provided by the
          * {@link android.view.translation.Translator}'s destSpec.
@@ -137,7 +144,7 @@
 
 
 
-    // Code below generated by codegen v1.0.22.
+    // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -362,6 +369,8 @@
          * The translation result status code.
          */
         @DataClass.Generated.Member
+        @Override
+        @Deprecated
         public @NonNull Builder setTranslationStatus(@TranslationStatus int value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x1;
@@ -438,10 +447,10 @@
     }
 
     @DataClass.Generated(
-            time = 1616189850232L,
-            codegenVersion = "1.0.22",
+            time = 1617218030666L,
+            codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/translation/TranslationResponse.java",
-            inputSignatures = "public static final  int TRANSLATION_STATUS_SUCCESS\npublic static final  int TRANSLATION_STATUS_UNKNOWN_ERROR\npublic static final  int TRANSLATION_STATUS_CONTEXT_UNSUPPORTED\nprivate final @android.view.translation.TranslationResponse.TranslationStatus int mTranslationStatus\nprivate final @android.annotation.NonNull android.util.SparseArray<android.view.translation.TranslationResponseValue> mTranslationResponseValues\nprivate final @android.annotation.NonNull android.util.SparseArray<android.view.translation.ViewTranslationResponse> mViewTranslationResponses\nprivate final  boolean mFinalResponse\nprivate static  android.util.SparseArray<android.view.translation.TranslationResponseValue> defaultTranslationResponseValues()\nprivate static  android.util.SparseArray<android.view.translation.ViewTranslationResponse> defaultViewTranslationResponses()\nprivate static  boolean defaultFinalResponse()\nclass TranslationResponse extends java.lang.Object implements [android.os.Parcelable]\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int,android.view.translation.TranslationResponseValue)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int,android.view.translation.ViewTranslationResponse)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genHiddenConstDefs=true)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int,android.view.translation.TranslationResponseValue)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int,android.view.translation.ViewTranslationResponse)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int TRANSLATION_STATUS_SUCCESS\npublic static final  int TRANSLATION_STATUS_UNKNOWN_ERROR\npublic static final  int TRANSLATION_STATUS_CONTEXT_UNSUPPORTED\nprivate final @android.view.translation.TranslationResponse.TranslationStatus int mTranslationStatus\nprivate final @android.annotation.NonNull android.util.SparseArray<android.view.translation.TranslationResponseValue> mTranslationResponseValues\nprivate final @android.annotation.NonNull android.util.SparseArray<android.view.translation.ViewTranslationResponse> mViewTranslationResponses\nprivate final  boolean mFinalResponse\nprivate static  android.util.SparseArray<android.view.translation.TranslationResponseValue> defaultTranslationResponseValues()\nprivate static  android.util.SparseArray<android.view.translation.ViewTranslationResponse> defaultViewTranslationResponses()\nprivate static  boolean defaultFinalResponse()\nclass TranslationResponse extends java.lang.Object implements [android.os.Parcelable]\npublic abstract @java.lang.Deprecated android.view.translation.TranslationResponse.Builder setTranslationStatus(int)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int,android.view.translation.TranslationResponseValue)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int,android.view.translation.ViewTranslationResponse)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genHiddenConstDefs=true)\npublic abstract @java.lang.Deprecated android.view.translation.TranslationResponse.Builder setTranslationStatus(int)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int,android.view.translation.TranslationResponseValue)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int,android.view.translation.ViewTranslationResponse)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 29c78b5..f49aa74 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -3263,16 +3263,14 @@
 
     /**
      * Create a new RemoteViews object that will display the views contained
-     * in the specified layout file.
+     * in the specified layout file and change the id of the root view to the specified one.
      *
-     * @param packageName Name of the package that contains the layout resource.
-     * @param userId The user under which the package is running.
-     * @param layoutId The id of the layout resource.
-     *
-     * @hide
+     * @param packageName Name of the package that contains the layout resource
+     * @param layoutId The id of the layout resource
      */
-    public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) {
-        this(getApplicationInfo(packageName, userId), layoutId);
+    public RemoteViews(@NonNull String packageName, @LayoutRes int layoutId, @IdRes int viewId) {
+        this(packageName, layoutId);
+        this.mViewId = viewId;
     }
 
     /**
@@ -3727,7 +3725,7 @@
      * The {@code stableId} will be used to identify a potential view to recycled when the remote
      * view is inflated. Views can be re-used if inserted in the same order, potentially with
      * some views appearing / disappearing. To be recycled the view must not change the layout
-     * used to inflate it or its view id (see {@link RemoteViews#setViewId}).
+     * used to inflate it or its view id (see {@link RemoteViews#RemoteViews(String, int, int)}).
      *
      * Note: if a view is re-used, all the actions will be re-applied on it. However, its properties
      * are not reset, so what was applied in previous round will have an effect. As a view may be
@@ -6340,25 +6338,9 @@
     }
 
     /**
-     * Set the ID of the top-level view of the XML layout.
-     *
-     * The view's ID is changed right after inflation, before it gets added to its parent. The ID
-     * of a given view can never change after the initial inflation.
-     *
-     * Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
-     *
-     * @throws UnsupportedOperationException if the method is called on a RemoteViews defined in
-     * term of other RemoteViews (e.g. {@link #RemoteViews(RemoteViews, RemoteViews)}).
+     * Get the ID of the top-level view of the XML layout, if set using
+     * {@link RemoteViews#RemoteViews(String, int, int)}.
      */
-    public void setViewId(@IdRes int viewId) {
-        if (hasMultipleLayouts()) {
-            throw new UnsupportedOperationException(
-                    "The viewId can only be set on RemoteViews defined from a XML layout.");
-        }
-        mViewId = viewId;
-    }
-
-    /** Get the ID of the top-level view of the XML layout, as set by {@link #setViewId}. */
     public @IdRes int getViewId() {
         return mViewId;
     }
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index fffeb02..5a5e745 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -116,7 +116,6 @@
     KeyphraseMetadata getEnrolledKeyphraseMetadata(String keyphrase, String bcp47Locale);
     /**
      * @return the component name for the currently active voice interaction service
-     * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
      */
     ComponentName getActiveServiceComponentName();
 
diff --git a/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java b/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java
index 7bd7acf..1fc126e 100644
--- a/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java
+++ b/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java
@@ -24,7 +24,9 @@
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.graphics.Xfermode;
 import android.graphics.drawable.Drawable;
 import android.view.animation.DecelerateInterpolator;
@@ -44,6 +46,9 @@
     private int mMainColor;
     private ValueAnimator mColorAnimation;
     private int mMainColorTo;
+    private float mCornerRadius;
+    private Rect mBounds;
+    private ConcaveInfo mConcaveInfo;
 
     public ScrimDrawable() {
         mPaint = new Paint();
@@ -127,15 +132,67 @@
         return PixelFormat.TRANSLUCENT;
     }
 
+    /**
+     * Enable drawable shape to have rounded corners with provided radius
+     */
+    public void setRoundedCorners(float radius) {
+        mCornerRadius = radius;
+    }
+
+    /**
+     * Make bottom edge concave with provided corner radius
+     */
+    public void setBottomEdgeConcave(float radius) {
+        // only rounding top corners for clip out path
+        float[] cornerRadii = new float[]{radius, radius, radius, radius, 0, 0, 0, 0};
+        mConcaveInfo = new ConcaveInfo(radius, cornerRadii);
+    }
+
     @Override
     public void draw(@NonNull Canvas canvas) {
         mPaint.setColor(mMainColor);
         mPaint.setAlpha(mAlpha);
-        canvas.drawRect(getBounds(), mPaint);
+        if (mConcaveInfo != null) {
+            drawConcave(canvas);
+        }
+        canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right,
+                getBounds().bottom + mCornerRadius,
+                /* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint);
+    }
+
+    private void drawConcave(Canvas canvas) {
+        // checking if width of clip out path needs to change
+        if (mBounds == null
+                || getBounds().right != mBounds.right
+                || getBounds().left != mBounds.left) {
+            mConcaveInfo.mPath.reset();
+            float left = getBounds().left;
+            float right = getBounds().right;
+            float top = 0f;
+            float bottom = mConcaveInfo.mPathOverlap;
+            mConcaveInfo.mPath.addRoundRect(left, top, right, bottom,
+                    mConcaveInfo.mCornerRadii, Path.Direction.CW);
+        }
+        mBounds = getBounds();
+        int translation = (int) (mBounds.bottom - mConcaveInfo.mPathOverlap);
+        canvas.translate(0, translation);
+        canvas.clipOutPath(mConcaveInfo.mPath);
+        canvas.translate(0, -translation);
     }
 
     @VisibleForTesting
     public int getMainColor() {
         return mMainColor;
     }
+
+    private static class ConcaveInfo {
+        private final float mPathOverlap;
+        private final float[] mCornerRadii;
+        private final Path mPath = new Path();
+
+        ConcaveInfo(float pathOverlap, float[] cornerRadii) {
+            mPathOverlap = pathOverlap;
+            mCornerRadii = cornerRadii;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 98c75b9..1000914 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -18,6 +18,7 @@
 
 import android.annotation.AnyThread;
 import android.annotation.DrawableRes;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.Uri;
 import android.os.IBinder;
@@ -28,6 +29,8 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.Objects;
+
 /**
  * A utility class to take care of boilerplate code around IPCs.
  */
@@ -47,7 +50,7 @@
          * @param privOps Binder interface to be set
          */
         @AnyThread
-        public synchronized void set(IInputMethodPrivilegedOperations privOps) {
+        public synchronized void set(@NonNull IInputMethodPrivilegedOperations privOps) {
             if (mPrivOps != null) {
                 throw new IllegalStateException(
                         "IInputMethodPrivilegedOperations must be set at most once."
@@ -90,7 +93,8 @@
      * @param privOps Binder interface to be set
      */
     @AnyThread
-    public void set(IInputMethodPrivilegedOperations privOps) {
+    public void set(@NonNull IInputMethodPrivilegedOperations privOps) {
+        Objects.requireNonNull(privOps, "privOps must not be null");
         mOps.set(privOps);
     }
 
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 594a1a7..135c076 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -27,6 +27,7 @@
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED;
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN;
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL;
+import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_END;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -215,7 +216,7 @@
             mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
         }
         if (mListener != null) {
-            mListener.onNotifyCujEvents(mSession, ACTION_SESSION_BEGIN);
+            mListener.onCujEvents(mSession, ACTION_SESSION_BEGIN);
         }
     }
 
@@ -240,7 +241,9 @@
             }
             Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
             mSession.setReason(reason);
-            InteractionJankMonitor.getInstance().removeTimeout(mSession.getCuj());
+            if (mListener != null) {
+                mListener.onCujEvents(mSession, ACTION_SESSION_END);
+            }
         }
         // We don't remove observer here,
         // will remove it when all the frame metrics in this duration are called back.
@@ -269,7 +272,7 @@
         // Notify the listener the session has been cancelled.
         // We don't notify the listeners if the session never begun.
         if (mListener != null) {
-            mListener.onNotifyCujEvents(mSession, ACTION_SESSION_CANCEL);
+            mListener.onCujEvents(mSession, ACTION_SESSION_CANCEL);
         }
     }
 
@@ -445,7 +448,7 @@
                     maxFrameTimeNanos,
                     missedSfFramesCounts);
             if (mListener != null) {
-                mListener.onNotifyCujEvents(mSession, ACTION_METRICS_LOGGED);
+                mListener.onCujEvents(mSession, ACTION_METRICS_LOGGED);
             }
         }
         if (DEBUG) {
@@ -587,6 +590,6 @@
          * @param session the CUJ session
          * @param action the specific action
          */
-        void onNotifyCujEvents(Session session, String action);
+        void onCujEvents(Session session, String action);
     }
 }
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 6c56d42..5ab2a82 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -19,7 +19,9 @@
 import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
 
 import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
+import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL;
 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NOT_BEGUN;
+import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
 import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
@@ -100,6 +102,7 @@
     private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
 
     public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN";
+    public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
     public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
     public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED";
     public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME";
@@ -275,10 +278,7 @@
     public FrameTracker createFrameTracker(View v, Session session) {
         final Context c = v.getContext().getApplicationContext();
         synchronized (this) {
-            boolean needListener = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false);
-            FrameTrackerListener eventsListener =
-                    !needListener ? null : (s, act) -> notifyEvents(c, act, s);
-
+            FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(c, act, s);
             return new FrameTracker(session, mWorker.getThreadHandler(),
                     new ThreadedRendererWrapper(v.getThreadedRenderer()),
                     new ViewRootWrapper(v.getViewRootImpl()), new SurfaceControlWrapper(),
@@ -287,6 +287,28 @@
         }
     }
 
+    private void handleCujEvents(Context context, String action, Session session) {
+        // Clear the running and timeout tasks if the end / cancel was fired within the tracker.
+        // Or we might have memory leaks.
+        if (needRemoveTasks(action, session)) {
+            removeTimeout(session.getCuj());
+            removeTracker(session.getCuj());
+        }
+
+        // Notify the receivers if necessary.
+        if (session.shouldNotify()) {
+            notifyEvents(context, action, session);
+        }
+    }
+
+    private boolean needRemoveTasks(String action, Session session) {
+        final boolean badEnd = action.equals(ACTION_SESSION_END)
+                && session.getReason() != REASON_END_NORMAL;
+        final boolean badCancel = action.equals(ACTION_SESSION_CANCEL)
+                && session.getReason() != REASON_CANCEL_NORMAL;
+        return badEnd || badCancel;
+    }
+
     private void notifyEvents(Context context, String action, Session session) {
         if (action.equals(ACTION_SESSION_CANCEL)
                 && session.getReason() == REASON_CANCEL_NOT_BEGUN) {
@@ -299,7 +321,7 @@
         context.sendBroadcast(intent);
     }
 
-    void removeTimeout(@CujType int cujType) {
+    private void removeTimeout(@CujType int cujType) {
         synchronized (this) {
             Runnable timeout = mTimeoutActions.get(cujType);
             if (timeout != null) {
@@ -371,7 +393,7 @@
             // Skip this call since we haven't started a trace yet.
             if (tracker == null) return false;
             tracker.end(FrameTracker.REASON_END_NORMAL);
-            mRunningTrackers.remove(cujType);
+            removeTracker(cujType);
             return true;
         }
     }
@@ -390,7 +412,7 @@
             // Skip this call since we haven't started a trace yet.
             if (tracker == null) return false;
             tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
-            mRunningTrackers.remove(cujType);
+            removeTracker(cujType);
             return true;
         }
     }
@@ -401,6 +423,12 @@
         }
     }
 
+    private void removeTracker(@CujType int cuj) {
+        synchronized (this) {
+            mRunningTrackers.remove(cuj);
+        }
+    }
+
     private void updateProperties(DeviceConfig.Properties properties) {
         synchronized (this) {
             mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
@@ -516,9 +544,11 @@
         private long mTimeStamp;
         @FrameTracker.Reasons
         private int mReason = FrameTracker.REASON_END_UNKNOWN;
+        private boolean mShouldNotify;
 
         public Session(@CujType int cujType) {
             mCujType = cujType;
+            mShouldNotify = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false);
         }
 
         @CujType
@@ -558,5 +588,10 @@
         public int getReason() {
             return mReason;
         }
+
+        /** Determine if should notify the receivers of cuj events */
+        public boolean shouldNotify() {
+            return mShouldNotify;
+        }
     }
 }
diff --git a/core/java/com/android/internal/os/BINDER_OWNERS b/core/java/com/android/internal/os/BINDER_OWNERS
new file mode 100644
index 0000000..9f68a32
--- /dev/null
+++ b/core/java/com/android/internal/os/BINDER_OWNERS
@@ -0,0 +1,2 @@
+dplotnikov@google.com
+gaillard@google.com
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 3f01ebb..ea3b3a7 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -1,6 +1,7 @@
 per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
 per-file *Zygote* = file:/ZYGOTE_OWNERS
 per-file *Cpu* = file:CPU_OWNERS
+per-file *Binder* = file:BINDER_OWNERS
 
 # BatteryStats
 per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 654b461..d16d9c6 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -95,4 +95,5 @@
     boolean hasSecureLockScreen();
     boolean tryUnlockWithCachedUnifiedChallenge(int userId);
     void removeCachedUnifiedChallenge(int userId);
+    void updateEncryptionPassword(int type, in byte[] password);
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a161f18..a0e50be 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -34,7 +34,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.UserInfo;
-import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -59,18 +58,13 @@
 
 import com.google.android.collect.Lists;
 
-import libcore.util.HexEncoding;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
-import java.util.StringJoiner;
 
 /**
  * Utilities for the lock pattern and its settings.
@@ -186,7 +180,7 @@
     public static final String SYNTHETIC_PASSWORD_HANDLE_KEY = "sp-handle";
     public static final String SYNTHETIC_PASSWORD_ENABLED_KEY = "enable-sp";
     public static final int SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT = 1;
-    private static final String HISTORY_DELIMITER = ",";
+    public static final String PASSWORD_HISTORY_DELIMITER = ",";
 
     @UnsupportedAppUsage
     private final Context mContext;
@@ -559,9 +553,11 @@
         if(passwordHistoryLength == 0) {
             return false;
         }
-        String legacyHash = legacyPasswordToHash(passwordToCheck, userId);
-        String passwordHash = passwordToHistoryHash(passwordToCheck, hashFactor, userId);
-        String[] history = passwordHistory.split(HISTORY_DELIMITER);
+        byte[] salt = getSalt(userId).getBytes();
+        String legacyHash = LockscreenCredential.legacyPasswordToHash(passwordToCheck, salt);
+        String passwordHash = LockscreenCredential.passwordToHistoryHash(
+                passwordToCheck, salt, hashFactor);
+        String[] history = passwordHistory.split(PASSWORD_HISTORY_DELIMITER);
         // Password History may be too long...
         for (int i = 0; i < Math.min(passwordHistoryLength, history.length); i++) {
             if (history[i].equals(legacyHash) || history[i].equals(passwordHash)) {
@@ -701,20 +697,9 @@
         } catch (RemoteException e) {
             throw new RuntimeException("Unable to save lock password", e);
         }
-
-        onPostPasswordChanged(newCredential, userHandle);
         return true;
     }
 
-    private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
-        updateEncryptionPasswordIfNeeded(newCredential, userHandle);
-        if (newCredential.isPattern()) {
-            reportPatternWasChosen(userHandle);
-        }
-        updatePasswordHistory(newCredential, userHandle);
-        reportEnabledTrustAgentsChanged(userHandle);
-    }
-
     private void updateCryptoUserInfo(int userId) {
         if (userId != UserHandle.USER_SYSTEM) {
             return;
@@ -781,100 +766,6 @@
         return getDeviceOwnerInfo() != null;
     }
 
-    /** Update the encryption password if it is enabled **/
-    private void updateEncryptionPassword(final int type, final byte[] password) {
-        if (!hasSecureLockScreen() && password != null && password.length != 0) {
-            throw new UnsupportedOperationException(
-                    "This operation requires the lock screen feature.");
-        }
-        if (!isDeviceEncryptionEnabled()) {
-            return;
-        }
-        final IBinder service = ServiceManager.getService("mount");
-        if (service == null) {
-            Log.e(TAG, "Could not find the mount service to update the encryption password");
-            return;
-        }
-
-        // TODO(b/120484642): This is a location where we still use a String for vold
-        String passwordString = password != null ? new String(password) : null;
-        new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... dummy) {
-                IStorageManager storageManager = IStorageManager.Stub.asInterface(service);
-                try {
-                    storageManager.changeEncryptionPassword(type, passwordString);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Error changing encryption password", e);
-                }
-                return null;
-            }
-        }.execute();
-    }
-
-    /**
-     * Update device encryption password if calling user is USER_SYSTEM and device supports
-     * encryption.
-     */
-    private void updateEncryptionPasswordIfNeeded(LockscreenCredential credential, int userHandle) {
-        // Update the device encryption password.
-        if (userHandle != UserHandle.USER_SYSTEM || !isDeviceEncryptionEnabled()) {
-            return;
-        }
-        if (!shouldEncryptWithCredentials(true)) {
-            updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
-            return;
-        }
-        if (credential.isNone()) {
-            // Set the encryption password to default.
-            setCredentialRequiredToDecrypt(false);
-        }
-        updateEncryptionPassword(credential.getStorageCryptType(), credential.getCredential());
-    }
-
-    /**
-     * Store the hash of the *current* password in the password history list, if device policy
-     * enforces password history requirement.
-     */
-    private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
-        if (password.isNone()) {
-            return;
-        }
-        if (password.isPattern()) {
-            // Do not keep track of historical patterns
-            return;
-        }
-        // Add the password to the password history. We assume all
-        // password hashes have the same length for simplicity of implementation.
-        String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
-        if (passwordHistory == null) {
-            passwordHistory = "";
-        }
-        int passwordHistoryLength = getRequestedPasswordHistoryLength(userHandle);
-        if (passwordHistoryLength == 0) {
-            passwordHistory = "";
-        } else {
-            final byte[] hashFactor = getPasswordHistoryHashFactor(password, userHandle);
-            String hash = passwordToHistoryHash(password.getCredential(), hashFactor, userHandle);
-            if (hash == null) {
-                Log.e(TAG, "Compute new style password hash failed, fallback to legacy style");
-                hash = legacyPasswordToHash(password.getCredential(), userHandle);
-            }
-            if (TextUtils.isEmpty(passwordHistory)) {
-                passwordHistory = hash;
-            } else {
-                String[] history = passwordHistory.split(HISTORY_DELIMITER);
-                StringJoiner joiner = new StringJoiner(HISTORY_DELIMITER);
-                joiner.add(hash);
-                for (int i = 0; i < passwordHistoryLength - 1 && i < history.length; i++) {
-                    joiner.add(history[i]);
-                }
-                passwordHistory = joiner.toString();
-            }
-        }
-        setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
-    }
-
     /**
      * Determine if the device supports encryption, even if it's set to default. This
      * differs from isDeviceEncrypted() in that it returns true even if the device is
@@ -898,7 +789,11 @@
      * Clears the encryption password.
      */
     public void clearEncryptionPassword() {
-        updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
+        try {
+            getLockSettings().updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't clear encryption password");
+        }
     }
 
     /**
@@ -1045,56 +940,9 @@
      * @param password the gesture pattern.
      *
      * @return the hash of the pattern in a byte array.
-     * TODO: move to LockscreenCredential class
      */
     public String legacyPasswordToHash(byte[] password, int userId) {
-        if (password == null || password.length == 0) {
-            return null;
-        }
-
-        try {
-            // Previously the password was passed as a String with the following code:
-            // byte[] saltedPassword = (password + getSalt(userId)).getBytes();
-            // The code below creates the identical digest preimage using byte arrays:
-            byte[] salt = getSalt(userId).getBytes();
-            byte[] saltedPassword = Arrays.copyOf(password, password.length + salt.length);
-            System.arraycopy(salt, 0, saltedPassword, password.length, salt.length);
-            byte[] sha1 = MessageDigest.getInstance("SHA-1").digest(saltedPassword);
-            byte[] md5 = MessageDigest.getInstance("MD5").digest(saltedPassword);
-
-            byte[] combined = new byte[sha1.length + md5.length];
-            System.arraycopy(sha1, 0, combined, 0, sha1.length);
-            System.arraycopy(md5, 0, combined, sha1.length, md5.length);
-
-            final char[] hexEncoded = HexEncoding.encode(combined);
-            Arrays.fill(saltedPassword, (byte) 0);
-            return new String(hexEncoded);
-        } catch (NoSuchAlgorithmException e) {
-            throw new AssertionError("Missing digest algorithm: ", e);
-        }
-    }
-
-    /**
-     * Hash the password for password history check purpose.
-     * TODO: move to LockscreenCredential class
-     */
-    private String passwordToHistoryHash(byte[] passwordToHash, byte[] hashFactor, int userId) {
-        if (passwordToHash == null || passwordToHash.length == 0 || hashFactor == null) {
-            return null;
-        }
-        try {
-            MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
-            sha256.update(hashFactor);
-            byte[] salt = getSalt(userId).getBytes();
-            byte[] saltedPassword = Arrays.copyOf(passwordToHash, passwordToHash.length
-                    + salt.length);
-            System.arraycopy(salt, 0, saltedPassword, passwordToHash.length, salt.length);
-            sha256.update(saltedPassword);
-            Arrays.fill(saltedPassword, (byte) 0);
-            return new String(HexEncoding.encode(sha256.digest()));
-        } catch (NoSuchAlgorithmException e) {
-            throw new AssertionError("Missing digest algorithm: ", e);
-        }
+        return LockscreenCredential.legacyPasswordToHash(password, getSalt(userId).getBytes());
     }
 
     /**
@@ -1396,14 +1244,6 @@
         }
     }
 
-    private boolean isDoNotAskCredentialsOnBootSet() {
-        return getDevicePolicyManager().getDoNotAskCredentialsOnBoot();
-    }
-
-    private boolean shouldEncryptWithCredentials(boolean defaultValue) {
-        return isCredentialRequiredToDecrypt(defaultValue) && !isDoNotAskCredentialsOnBootSet();
-    }
-
     private void throwIfCalledOnMainThread() {
         if (Looper.getMainLooper().isCurrentThread()) {
             throw new IllegalStateException("should not be called from the main thread.");
@@ -1590,12 +1430,7 @@
         credential.checkLength();
         LockSettingsInternal localService = getLockSettingsInternal();
 
-        if (!localService.setLockCredentialWithToken(credential, tokenHandle, token, userHandle)) {
-            return false;
-        }
-
-        onPostPasswordChanged(credential, userHandle);
-        return true;
+        return localService.setLockCredentialWithToken(credential, tokenHandle, token, userHandle);
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index a488449..361ba95 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -31,6 +31,10 @@
 
 import com.android.internal.util.Preconditions;
 
+import libcore.util.HexEncoding;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
@@ -276,6 +280,82 @@
         return getType() == storedCredentialType;
     }
 
+    /**
+     * Hash the password for password history check purpose.
+     */
+    public String passwordToHistoryHash(byte[] salt, byte[] hashFactor) {
+        return passwordToHistoryHash(mCredential, salt, hashFactor);
+    }
+
+    /**
+     * Hash the password for password history check purpose.
+     */
+    public static String passwordToHistoryHash(
+            byte[] passwordToHash, byte[] salt, byte[] hashFactor) {
+        if (passwordToHash == null || passwordToHash.length == 0
+                || hashFactor == null || salt == null) {
+            return null;
+        }
+        try {
+            MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+            sha256.update(hashFactor);
+            byte[] saltedPassword = Arrays.copyOf(passwordToHash, passwordToHash.length
+                    + salt.length);
+            System.arraycopy(salt, 0, saltedPassword, passwordToHash.length, salt.length);
+            sha256.update(saltedPassword);
+            Arrays.fill(saltedPassword, (byte) 0);
+            return new String(HexEncoding.encode(sha256.digest()));
+        } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError("Missing digest algorithm: ", e);
+        }
+    }
+
+    /**
+     * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
+     * Not the most secure, but it is at least a second level of protection. First level is that
+     * the file is in a location only readable by the system process.
+     *
+     * @return the hash of the pattern in a byte array.
+     */
+    public String legacyPasswordToHash(byte[] salt) {
+        return legacyPasswordToHash(mCredential, salt);
+    }
+
+    /**
+     * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
+     * Not the most secure, but it is at least a second level of protection. First level is that
+     * the file is in a location only readable by the system process.
+     *
+     * @param password the gesture pattern.
+     *
+     * @return the hash of the pattern in a byte array.
+     */
+    public static String legacyPasswordToHash(byte[] password, byte[] salt) {
+        if (password == null || password.length == 0 || salt == null) {
+            return null;
+        }
+
+        try {
+            // Previously the password was passed as a String with the following code:
+            // byte[] saltedPassword = (password + salt).getBytes();
+            // The code below creates the identical digest preimage using byte arrays:
+            byte[] saltedPassword = Arrays.copyOf(password, password.length + salt.length);
+            System.arraycopy(salt, 0, saltedPassword, password.length, salt.length);
+            byte[] sha1 = MessageDigest.getInstance("SHA-1").digest(saltedPassword);
+            byte[] md5 = MessageDigest.getInstance("MD5").digest(saltedPassword);
+
+            byte[] combined = new byte[sha1.length + md5.length];
+            System.arraycopy(sha1, 0, combined, 0, sha1.length);
+            System.arraycopy(md5, 0, combined, sha1.length, md5.length);
+
+            final char[] hexEncoded = HexEncoding.encode(combined);
+            Arrays.fill(saltedPassword, (byte) 0);
+            return new String(hexEncoded);
+        } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError("Missing digest algorithm: ", e);
+        }
+    }
+
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mType);
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 5630a1e..7fde92c 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -45,6 +45,7 @@
 #define ENCODING_MPEGH_BL_L4 24
 #define ENCODING_MPEGH_LC_L3 25
 #define ENCODING_MPEGH_LC_L4 26
+#define ENCODING_DTS_UHD 27
 
 #define ENCODING_INVALID    0
 #define ENCODING_DEFAULT    1
@@ -110,6 +111,8 @@
         return AUDIO_FORMAT_MPEGH_LC_L3;
     case ENCODING_MPEGH_LC_L4:
         return AUDIO_FORMAT_MPEGH_LC_L4;
+    case ENCODING_DTS_UHD:
+        return AUDIO_FORMAT_DTS_UHD;
     default:
         return AUDIO_FORMAT_INVALID;
     }
@@ -179,6 +182,8 @@
         return ENCODING_MPEGH_LC_L3;
     case AUDIO_FORMAT_MPEGH_LC_L4:
         return ENCODING_MPEGH_LC_L4;
+    case AUDIO_FORMAT_DTS_UHD:
+        return ENCODING_DTS_UHD;
     case AUDIO_FORMAT_DEFAULT:
         return ENCODING_DEFAULT;
     default:
diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto
index ead7e16..a21d1d4 100644
--- a/core/proto/android/app/activitymanager.proto
+++ b/core/proto/android/app/activitymanager.proto
@@ -33,4 +33,6 @@
     UID_OBSERVER_FLAG_ACTIVE = 4;
     // report uid cached state has changed, original value is 1 << 4
     UID_OBSERVER_FLAG_CACHED = 5;
+    // report uid capability has changed, original value is 1 << 5
+    UID_OBSERVER_FLAG_CAPABILITY = 6;
 }
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 74a37ca..17dc4589 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -778,6 +778,7 @@
         CHANGE_ACTIVE = 2;
         CHANGE_CACHED = 3;
         CHANGE_UNCACHED = 4;
+        CHANGE_CAPABILITY = 5;
     }
     repeated Change last_reported_changes = 8;
     optional int32 num_procs = 9;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 83e6c4b..58abfeb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -326,7 +326,6 @@
     <protected-broadcast android:name="android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED" />
 
     <protected-broadcast android:name="android.nfc.action.ADAPTER_STATE_CHANGED" />
-    <protected-broadcast android:name="android.nfc.action.ALWAYS_ON_STATE_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.PREFERRED_PAYMENT_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
     <protected-broadcast android:name="android.nfc.action.REQUIRE_UNLOCK_FOR_NFC" />
@@ -346,7 +345,7 @@
     <protected-broadcast android:name="android.nfc.handover.intent.action.HANDOVER_SEND_MULTIPLE" />
     <protected-broadcast android:name="com.android.nfc.handover.action.CANCEL_HANDOVER_TRANSFER" />
 
-    <protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
+    <protected-broadcast android:name="android.net.action.CLEAR_DNS_CACHE" />
     <protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
 
     <protected-broadcast android:name="android.os.UpdateLock.UPDATE_LOCK_CHANGED" />
@@ -1391,7 +1390,7 @@
     <!-- Required to be able to discover and connect to nearby Bluetooth devices.
          <p>Protection level: dangerous -->
     <permission-group android:name="android.permission-group.NEARBY_DEVICES"
-        android:icon="@drawable/ic_qs_bluetooth"
+        android:icon="@drawable/perm_group_nearby_devices"
         android:label="@string/permgrouplab_nearby_devices"
         android:description="@string/permgroupdesc_nearby_devices"
         android:priority="750" />
@@ -4241,6 +4240,15 @@
     <permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
                 android:protectionLevel="signature|installer" />
 
+    <!-- @SystemApi Allows an application to bypass role qualification. This allows switching role
+         holders to otherwise non eligible holders. Only the shell is allowed to do this, the
+         qualification for the shell role itself cannot be bypassed, and each role needs to
+         explicitly allow bypassing qualification in its definition. The bypass state will not be
+         persisted across reboot.
+     @hide -->
+    <permission android:name="android.permission.BYPASS_ROLE_QUALIFICATION"
+                android:protectionLevel="internal|role" />
+
     <!-- @SystemApi Allows an application to observe role holder changes.
          @hide -->
     <permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
@@ -4258,6 +4266,7 @@
                 android:protectionLevel="normal" />
 
     <!-- Allows an application to create new companion device associations.
+         @SystemApi
          @hide -->
     <permission android:name="android.permission.ASSOCIATE_COMPANION_DEVICES"
         android:protectionLevel="internal|role" />
diff --git a/core/res/res/drawable/perm_group_nearby_devices.xml b/core/res/res/drawable/perm_group_nearby_devices.xml
new file mode 100644
index 0000000..84cc432
--- /dev/null
+++ b/core/res/res/drawable/perm_group_nearby_devices.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M12,16.427L7.574,12 12,7.574 16.426,12zM10.58,2.59l-8,8c-0.78,0.78 -0.78,2.05 0,2.83l8,8c0.78,0.78 2.05,0.78 2.83,0l8,-8c0.78,-0.78 0.78,-2.05 0,-2.83l-8,-8c-0.78,-0.79 -2.04,-0.79 -2.83,0zM13.39,17.81L12,19.2l-1.39,-1.39 -4.42,-4.42L4.8,12l1.39,-1.39 4.42,-4.42L12,4.8l1.39,1.39 4.42,4.42L19.2,12l-1.39,1.39 -4.42,4.42z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 986bb82..c51b2d8 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1867,16 +1867,21 @@
              -->
         <attr name="preserveLegacyExternalStorage" format="boolean" />
 
-        <!-- If {@code true} this app would like optimized external storage access.
+        <!-- If {@code true} this app would like raw external storage access.
 
         <p> This flag can only be used by apps holding
         <ul>
         <li>{@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission or
         <li>{@link android.app.role}#SYSTEM_GALLERY role.
         </ul>
-        When the flag is set, bulk file path operations will be optimized.
+        <p> When the flag is set, all file path access on external storage will bypass database
+        operations that update MediaStore collection. Raw external storage access as a side effect
+        can improve performance of bulk file path operations but can cause unexpected behavior in
+        apps due to inconsistencies in MediaStore collection and lower file system.
+        When the flag is set, app should scan the file after file path operations to ensure
+        consistency of MediaStore collection.
 
-        The default value is {@code true} if
+        <p> The default value is {@code true} if
         <ul>
         <li>app has {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission and
         targets targetSDK<=30.
@@ -1884,7 +1889,7 @@
         </ul>
         {@code false} otherwise.
         -->
-        <attr name="requestOptimizedExternalStorageAccess" format="boolean" />
+        <attr name="requestRawExternalStorageAccess" format="boolean" />
 
         <!-- If {@code true} this app declares that it should be visible to all other apps on
              device, regardless of what they declare via the {@code queries} tags in their
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc2bdf3..142d923 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1637,32 +1637,22 @@
          config_timeZoneRulesUpdateTrackingEnabled are true.] -->
     <integer name="config_timeZoneRulesCheckRetryCount">5</integer>
 
-    <!-- Whether the geolocation time zone detection feature is enabled. -->
+    <!-- Whether the geolocation time zone detection feature is enabled. Setting this to false means
+         the feature cannot be used. Setting this to true means it may be used if other
+         configuration allows (see provider configuration below, also compile time overlays). -->
     <bool name="config_enableGeolocationTimeZoneDetection" translatable="false">true</bool>
 
-    <!-- Whether the primary LocationTimeZoneProvider is enabled device.
+    <!-- Whether the primary LocationTimeZoneProvider is enabled.
          Ignored if config_enableGeolocationTimeZoneDetection is false -->
     <bool name="config_enablePrimaryLocationTimeZoneProvider" translatable="false">false</bool>
-    <!-- Used when enablePrimaryLocationTimeZoneProvider is true. Controls whether to enable primary
-         location time zone provider overlay which allows the primary location time zone provider to
-         be replaced by an app at run-time. When disabled, only the
-         config_primaryLocationTimeZoneProviderPackageName package will be searched for the primary
-         location time zone provider, otherwise any system package is eligible. Anyone who wants to
-         disable the runtime overlay mechanism can set it to false. -->
-    <bool name="config_enablePrimaryLocationTimeZoneOverlay" translatable="false">false</bool>
-    <!-- Package name providing the primary location time zone provider. Used only when
-         config_enablePrimaryLocationTimeZoneOverlay is false. -->
+    <!-- The package name providing the primary location time zone provider.
+         Only used when config_enableGeolocationTimeZoneDetection and
+         enablePrimaryLocationTimeZoneProvider are true. -->
     <string name="config_primaryLocationTimeZoneProviderPackageName" translatable="false">@null</string>
 
-    <!-- Whether the secondary LocationTimeZoneProvider is enabled device.
+    <!-- Whether the secondary LocationTimeZoneProvider is enabled.
          Ignored if config_enableGeolocationTimeZoneDetection is false -->
     <bool name="config_enableSecondaryLocationTimeZoneProvider" translatable="false">true</bool>
-    <!-- Used when enableSecondaryLocationTimeZoneProvider is true. Controls whether to enable
-         secondary location time zone provider overlay which allows the primary location time zone
-         provider to config_secondaryLocationTimeZoneProviderPackageName package will be searched
-         for the secondary location time zone provider, otherwise any system package is eligible.
-         Anyone who wants to disable the runtime overlay mechanism can set it to false. -->
-    <bool name="config_enableSecondaryLocationTimeZoneOverlay" translatable="false">false</bool>
     <!-- Package name providing the secondary location time zone provider. Used only when
          config_enableSecondaryLocationTimeZoneOverlay is false.
 
@@ -1942,11 +1932,13 @@
     <!-- The name of the package that will hold the speech recognizer role by default. -->
     <string name="config_systemSpeechRecognizer" translatable="false"></string>
     <!-- The name of the package that will hold the system Wi-Fi coex manager role. -->
-    <string name="config_systemWifiCoexManager" translateable="false"></string>
+    <string name="config_systemWifiCoexManager" translatable="false"></string>
     <!-- The name of the package that will hold the wellbeing role. -->
     <string name="config_systemWellbeing" translatable="false"></string>
     <!-- The name of the package that will hold the television notification handler role -->
     <string name="config_systemTelevisionNotificationHandler" translatable="false"></string>
+    <!-- The name of the package that will hold the system activity recognizer role. -->
+    <string name="config_systemActivityRecognizer" translatable="false"></string>
 
     <!-- The name of the package that will be allowed to change its components' label/icon. -->
     <string name="config_overrideComponentUiPackage" translatable="false"></string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b402c95..3a5621b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3092,7 +3092,7 @@
     <public name="attributionTags"/>
     <public name="suppressesSpellChecker" />
     <public name="usesPermissionFlags" />
-    <public name="requestOptimizedExternalStorageAccess" />
+    <public name="requestRawExternalStorageAccess" />
     <!-- @hide @SystemApi -->
     <public name="playHomeTransitionSound" />
     <public name="lStar" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 61e766e..90e9c09 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -823,9 +823,9 @@
     <string name="permgroupdesc_camera">take pictures and record video</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
-    <string name="permgrouplab_nearby_devices">Nearby Bluetooth Devices</string>
+    <string name="permgrouplab_nearby_devices">Nearby devices</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
-    <string name="permgroupdesc_nearby_devices">discover and connect to nearby Bluetooth devices</string>
+    <string name="permgroupdesc_nearby_devices">discover and connect to nearby devices</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_calllog">Call logs</string>
@@ -1486,9 +1486,9 @@
     <string name="permdesc_bluetooth_connect" product="default">Allows the app to connect to paired Bluetooth devices</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50]-->
-    <string name="permlab_uwb_ranging">range to devices using ultra-wideband</string>
+    <string name="permlab_uwb_ranging">determine relative position between nearby Ultra-Wideband devices</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=120]-->
-    <string name="permdesc_uwb_ranging" product="default">Allows the app to range to devices using ultra-wideband</string>
+    <string name="permdesc_uwb_ranging">Allow the app to determine relative position between nearby Ultra-Wideband devices</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_preferredPaymentInfo">Preferred NFC Payment Service Information</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index eb1fe1d..408620a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2180,10 +2180,8 @@
   <java-symbol type="bool" name="config_enableGnssTimeUpdateService" />
   <java-symbol type="bool" name="config_enableGeolocationTimeZoneDetection" />
   <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneProvider" />
-  <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" />
   <java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" />
   <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" />
-  <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneOverlay" />
   <java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
 
   <java-symbol type="layout" name="resolver_list" />
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
index dd60dd4..1a63660 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
@@ -44,8 +44,6 @@
 import com.android.bandwidthtest.NetworkState.StateTransitionDirection;
 import com.android.internal.util.AsyncChannel;
 
-import junit.framework.Assert;
-
 import java.io.IOException;
 import java.net.UnknownHostException;
 import java.util.List;
@@ -76,7 +74,11 @@
     private WifiManager mWifiManager;
     private Context mContext;
     // Verify connectivity state
-    private static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1;
+    // ConnectivityManager.TYPE_* is deprecated and no longer extended, so use the max public
+    // network type - TYPE_VPN should be enough.
+    // TODO: Replace registering CONNECTIVITY_ACTION with registering NetworkCallback and check
+    //  network by NetworkCapabilities.TRANSPORT_* and NetworkCapabilities.hasTransport() instead.
+    private static final int NUM_NETWORK_TYPES = ConnectivityManager.TYPE_VPN + 1;
     private NetworkState[] mConnectivityState = new NetworkState[NUM_NETWORK_TYPES];
 
     public ConnectionUtil(Context context) {
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index f47fa39..4dbdc60 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -531,7 +531,7 @@
         }
 
         @Override
-        public void scheduleCrash(String s) throws RemoteException {
+        public void scheduleCrash(String s, int i) throws RemoteException {
         }
 
         @Override
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index fa1aa5e..74cdd21 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -1,3 +1,6 @@
+# Accessibility
+per-file WindowInfoTest.java = file:/services/accessibility/OWNERS
+
 # Input
 per-file *MotionEventTest.* = file:/services/core/java/com/android/server/input/OWNERS
 per-file *KeyEventTest.* = file:/services/core/java/com/android/server/input/OWNERS
@@ -9,6 +12,7 @@
 per-file *Insets* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file *View* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file *Visibility* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Window*  = file:/services/core/java/com/android/server/wm/OWNERS
 
 # Scroll Capture
 per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
index 05bab1c..9d77d16 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
@@ -17,6 +17,8 @@
 package com.android.internal.widget;
 
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.test.AndroidTestCase;
 
 import java.util.Arrays;
@@ -175,6 +177,81 @@
         assertEquals(credential, credential.duplicate());
     }
 
+    public void testPasswordToHistoryHash() {
+        String password = "1234";
+        LockscreenCredential credential = LockscreenCredential.createPassword(password);
+        String hashFactor = "6637D20C0798382D9F1304861C81DE222BC6CB7183623C67DA99B115A7AF702D";
+        String salt = "6d5331dd120077a0";
+        String expectedHash = "BCFB17409F2CD0A00D8627F76D080FB547B0B6A30CB7A375A34720D2312EDAC7";
+
+        assertThat(
+                credential.passwordToHistoryHash(salt.getBytes(), hashFactor.getBytes()))
+                .isEqualTo(expectedHash);
+        assertThat(
+                LockscreenCredential.passwordToHistoryHash(
+                        password.getBytes(), salt.getBytes(), hashFactor.getBytes()))
+                .isEqualTo(expectedHash);
+    }
+
+    public void testPasswordToHistoryHashInvalidInput() {
+        String password = "1234";
+        LockscreenCredential credential = LockscreenCredential.createPassword(password);
+        String hashFactor = "6637D20C0798382D9F1304861C81DE222BC6CB7183623C67DA99B115A7AF702D";
+        String salt = "6d5331dd120077a0";
+
+        assertThat(
+                credential.passwordToHistoryHash(/* salt= */ null, hashFactor.getBytes()))
+                .isNull();
+        assertThat(
+                LockscreenCredential.passwordToHistoryHash(
+                        password.getBytes(), /* salt= */ null, hashFactor.getBytes()))
+                .isNull();
+
+        assertThat(
+                credential.passwordToHistoryHash(salt.getBytes(), /* hashFactor= */ null))
+                .isNull();
+        assertThat(
+                LockscreenCredential.passwordToHistoryHash(
+                        password.getBytes(), salt.getBytes(), /* hashFactor= */ null))
+                .isNull();
+
+        assertThat(
+                LockscreenCredential.passwordToHistoryHash(
+                        /* password= */ null, salt.getBytes(), hashFactor.getBytes()))
+                .isNull();
+    }
+
+    public void testLegacyPasswordToHash() {
+        String password = "1234";
+        LockscreenCredential credential = LockscreenCredential.createPassword(password);
+        String salt = "6d5331dd120077a0";
+        String expectedHash =
+                "2DD04348ADBF8F4CABD7F722DC2E2887FAD4B6020A0C3E02C831E09946F0554FDC13B155";
+
+        assertThat(
+                credential.legacyPasswordToHash(salt.getBytes()))
+                .isEqualTo(expectedHash);
+        assertThat(
+                LockscreenCredential.legacyPasswordToHash(
+                        password.getBytes(), salt.getBytes()))
+                .isEqualTo(expectedHash);
+    }
+
+    public void testLegacyPasswordToHashInvalidInput() {
+        String password = "1234";
+        LockscreenCredential credential = LockscreenCredential.createPassword(password);
+        String salt = "6d5331dd120077a0";
+
+        assertThat(credential.legacyPasswordToHash(/* salt= */ null)).isNull();
+        assertThat(LockscreenCredential.legacyPasswordToHash(
+                password.getBytes(), /* salt= */ null)).isNull();
+
+        assertThat(
+                LockscreenCredential.legacyPasswordToHash(
+                        /* password= */ null, salt.getBytes()))
+                .isNull();
+    }
+
     private LockscreenCredential createPattern(String patternString) {
         return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
                 patternString.getBytes()));
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
index 8271bed..5de6d42 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
@@ -128,8 +128,8 @@
         rangingManager.onRangingReconfigureFailed(handle, REASON, PARAMS);
         verify(callback, times(1)).onReconfigureFailed(eq(REASON), eq(PARAMS));
 
-        rangingManager.onRangingStopped(handle);
-        verify(callback, times(1)).onStopped();
+        rangingManager.onRangingStopped(handle, REASON, PARAMS);
+        verify(callback, times(1)).onStopped(eq(REASON), eq(PARAMS));
 
         rangingManager.onRangingStopFailed(handle, REASON, PARAMS);
         verify(callback, times(1)).onStopFailed(eq(REASON), eq(PARAMS));
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 0987f77..489da16 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -429,6 +429,7 @@
         <permission name="android.permission.LOG_COMPAT_CHANGE" />
         <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
         <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" />
+        <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD" />
         <!-- Permissions required to test ambient display. -->
         <permission name="android.permission.READ_DREAM_STATE" />
         <permission name="android.permission.WRITE_DREAM_STATE" />
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 54f9fa8..a612265 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -673,6 +673,13 @@
     /**
      * @hide
      */
+    public void punchHole(float left, float top, float right, float bottom, float rx, float ry) {
+        nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry);
+    }
+
+    /**
+     * @hide
+     */
     public void setHwBitmapsInSwModeEnabled(boolean enabled) {
         mAllowHwBitmapsInSwMode = enabled;
     }
@@ -815,4 +822,7 @@
 
     private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath,
             float hOffset, float vOffset, int flags, long nativePaint);
+
+    private static native void nPunchHole(long renderer, float left, float top, float right,
+            float bottom, float rx, float ry);
 }
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index a8922e8..a998ba8 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -610,6 +610,14 @@
                 indices, indexOffset, indexCount, paint.getNativeInstance());
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public void punchHole(float left, float top, float right, float bottom, float rx, float ry) {
+        nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry);
+    }
+
     @FastNative
     private static native void nDrawBitmap(long nativeCanvas, long bitmapHandle, float left,
             float top, long nativePaintOrZero, int canvasDensity, int screenDensity,
@@ -735,4 +743,8 @@
     @FastNative
     private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath,
             float hOffset, float vOffset, int flags, long nativePaint);
+
+    @FastNative
+    private static native void nPunchHole(long renderer, float left, float top, float right,
+            float bottom, float rx, float ry);
 }
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 1f9022b..a6aa4f2 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -353,7 +353,7 @@
             boolean userPresenceRequired,
             byte[] attestationChallenge,
             boolean devicePropertiesAttestationIncluded,
-            int[] attestationIds,
+            @NonNull int[] attestationIds,
             boolean uniqueIdIncluded,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
@@ -779,9 +779,8 @@
      * @return integer array representing the requested device IDs to attest.
      */
     @SystemApi
-    @Nullable
-    public int[] getAttestationIds() {
-        return Utils.cloneIfNotNull(mAttestationIds);
+    public @NonNull int[] getAttestationIds() {
+        return mAttestationIds.clone();
     }
 
     /**
@@ -911,7 +910,7 @@
         private boolean mUserPresenceRequired = false;
         private byte[] mAttestationChallenge = null;
         private boolean mDevicePropertiesAttestationIncluded = false;
-        private int[] mAttestationIds = null;
+        private int[] mAttestationIds = new int[0];
         private boolean mUniqueIdIncluded = false;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index c26d9f583..dc7f3dd 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -655,7 +655,7 @@
             }
 
             int[] idTypes = mSpec.getAttestationIds();
-            if (idTypes == null) {
+            if (idTypes.length == 0) {
                 return;
             }
             final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f6b63eb..e152633 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1011,6 +1011,9 @@
 
     /** Moves the PiP menu to the destination bounds. */
     public void finishResizeForMenu(Rect destinationBounds) {
+        if (!isInPip()) {
+            return;
+        }
         mPipMenuController.movePipMenu(null, null, destinationBounds);
         mPipMenuController.updateMenuBounds(destinationBounds);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 4cf8ab4..17cde73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -16,13 +16,9 @@
 
 package com.android.wm.shell.pip.phone;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
 
 import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
-import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.RemoteAction;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
@@ -43,6 +39,7 @@
 
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipMediaController.ActionListener;
 import com.android.wm.shell.pip.PipMenuController;
@@ -59,7 +56,7 @@
  */
 public class PhonePipMenuController implements PipMenuController {
 
-    private static final String TAG = "PipMenuActController";
+    private static final String TAG = "PhonePipMenuController";
     private static final boolean DEBUG = false;
 
     public static final int MENU_STATE_NONE = 0;
@@ -99,6 +96,7 @@
     private final RectF mTmpSourceRectF = new RectF();
     private final RectF mTmpDestinationRectF = new RectF();
     private final Context mContext;
+    private final PipBoundsState mPipBoundsState;
     private final PipMediaController mMediaController;
     private final ShellExecutor mMainExecutor;
     private final Handler mMainHandler;
@@ -134,10 +132,11 @@
         }
     };
 
-    public PhonePipMenuController(Context context, PipMediaController mediaController,
-            SystemWindows systemWindows, ShellExecutor mainExecutor,
-            Handler mainHandler) {
+    public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
+            PipMediaController mediaController, SystemWindows systemWindows,
+            ShellExecutor mainExecutor, Handler mainHandler) {
         mContext = context;
+        mPipBoundsState = pipBoundsState;
         mMediaController = mediaController;
         mSystemWindows = systemWindows;
         mMainExecutor = mainExecutor;
@@ -466,20 +465,11 @@
      * Updates the PiP menu with the best set of actions provided.
      */
     private void updateMenuActions() {
-        if (isMenuVisible()) {
-            // Fetch the pinned stack bounds
-            Rect stackBounds = null;
-            try {
-                RootTaskInfo pinnedTaskInfo = ActivityTaskManager.getService().getRootTaskInfo(
-                        WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
-                if (pinnedTaskInfo != null) {
-                    stackBounds = pinnedTaskInfo.bounds;
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error showing PIP menu", e);
+        if (mPipMenuView != null) {
+            final ParceledListSlice<RemoteAction> menuActions = resolveMenuActions();
+            if (menuActions != null) {
+                mPipMenuView.setActions(mPipBoundsState.getBounds(), menuActions.getList());
             }
-
-            mPipMenuView.setActions(stackBounds, resolveMenuActions().getList());
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 1bfae53..7e594a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -374,7 +374,9 @@
     void setActions(Rect stackBounds, List<RemoteAction> actions) {
         mActions.clear();
         mActions.addAll(actions);
-        updateActionViews(stackBounds);
+        if (mMenuState == MENU_STATE_FULL) {
+            updateActionViews(stackBounds);
+        }
     }
 
     private void updateActionViews(Rect stackBounds) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 607ef72..c75b21f 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -464,10 +464,12 @@
         "canvas/CanvasOpBuffer.cpp",
         "canvas/CanvasOpRasterizer.cpp",
         "effects/StretchEffect.cpp",
+        "pipeline/skia/HolePunch.cpp",
         "pipeline/skia/SkiaDisplayList.cpp",
         "pipeline/skia/SkiaRecordingCanvas.cpp",
         "pipeline/skia/RenderNodeDrawable.cpp",
         "pipeline/skia/ReorderBarrierDrawables.cpp",
+        "pipeline/skia/TransformCanvas.cpp",
         "renderthread/Frame.cpp",
         "renderthread/RenderTask.cpp",
         "renderthread/TimeLord.cpp",
@@ -647,6 +649,7 @@
         "tests/unit/CommonPoolTests.cpp",
         "tests/unit/DamageAccumulatorTests.cpp",
         "tests/unit/DeferredLayerUpdaterTests.cpp",
+        "tests/unit/EglManagerTests.cpp",
         "tests/unit/FatVectorTests.cpp",
         "tests/unit/GraphicsStatsServiceTests.cpp",
         "tests/unit/LayerUpdateQueueTests.cpp",
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 3aa5b4b..894b479 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -17,8 +17,10 @@
 #pragma once
 
 #include "pipeline/skia/SkiaDisplayList.h"
+#include "canvas/CanvasOpBuffer.h"
 
 #include <memory>
+#include <variant>
 
 namespace android {
 namespace uirenderer {
@@ -28,29 +30,25 @@
 };
 typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
 
-/**
- * Data structure that holds the list of commands used in display list stream
- */
-//using DisplayList = skiapipeline::SkiaDisplayList;
-class DisplayList {
+class SkiaDisplayListWrapper {
 public:
     // Constructs an empty (invalid) DisplayList
-    explicit DisplayList() {}
+    explicit SkiaDisplayListWrapper() {}
 
     // Constructs a DisplayList from a SkiaDisplayList
-    explicit DisplayList(std::unique_ptr<skiapipeline::SkiaDisplayList> impl)
+    explicit SkiaDisplayListWrapper(std::unique_ptr<skiapipeline::SkiaDisplayList> impl)
         : mImpl(std::move(impl)) {}
 
     // Move support
-    DisplayList(DisplayList&& other) : mImpl(std::move(other.mImpl)) {}
-    DisplayList& operator=(DisplayList&& other) {
+    SkiaDisplayListWrapper(SkiaDisplayListWrapper&& other) : mImpl(std::move(other.mImpl)) {}
+    SkiaDisplayListWrapper& operator=(SkiaDisplayListWrapper&& other) {
         mImpl = std::move(other.mImpl);
         return *this;
     }
 
     // No copy support
-    DisplayList(const DisplayList& other) = delete;
-    DisplayList& operator=(const DisplayList&) = delete;
+    SkiaDisplayListWrapper(const SkiaDisplayListWrapper& other) = delete;
+    SkiaDisplayListWrapper& operator=(const SkiaDisplayListWrapper&) = delete;
 
     void updateChildren(std::function<void(RenderNode*)> updateFn) {
         mImpl->updateChildren(std::move(updateFn));
@@ -74,6 +72,10 @@
         return mImpl && !(mImpl->isEmpty());
     }
 
+    [[nodiscard]] bool hasHolePunches() const {
+        return mImpl && mImpl->hasHolePunches();
+    }
+
     [[nodiscard]] bool containsProjectionReceiver() const {
         return mImpl && mImpl->containsProjectionReceiver();
     }
@@ -137,7 +139,7 @@
 
     void applyColorTransform(ColorTransform transform) {
         if (mImpl) {
-            mImpl->mDisplayList.applyColorTransform(transform);
+            mImpl->applyColorTransform(transform);
         }
     }
 
@@ -145,5 +147,172 @@
     std::unique_ptr<skiapipeline::SkiaDisplayList> mImpl;
 };
 
+
+/**
+ * Data structure that holds the list of commands used in display list stream
+ */
+//using DisplayList = skiapipeline::SkiaDisplayList;
+class MultiDisplayList {
+private:
+    using SkiaDisplayList = skiapipeline::SkiaDisplayList;
+
+    struct EmptyList {
+        bool hasText() const { return false; }
+        void updateChildren(std::function<void(RenderNode*)> updateFn) {}
+        bool isEmpty() const { return true; }
+        bool containsProjectionReceiver() const { return false; }
+        bool hasVectorDrawables() const { return false; }
+        size_t getUsedSize() const { return 0; }
+        size_t getAllocatedSize() const { return 0; }
+        void output(std::ostream& output, uint32_t level) const { }
+        bool hasFunctor() const { return false; }
+        bool prepareListAndChildren(
+                TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+                std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
+            return false;
+        }
+        void syncContents(const WebViewSyncData& data) { }
+        void applyColorTransform(ColorTransform transform) { }
+    };
+
+    std::variant<EmptyList, std::unique_ptr<SkiaDisplayList>, CanvasOpBuffer> mImpls;
+
+    template <typename T>
+    static constexpr T& get(T& t) { return t; }
+    template <typename T>
+    static constexpr const T& get(const T& t) { return t; }
+
+    template <typename T>
+    static constexpr T& get(std::unique_ptr<T>& t) { return *t; }
+    template <typename T>
+    static constexpr const T& get(const std::unique_ptr<T>& t) { return *t; }
+
+    template <typename T>
+    auto apply(T&& t) {
+        return std::visit([&t](auto& it) -> auto {
+            return t(get(it));
+        }, mImpls);
+    }
+
+    template <typename T>
+    auto apply(T&& t) const {
+        return std::visit([&t](const auto& it) -> auto {
+            return t(get(it));
+        }, mImpls);
+    }
+
+public:
+    // Constructs an empty (invalid) DisplayList
+    explicit MultiDisplayList() {}
+
+    // Constructs a DisplayList from a SkiaDisplayList
+    explicit MultiDisplayList(std::unique_ptr<SkiaDisplayList> impl)
+        : mImpls(std::move(impl)) {}
+
+    explicit MultiDisplayList(CanvasOpBuffer&& opBuffer) : mImpls(std::move(opBuffer)) {}
+
+    // Move support
+    MultiDisplayList(MultiDisplayList&& other) : mImpls(std::move(other.mImpls)) {}
+    MultiDisplayList& operator=(MultiDisplayList&& other) {
+        mImpls = std::move(other.mImpls);
+        return *this;
+    }
+
+    // No copy support
+    MultiDisplayList(const MultiDisplayList& other) = delete;
+    MultiDisplayList& operator=(const MultiDisplayList&) = delete;
+
+    void updateChildren(std::function<void(RenderNode*)> updateFn) {
+        apply([&](auto& it) { it.updateChildren(std::move(updateFn)); });
+    }
+
+    [[nodiscard]] explicit operator bool() const {
+        return isValid();
+    }
+
+    // If true this DisplayList contains a backing content, even if that content is empty
+    // If false, there this DisplayList is in an "empty" state
+    [[nodiscard]] bool isValid() const {
+        return mImpls.index() != 0;
+    }
+
+    [[nodiscard]] bool isEmpty() const {
+        return apply([](const auto& it) -> auto { return it.isEmpty(); });
+    }
+
+    [[nodiscard]] bool hasContent() const {
+        return !isEmpty();
+    }
+
+    [[nodiscard]] bool containsProjectionReceiver() const {
+        return apply([](const auto& it) -> auto { return it.containsProjectionReceiver(); });
+    }
+
+    [[nodiscard]] SkiaDisplayList* asSkiaDl() {
+        return std::get<1>(mImpls).get();
+    }
+
+    [[nodiscard]] const SkiaDisplayList* asSkiaDl() const {
+        return std::get<1>(mImpls).get();
+    }
+
+    [[nodiscard]] bool hasVectorDrawables() const {
+        return apply([](const auto& it) -> auto { return it.hasVectorDrawables(); });
+    }
+
+    void clear(RenderNode* owningNode = nullptr) {
+        if (owningNode && mImpls.index() == 1) {
+            auto& skiaDl = std::get<1>(mImpls);
+            if (skiaDl->reuseDisplayList(owningNode)) {
+                skiaDl.release();
+            }
+        }
+        mImpls = EmptyList{};
+    }
+
+    [[nodiscard]] size_t getUsedSize() const {
+        return apply([](const auto& it) -> auto { return it.getUsedSize(); });
+    }
+
+    [[nodiscard]] size_t getAllocatedSize() const {
+        return apply([](const auto& it) -> auto { return it.getAllocatedSize(); });
+    }
+
+    void output(std::ostream& output, uint32_t level) const {
+        apply([&](const auto& it) { it.output(output, level); });
+    }
+
+    [[nodiscard]] bool hasFunctor() const {
+        return apply([](const auto& it) -> auto { return it.hasFunctor(); });
+    }
+
+    bool prepareListAndChildren(
+            TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
+        return apply([&](auto& it) -> auto {
+            return it.prepareListAndChildren(observer, info, functorsNeedLayer, std::move(childFn));
+        });
+    }
+
+    void syncContents(const WebViewSyncData& data) {
+        apply([&](auto& it) { it.syncContents(data); });
+    }
+
+    [[nodiscard]] bool hasText() const {
+        return apply([](const auto& it) -> auto { return it.hasText(); });
+    }
+
+    void applyColorTransform(ColorTransform transform) {
+        apply([=](auto& it) { it.applyColorTransform(transform); });
+    }
+
+    [[nodiscard]] CanvasOpBuffer& asOpBuffer() {
+        return std::get<CanvasOpBuffer>(mImpls);
+    }
+};
+
+// For now stick to the original single-type container to avoid any regressions
+using DisplayList = SkiaDisplayListWrapper;
+
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index f5b2675..e9eae3d 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -255,15 +255,19 @@
 
     if (mDisplayList) {
         info.out.hasFunctors |= mDisplayList.hasFunctor();
+        mHasHolePunches = mDisplayList.hasHolePunches();
         bool isDirty = mDisplayList.prepareListAndChildren(
                 observer, info, childFunctorsNeedLayer,
-                [](RenderNode* child, TreeObserver& observer, TreeInfo& info,
-                   bool functorsNeedLayer) {
+                [this](RenderNode* child, TreeObserver& observer, TreeInfo& info,
+                       bool functorsNeedLayer) {
                     child->prepareTreeImpl(observer, info, functorsNeedLayer);
+                    mHasHolePunches |= child->hasHolePunches();
                 });
         if (isDirty) {
             damageSelf(info);
         }
+    } else {
+        mHasHolePunches = false;
     }
     pushLayerUpdate(info);
 
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 39ea53b..988141f 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -35,6 +35,7 @@
 #include "DisplayList.h"
 #include "Matrix.h"
 #include "RenderProperties.h"
+#include "pipeline/skia/HolePunch.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "pipeline/skia/SkiaLayer.h"
 
@@ -284,6 +285,8 @@
 
     UsageHint mUsageHint = UsageHint::Unknown;
 
+    bool mHasHolePunches;
+
     // METHODS & FIELDS ONLY USED BY THE SKIA RENDERER
 public:
     /**
@@ -294,6 +297,8 @@
         return std::move(mAvailableDisplayList);
     }
 
+    bool hasHolePunches() { return mHasHolePunches; }
+
     /**
      * Attach unused displayList to this node for potential future reuse.
      */
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 28d2b4c..4c4a152 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -23,6 +23,7 @@
 #include "hwui/MinikinUtils.h"
 #include "hwui/PaintFilter.h"
 #include "pipeline/skia/AnimatedDrawables.h"
+#include "pipeline/skia/HolePunch.h"
 
 #include <SkAndroidFrameworkUtils.h>
 #include <SkAnimatedImage.h>
@@ -244,6 +245,13 @@
     return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr;
 }
 
+void SkiaCanvas::punchHole(const SkRRect& rect) {
+    SkPaint paint = SkPaint();
+    paint.setColor(0);
+    paint.setBlendMode(SkBlendMode::kClear);
+    mCanvas->drawRRect(rect, paint);
+}
+
 // ----------------------------------------------------------------------------
 // functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags)
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 9ab2b10..e0a0be5 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -60,6 +60,8 @@
         LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ");
     }
 
+    virtual void punchHole(const SkRRect& rect) override;
+
     virtual void setBitmap(const SkBitmap& bitmap) override;
 
     virtual bool isOpaque() override;
diff --git a/libs/hwui/canvas/CanvasFrontend.h b/libs/hwui/canvas/CanvasFrontend.h
index d749d2f..f9a6101 100644
--- a/libs/hwui/canvas/CanvasFrontend.h
+++ b/libs/hwui/canvas/CanvasFrontend.h
@@ -147,8 +147,7 @@
 public:
     template<class... Args>
     CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height),
-            mReceiver(std::forward<Args>(args)...) { }
-    ~CanvasFrontend() = default;
+            mReceiver(std::in_place, std::forward<Args>(args)...) { }
 
     void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) {
         if (internalSave(flagsToSaveEntry(flags))) {
@@ -186,7 +185,10 @@
         submit(std::move(op));
     }
 
-    const CanvasOpReceiver& receiver() const { return *mReceiver; }
+    const CanvasOpReceiver& receiver() const {
+        LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
+        return *mReceiver;
+    }
 
     CanvasOpReceiver finish() {
         auto ret = std::move(mReceiver.value());
@@ -205,6 +207,7 @@
 
     template <CanvasOpType T>
     void submit(CanvasOp<T>&& op) {
+        LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
         mReceiver->push_container(CanvasOpContainer(std::move(op), transform()));
     }
 };
diff --git a/libs/hwui/canvas/CanvasOpBuffer.cpp b/libs/hwui/canvas/CanvasOpBuffer.cpp
index 7054e47e..6089c572 100644
--- a/libs/hwui/canvas/CanvasOpBuffer.cpp
+++ b/libs/hwui/canvas/CanvasOpBuffer.cpp
@@ -22,4 +22,32 @@
 
 template class OpBuffer<CanvasOpType, CanvasOpContainer>;
 
+void CanvasOpBuffer::updateChildren(std::function<void(RenderNode*)> updateFn) {
+    // TODO: Do we need a fast-path for finding children?
+    if (mHas.children) {
+        for (auto& iter : filter<CanvasOpType::DrawRenderNode>()) {
+            updateFn(iter->renderNode.get());
+        }
+    }
+}
+
+void CanvasOpBuffer::output(std::ostream& output, uint32_t level) const {
+    LOG_ALWAYS_FATAL("TODO");
+}
+
+bool CanvasOpBuffer::prepareListAndChildren(
+            TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
+    LOG_ALWAYS_FATAL("TODO");
+    return false;
+}
+
+void CanvasOpBuffer::syncContents(const WebViewSyncData& data) {
+    LOG_ALWAYS_FATAL("TODO");
+}
+
+void CanvasOpBuffer::applyColorTransform(ColorTransform transform) {
+    LOG_ALWAYS_FATAL("TODO");
+}
+
 }  // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpBuffer.h b/libs/hwui/canvas/CanvasOpBuffer.h
index 07e079a..af797ca 100644
--- a/libs/hwui/canvas/CanvasOpBuffer.h
+++ b/libs/hwui/canvas/CanvasOpBuffer.h
@@ -19,10 +19,17 @@
 #include <SkMatrix.h>
 
 #include "CanvasOpTypes.h"
+#include "CanvasTransform.h"
 #include "OpBuffer.h"
+#include "TreeInfo.h"
+#include "private/hwui/WebViewFunctor.h"
+
+#include <functional>
 
 namespace android::uirenderer {
 
+class RenderNode;
+
 template <CanvasOpType T>
 struct CanvasOp;
 
@@ -53,12 +60,74 @@
 };
 
 extern template class OpBuffer<CanvasOpType, CanvasOpContainer>;
-class CanvasOpBuffer final : public OpBuffer<CanvasOpType, CanvasOpContainer> {
+class CanvasOpBuffer final : private OpBuffer<CanvasOpType, CanvasOpContainer> {
+private:
+    using SUPER = OpBuffer<CanvasOpType, CanvasOpContainer>;
+
 public:
+    // Expose select superclass methods publicly
+    using SUPER::for_each;
+    using SUPER::size;
+    using SUPER::resize;
+
     template <CanvasOpType T>
     void push(CanvasOp<T>&& op) {
         push_container(CanvasOpContainer<T>(std::move(op)));
     }
+
+    template <CanvasOpType T>
+    void push_container(CanvasOpContainer<T>&& op) {
+        if constexpr (IsDrawOp(T)) {
+            mHas.content = true;
+        }
+        if constexpr (T == CanvasOpType::DrawRenderNode) {
+            mHas.children = true;
+            // use staging property, since recording on UI thread
+            if (op->renderNode->stagingProperties().isProjectionReceiver()) {
+                mHas.projectionReceiver = true;
+            }
+        }
+        SUPER::push_container(std::move(op));
+    }
+
+    void clear() {
+        mHas = Contains{};
+        SUPER::clear();
+    }
+
+    void updateChildren(std::function<void(RenderNode*)> updateFn);
+    bool prepareListAndChildren(
+            TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn);
+    void syncContents(const WebViewSyncData& data);
+    void applyColorTransform(ColorTransform transform);
+
+    [[nodiscard]] bool isEmpty() const { return !mHas.content; }
+    [[nodiscard]] bool hasText() const { return mHas.text; }
+    [[nodiscard]] bool hasVectorDrawables() const { return mHas.vectorDrawable; }
+    [[nodiscard]] bool containsProjectionReceiver() const { return mHas.projectionReceiver; }
+    [[nodiscard]] bool hasFunctor() const { return mHas.functor; }
+
+    [[nodiscard]] size_t getUsedSize() const {
+        return size();
+    }
+
+    [[nodiscard]] size_t getAllocatedSize() const {
+        return capacity();
+    }
+
+    void output(std::ostream& output, uint32_t level) const;
+
+private:
+    struct Contains {
+        bool content : 1 = false;
+        bool children : 1 = false;
+        bool projectionReceiver : 1 = false;
+        bool text : 1 = false;
+        bool vectorDrawable : 1 = false;
+        bool functor : 1 = false;
+    };
+    Contains mHas;
 };
 
 }  // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpRasterizer.cpp b/libs/hwui/canvas/CanvasOpRasterizer.cpp
index 0093c38..9297604 100644
--- a/libs/hwui/canvas/CanvasOpRasterizer.cpp
+++ b/libs/hwui/canvas/CanvasOpRasterizer.cpp
@@ -33,21 +33,15 @@
     SkMatrix& currentGlobalTransform = globalMatrixStack.emplace_back(SkMatrix::I());
 
     source.for_each([&]<CanvasOpType T>(const CanvasOpContainer<T> * op) {
-        if constexpr (
-            T == CanvasOpType::BeginZ ||
-            T == CanvasOpType::EndZ   ||
-            T == CanvasOpType::DrawLayer
-        ) {
-            // Do beginZ or endZ
-            LOG_ALWAYS_FATAL("TODO");
-            return;
-        } else {
+        if constexpr (CanvasOpTraits::can_draw<CanvasOp<T>>) {
             // Generic OP
             // First apply the current transformation
             destination->setMatrix(SkMatrix::Concat(currentGlobalTransform, op->transform()));
             // Now draw it
             (*op)->draw(destination);
+            return;
         }
+        LOG_ALWAYS_FATAL("TODO, unable to rasterize %d", static_cast<int>(T));
     });
 }
 
diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h
index cde50bd..b55ef9d 100644
--- a/libs/hwui/canvas/CanvasOpTypes.h
+++ b/libs/hwui/canvas/CanvasOpTypes.h
@@ -35,7 +35,8 @@
     ClipPath,
 
     // Drawing ops
-    DrawColor,
+    DRAW_OP_BEGIN,
+    DrawColor = DRAW_OP_BEGIN,
     DrawRect,
     DrawRegion,
     DrawRoundRect,
@@ -59,10 +60,16 @@
     DrawImageLattice,
     DrawPicture,
     DrawLayer,
+    DrawRenderNode,
+    DRAW_OP_END = DrawRenderNode,
 
     // TODO: Rest
 
     COUNT  // must be last
 };
 
+static constexpr bool IsDrawOp(CanvasOpType t) {
+    return CanvasOpType::DRAW_OP_BEGIN <= t && t <= CanvasOpType::DRAW_OP_END;
+}
+
 }  // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index 86b1ac7..855cd0d 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -24,13 +24,15 @@
 #include <SkImage.h>
 #include <SkPicture.h>
 #include <SkRuntimeEffect.h>
-#include <hwui/Bitmap.h>
-#include <log/log.h>
-#include "CanvasProperty.h"
-#include "Points.h"
 
+#include <log/log.h>
+
+#include "hwui/Bitmap.h"
+#include "CanvasProperty.h"
 #include "CanvasOpTypes.h"
 #include "Layer.h"
+#include "Points.h"
+#include "RenderNode.h"
 
 #include <experimental/type_traits>
 #include <utility>
@@ -450,6 +452,11 @@
     sp<Layer> layer;
 };
 
+template<>
+struct CanvasOp<CanvasOpType::DrawRenderNode> {
+    sp<RenderNode> renderNode;
+};
+
 // cleanup our macros
 #undef ASSERT_DRAWABLE
 
diff --git a/libs/hwui/canvas/OpBuffer.h b/libs/hwui/canvas/OpBuffer.h
index 1237d69..8b5cdbb 100644
--- a/libs/hwui/canvas/OpBuffer.h
+++ b/libs/hwui/canvas/OpBuffer.h
@@ -64,7 +64,7 @@
     static constexpr auto STARTING_SIZE = PadAlign(sizeof(BufferHeader));
     using ItemHeader = OpBufferItemHeader<ItemTypes>;
 
-    OpBuffer() = default;
+    explicit OpBuffer() = default;
 
     // Prevent copying by default
     OpBuffer(const OpBuffer&) = delete;
@@ -135,7 +135,7 @@
 
     template <typename F>
     void for_each(F&& f) const {
-        for_each(std::forward<F>(f), ItemTypesSequence{});
+        do_for_each(std::forward<F>(f), ItemTypesSequence{});
     }
 
     void clear();
@@ -225,7 +225,7 @@
     }
 
     template <typename F, std::size_t... I>
-    void for_each(F&& f, std::index_sequence<I...>) const {
+    void do_for_each(F&& f, std::index_sequence<I...>) const {
         // Validate we're not empty
         if (isEmpty()) return;
 
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index d7162b9..de14beb 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -171,13 +171,13 @@
 static const float ZERO = 0.f;
 static const float CONTENT_DISTANCE_STRETCHED = 1.f;
 
-sk_sp<SkImageFilter> StretchEffect::getImageFilter(const sk_sp<SkImage>& snapshotImage) const {
+sk_sp<SkShader> StretchEffect::getShader(const sk_sp<SkImage>& snapshotImage) const {
     if (isEmpty()) {
         return nullptr;
     }
 
-    if (mStretchFilter != nullptr) {
-        return mStretchFilter;
+    if (mStretchShader != nullptr) {
+        return mStretchShader;
     }
 
     float viewportWidth = stretchArea.width();
@@ -212,10 +212,9 @@
     mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
     mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
 
-    mStretchFilter = SkImageFilters::Shader(mBuilder->makeShader(nullptr, false),
-                                            SkRect{0, 0, viewportWidth, viewportHeight});
+    mStretchShader = mBuilder->makeShader(nullptr, false);
 
-    return mStretchFilter;
+    return mStretchShader;
 }
 
 sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 8221b41..546d53b 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -53,7 +53,7 @@
     StretchEffect& operator=(const StretchEffect& other) {
         this->stretchArea = other.stretchArea;
         this->mStretchDirection = other.mStretchDirection;
-        this->mStretchFilter = nullptr;
+        this->mStretchShader = other.mStretchShader;
         this->maxStretchAmountX = other.maxStretchAmountX;
         this->maxStretchAmountY = other.maxStretchAmountY;
         return *this;
@@ -76,14 +76,14 @@
         maxStretchAmountY = std::max(maxStretchAmountY, other.maxStretchAmountY);
     }
 
-    sk_sp<SkImageFilter> getImageFilter(const sk_sp<SkImage>& snapshotImage) const;
+    sk_sp<SkShader> getShader(const sk_sp<SkImage>& snapshotImage) const;
 
     SkRect stretchArea {0, 0, 0, 0};
     float maxStretchAmountX = 0;
     float maxStretchAmountY = 0;
 
     void setStretchDirection(const SkVector& direction) {
-        mStretchFilter = nullptr;
+        mStretchShader = nullptr;
         mStretchDirection = direction;
     }
 
@@ -93,7 +93,7 @@
     static sk_sp<SkRuntimeEffect> getStretchEffect();
     mutable SkVector mStretchDirection{0, 0};
     mutable std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
-    mutable sk_sp<SkImageFilter> mStretchFilter;
+    mutable sk_sp<SkShader> mStretchShader;
 };
 
 } // namespace android::uirenderer
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index d1bdb71..c1feb76 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -155,6 +155,8 @@
         LOG_ALWAYS_FATAL("Not supported");
     }
 
+    virtual void punchHole(const SkRRect& rect) = 0;
+
     // ----------------------------------------------------------------------------
     // Canvas state operations
     // ----------------------------------------------------------------------------
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index 89fb8bb..a611f7c 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -35,6 +35,7 @@
 #include "SkGraphics.h"
 #include "SkRegion.h"
 #include "SkVertices.h"
+#include "SkRRect.h"
 
 namespace minikin {
 class MeasuredText;
@@ -667,6 +668,11 @@
     Canvas::setCompatibilityVersion(apiLevel);
 }
 
+static void punchHole(JNIEnv* env, jobject, jlong canvasPtr, jfloat left, jfloat top, jfloat right,
+        jfloat bottom, jfloat rx, jfloat ry) {
+    auto canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry));
+}
 
 }; // namespace CanvasJNI
 
@@ -740,6 +746,7 @@
     {"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString},
     {"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars},
     {"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString},
+    {"nPunchHole", "(JFFFFFF)V", (void*) CanvasJNI::punchHole}
 };
 
 int register_android_graphics_Canvas(JNIEnv* env) {
diff --git a/libs/hwui/pipeline/skia/HolePunch.cpp b/libs/hwui/pipeline/skia/HolePunch.cpp
new file mode 100644
index 0000000..2b2bca6
--- /dev/null
+++ b/libs/hwui/pipeline/skia/HolePunch.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+#include "HolePunch.h"
+#include <string>
+
+using namespace android::uirenderer::skiapipeline;
+
+const std::string HOLE_PUNCH_ANNOTATION = "surface_hole_punch";
diff --git a/libs/hwui/pipeline/skia/HolePunch.h b/libs/hwui/pipeline/skia/HolePunch.h
new file mode 100644
index 0000000..92c6f77
--- /dev/null
+++ b/libs/hwui/pipeline/skia/HolePunch.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include "SkRRect.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+const static std::string HOLE_PUNCH_ANNOTATION;
+
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
\ No newline at end of file
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index cb0ff8d..77d99a6 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -18,6 +18,7 @@
 #include <SkPaintFilterCanvas.h>
 #include "RenderNode.h"
 #include "SkiaDisplayList.h"
+#include "TransformCanvas.h"
 #include "utils/TraceUtils.h"
 
 #include <include/effects/SkImageFilters.h>
@@ -179,20 +180,7 @@
         paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
 
         sk_sp<SkImageFilter> imageFilter = sk_ref_sp(properties.getImageFilter());
-        sk_sp<SkImageFilter> stretchFilter =
-                properties.getStretchEffect().getImageFilter(snapshotImage);
-        sk_sp<SkImageFilter> filter;
-        if (imageFilter && stretchFilter) {
-            filter = SkImageFilters::Compose(
-                  std::move(stretchFilter),
-                  std::move(imageFilter)
-            );
-        } else if (stretchFilter) {
-            filter = std::move(stretchFilter);
-        } else {
-            filter = std::move(imageFilter);
-        }
-        paint->setImageFilter(std::move(filter));
+        paint->setImageFilter(std::move(imageFilter));
         return true;
     }
     return false;
@@ -256,8 +244,21 @@
                 canvas->drawAnnotation(bounds, String8::format(
                     "SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr);
             }
-            canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
-                                  SkCanvas::kStrict_SrcRectConstraint);
+
+            if (renderNode->hasHolePunches()) {
+                TransformCanvas transformCanvas(canvas);
+                displayList->draw(&transformCanvas);
+            }
+
+            const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
+            if (stretch.isEmpty()) {
+                canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
+                                      SkCanvas::kStrict_SrcRectConstraint);
+            } else {
+                sk_sp<SkShader> stretchShader = stretch.getShader(snapshotImage);
+                paint.setShader(stretchShader);
+                canvas->drawRect(bounds, paint);
+            }
 
             if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
                 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 483264f..90e9bc6 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <deque>
+
 #include "RecordingCanvas.h"
 #include "RenderNodeDrawable.h"
 #include "TreeInfo.h"
@@ -23,8 +25,6 @@
 #include "utils/LinearAllocator.h"
 #include "utils/Pair.h"
 
-#include <deque>
-
 namespace android {
 namespace uirenderer {
 
@@ -46,8 +46,10 @@
 
 class SkiaDisplayList {
 public:
-    size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); }
-    size_t getAllocatedSize() { return allocator.allocatedSize() + mDisplayList.allocatedSize(); }
+    size_t getUsedSize() const { return allocator.usedSize() + mDisplayList.usedSize(); }
+    size_t getAllocatedSize() const {
+        return allocator.allocatedSize() + mDisplayList.allocatedSize();
+    }
 
     ~SkiaDisplayList() {
         /* Given that we are using a LinearStdAllocator to store some of the
@@ -109,6 +111,10 @@
      */
     void syncContents(const WebViewSyncData& data);
 
+    void applyColorTransform(ColorTransform transform) {
+        mDisplayList.applyColorTransform(transform);
+    }
+
     /**
      * ONLY to be called by RenderNode::prepareTree in order to prepare this
      * list while the UI thread is blocked.  Here we can upload mutable bitmaps
@@ -154,17 +160,25 @@
     std::deque<RenderNodeDrawable> mChildNodes;
     std::deque<FunctorDrawable*> mChildFunctors;
     std::vector<SkImage*> mMutableImages;
+
 private:
     std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables;
+    bool mHasHolePunches;
 public:
-    void appendVD(VectorDrawableRoot* r) {
-        appendVD(r, SkMatrix::I());
-    }
+    void appendVD(VectorDrawableRoot* r) { appendVD(r, SkMatrix::I()); }
 
     void appendVD(VectorDrawableRoot* r, const SkMatrix& mat) {
         mVectorDrawables.push_back(Pair<VectorDrawableRoot*, SkMatrix>(r, mat));
     }
 
+    void setHasHolePunches(bool hasHolePunches) {
+        mHasHolePunches = hasHolePunches;
+    }
+
+    bool hasHolePunches() {
+        return mHasHolePunches;
+    }
+
     std::vector<AnimatedImageDrawable*> mAnimatedImages;
     DisplayListData mDisplayList;
 
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 61f9960..82814de 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -53,6 +53,27 @@
 
     mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
     SkiaCanvas::reset(&mRecorder);
+    mDisplayList->setHasHolePunches(false);
+}
+
+void SkiaRecordingCanvas::punchHole(const SkRRect& rect) {
+    // Add the marker annotation to allow HWUI to determine where the current
+    // clip/transformation should be applied
+    SkVector vector = rect.getSimpleRadii();
+    const int dataSize = 2;
+    float data[dataSize];
+    data[0] = vector.x();
+    data[1] = vector.y();
+    mRecorder.drawAnnotation(rect.rect(), HOLE_PUNCH_ANNOTATION.c_str(),
+                             SkData::MakeWithCopy(data, dataSize));
+
+    // Clear the current rect within the layer itself
+    SkPaint paint = SkPaint();
+    paint.setColor(0);
+    paint.setBlendMode(SkBlendMode::kClear);
+    mRecorder.drawRRect(rect, paint);
+
+    mDisplayList->setHasHolePunches(true);
 }
 
 std::unique_ptr<SkiaDisplayList> SkiaRecordingCanvas::finishRecording() {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index ff03e0c..06f2a27 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -15,6 +15,7 @@
  */
 #pragma once
 
+#include "HolePunch.h"
 #include "RecordingCanvas.h"
 #include "ReorderBarrierDrawables.h"
 #include "SkiaCanvas.h"
@@ -43,6 +44,8 @@
         initDisplayList(renderNode, width, height);
     }
 
+    virtual void punchHole(const SkRRect& rect) override;
+
     virtual void finishRecording(uirenderer::RenderNode* destination) override;
     std::unique_ptr<SkiaDisplayList> finishRecording();
 
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
new file mode 100644
index 0000000..6bfbb0d
--- /dev/null
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+#include "TransformCanvas.h"
+#include "HolePunch.h"
+#include "SkData.h"
+#include "SkDrawable.h"
+
+using namespace android::uirenderer::skiapipeline;
+
+void TransformCanvas::onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) {
+    if (HOLE_PUNCH_ANNOTATION == key) {
+        auto* rectParams = static_cast<const float*>(value->data());
+        float radiusX = rectParams[0];
+        float radiusY = rectParams[1];
+        SkRRect roundRect = SkRRect::MakeRectXY(rect, radiusX, radiusY);
+
+        SkPaint paint;
+        paint.setColor(0);
+        paint.setBlendMode(SkBlendMode::kClear);
+        mWrappedCanvas->drawRRect(roundRect, paint);
+    }
+}
+
+void TransformCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
+    drawable->draw(this, matrix);
+}
+
+bool TransformCanvas::onFilter(SkPaint& paint) const {
+    return false;
+}
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.h b/libs/hwui/pipeline/skia/TransformCanvas.h
new file mode 100644
index 0000000..47f77f1
--- /dev/null
+++ b/libs/hwui/pipeline/skia/TransformCanvas.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <include/core/SkCanvas.h>
+#include "SkPaintFilterCanvas.h"
+
+class TransformCanvas : public SkPaintFilterCanvas {
+public:
+    TransformCanvas(SkCanvas* target) : SkPaintFilterCanvas(target), mWrappedCanvas(target) {}
+
+protected:
+    bool onFilter(SkPaint& paint) const override;
+
+protected:
+    void onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) override;
+    void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override;
+
+private:
+    // We don't own the canvas so just maintain a raw pointer to it
+    SkCanvas* mWrappedCanvas;
+};
diff --git a/libs/hwui/tests/unit/CanvasFrontendTests.cpp b/libs/hwui/tests/unit/CanvasFrontendTests.cpp
index 05b1179..4ddcf6f 100644
--- a/libs/hwui/tests/unit/CanvasFrontendTests.cpp
+++ b/libs/hwui/tests/unit/CanvasFrontendTests.cpp
@@ -124,12 +124,12 @@
 
 TEST(CanvasFrontend, drawOpTransform) {
     CanvasFrontend<CanvasOpBuffer> opCanvas(100, 100);
-    const auto& receiver = opCanvas.receiver();
+    const auto &receiver = opCanvas.receiver();
 
     auto makeDrawRect = [] {
         return CanvasOp<CanvasOpType::DrawRect>{
-            .rect = SkRect::MakeWH(50, 50),
-            .paint = SkPaint(SkColors::kBlack),
+                .rect = SkRect::MakeWH(50, 50),
+                .paint = SkPaint(SkColors::kBlack),
         };
     };
 
@@ -167,14 +167,14 @@
 
     {
         // First result should be identity
-        const auto& result = transforms[0];
+        const auto &result = transforms[0];
         EXPECT_EQ(SkMatrix::kIdentity_Mask, result.getType());
         EXPECT_EQ(SkMatrix::I(), result);
     }
 
     {
         // Should be translate 10, 10
-        const auto& result = transforms[1];
+        const auto &result = transforms[1];
         EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType());
         SkMatrix m;
         m.setTranslate(10, 10);
@@ -183,7 +183,7 @@
 
     {
         // Should be translate 10, 10 + scale 2, 4
-        const auto& result = transforms[2];
+        const auto &result = transforms[2];
         EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask, result.getType());
         SkMatrix m;
         m.setTranslate(10, 10);
@@ -193,7 +193,7 @@
 
     {
         // Should be translate 10, 10 + translate 20, 15
-        const auto& result = transforms[3];
+        const auto &result = transforms[3];
         EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType());
         SkMatrix m;
         m.setTranslate(30, 25);
@@ -202,9 +202,9 @@
 
     {
         // Should be translate 10, 10 + translate 20, 15 + rotate 90
-        const auto& result = transforms[4];
+        const auto &result = transforms[4];
         EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask,
-                result.getType());
+                  result.getType());
         SkMatrix m;
         m.setTranslate(30, 25);
         m.preRotate(90.f);
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index 54970df..a718d46 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -149,7 +149,7 @@
     CanvasOpBuffer buffer;
     EXPECT_EQ(buffer.size(), 0);
     size_t numPts = 3;
-    auto pts = sk_ref_sp(
+    auto pts = sk_sp<Points>(
           new Points({
               {32, 16},
               {48, 48},
@@ -192,7 +192,7 @@
     CanvasOpBuffer buffer;
     EXPECT_EQ(buffer.size(), 0);
     size_t numPts = 3;
-    auto pts = sk_ref_sp(
+    auto pts = sk_sp<Points>(
         new Points({
                {32, 16},
                {48, 48},
diff --git a/libs/hwui/tests/unit/EglManagerTests.cpp b/libs/hwui/tests/unit/EglManagerTests.cpp
new file mode 100644
index 0000000..f7f2406
--- /dev/null
+++ b/libs/hwui/tests/unit/EglManagerTests.cpp
@@ -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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "renderthread/EglManager.h"
+#include "tests/common/TestContext.h"
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
+
+TEST(EglManager, doesSurfaceLeak) {
+    EglManager eglManager;
+    eglManager.initialize();
+
+    ASSERT_TRUE(eglManager.hasEglContext());
+
+    auto colorSpace = SkColorSpace::MakeSRGB();
+    for (int i = 0; i < 100; i++) {
+        TestContext context;
+        auto result =
+                eglManager.createSurface(context.surface().get(), ColorMode::Default, colorSpace);
+        EXPECT_TRUE(result);
+        EGLSurface surface = result.unwrap();
+        eglManager.destroySurface(surface);
+    }
+
+    eglManager.destroy();
+}
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 6dd57b1..8c999c4 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -404,6 +404,7 @@
     EXPECT_TRUE(pipeline->isSurfaceReady());
     renderThread.destroyRenderingContext();
     EXPECT_FALSE(pipeline->isSurfaceReady());
+    LOG_ALWAYS_FATAL_IF(pipeline->isSurfaceReady());
 }
 
 RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) {
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index eedb996..24f553f 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -324,6 +324,8 @@
     public static final int ENCODING_MPEGH_LC_L3 = 25;
     /** Audio data format: MPEG-H low complexity profile, level 4 */
     public static final int ENCODING_MPEGH_LC_L4 = 26;
+    /** Audio data format: DTS UHD compressed */
+    public static final int ENCODING_DTS_UHD = 27;
 
     /** @hide */
     public static String toLogFriendlyEncoding(int enc) {
@@ -380,6 +382,8 @@
                 return "ENCODING_MPEGH_LC_L3";
             case ENCODING_MPEGH_LC_L4:
                 return "ENCODING_MPEGH_LC_L4";
+            case ENCODING_DTS_UHD:
+                return "ENCODING_DTS_UHD";
             default :
                 return "invalid encoding " + enc;
         }
@@ -615,6 +619,7 @@
             case ENCODING_MPEGH_BL_L4:
             case ENCODING_MPEGH_LC_L3:
             case ENCODING_MPEGH_LC_L4:
+            case ENCODING_DTS_UHD:
                 return true;
             default:
                 return false;
@@ -650,6 +655,7 @@
             case ENCODING_MPEGH_BL_L4:
             case ENCODING_MPEGH_LC_L3:
             case ENCODING_MPEGH_LC_L4:
+            case ENCODING_DTS_UHD:
                 return true;
             default:
                 return false;
@@ -688,6 +694,7 @@
             case ENCODING_MPEGH_BL_L4:
             case ENCODING_MPEGH_LC_L3:
             case ENCODING_MPEGH_LC_L4:
+            case ENCODING_DTS_UHD:
                 return false;
             case ENCODING_INVALID:
             default:
@@ -726,6 +733,7 @@
             case ENCODING_MPEGH_BL_L4:
             case ENCODING_MPEGH_LC_L3:
             case ENCODING_MPEGH_LC_L4:
+            case ENCODING_DTS_UHD:
                 return false;
             case ENCODING_INVALID:
             default:
@@ -1012,6 +1020,7 @@
                 case ENCODING_MPEGH_BL_L4:
                 case ENCODING_MPEGH_LC_L3:
                 case ENCODING_MPEGH_LC_L4:
+                case ENCODING_DTS_UHD:
                     mEncoding = encoding;
                     break;
                 case ENCODING_INVALID:
@@ -1238,7 +1247,8 @@
         ENCODING_MPEGH_BL_L3,
         ENCODING_MPEGH_BL_L4,
         ENCODING_MPEGH_LC_L3,
-        ENCODING_MPEGH_LC_L4 }
+        ENCODING_MPEGH_LC_L4,
+        ENCODING_DTS_UHD }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface Encoding {}
@@ -1258,6 +1268,7 @@
             ENCODING_MPEGH_BL_L4,
             ENCODING_MPEGH_LC_L3,
             ENCODING_MPEGH_LC_L4,
+            ENCODING_DTS_UHD
     };
 
     /** @hide */
@@ -1274,7 +1285,8 @@
             ENCODING_MPEGH_BL_L3,
             ENCODING_MPEGH_BL_L4,
             ENCODING_MPEGH_LC_L3,
-            ENCODING_MPEGH_LC_L4 }
+            ENCODING_MPEGH_LC_L4,
+            ENCODING_DTS_UHD }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface SurroundSoundEncoding {}
@@ -1316,6 +1328,8 @@
                 return "MPEG-H 3D Audio low complexity profile level 3";
             case ENCODING_MPEGH_LC_L4:
                 return "MPEG-H 3D Audio low complexity profile level 4";
+            case ENCODING_DTS_UHD:
+                return "DTS UHD";
             default:
                 return "Unknown surround sound format";
         }
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 7220379..5f60fb6 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -428,6 +428,8 @@
                 return "AUDIO_FORMAT_MAT_2_0"; // (MAT | MAT_SUB_2_0)
             case /* AUDIO_FORMAT_MAT_2_1           */ 0x24000003:
                 return "AUDIO_FORMAT_MAT_2_1"; // (MAT | MAT_SUB_2_1)
+            case /* AUDIO_FORMAT_DTS_UHD */           0x2E000000:
+                return "AUDIO_FORMAT_DTS_UHD";
             default:
                 return "AUDIO_FORMAT_(" + audioFormat + ")";
         }
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index e0f6379..7433cf9 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -432,14 +432,27 @@
                                      const ARect& destination, int32_t transform) {
     CHECK_NOT_NULL(aSurfaceTransaction);
     CHECK_NOT_NULL(aSurfaceControl);
-    CHECK_VALID_RECT(source);
     CHECK_VALID_RECT(destination);
 
+    Rect sourceRect = static_cast<const Rect&>(source);
+    // Adjust the source so its top and left are not negative
+    sourceRect.left = std::max(sourceRect.left, 0);
+    sourceRect.top = std::max(sourceRect.top, 0);
+    LOG_ALWAYS_FATAL_IF(sourceRect.isEmpty(), "invalid arg passed as source argument");
+
     sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
     Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
 
-    transaction->setCrop(surfaceControl, static_cast<const Rect&>(source));
-    transaction->setFrame(surfaceControl, static_cast<const Rect&>(destination));
+    transaction->setCrop(surfaceControl, sourceRect);
+
+    float dsdx = (destination.right - destination.left) /
+            static_cast<float>(sourceRect.right - sourceRect.left);
+    float dsdy = (destination.bottom - destination.top) /
+            static_cast<float>(sourceRect.bottom - sourceRect.top);
+
+    transaction->setPosition(surfaceControl, destination.left - (sourceRect.left * dsdx),
+                             destination.top - (sourceRect.top * dsdy));
+    transaction->setMatrix(surfaceControl, dsdx, 0, 0, dsdy);
     transaction->setTransform(surfaceControl, transform);
     bool transformToInverseDisplay = (NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY & transform) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
@@ -458,16 +471,18 @@
     transaction->setCrop(surfaceControl, static_cast<const Rect&>(source));
 }
 
-void ASurfaceTransaction_setPosition(ASurfaceTransaction* aSurfaceTransaction,
-                                     ASurfaceControl* aSurfaceControl, const ARect& destination) {
-    CHECK_NOT_NULL(aSurfaceTransaction);
+void ASurfaceTransaction_setPosition(ASurfaceTransaction* /* aSurfaceTransaction */,
+                                     ASurfaceControl* /* aSurfaceControl */,
+                                     const ARect& /* destination */) {
+    // TODO: Fix this function
+    /* CHECK_NOT_NULL(aSurfaceTransaction);
     CHECK_NOT_NULL(aSurfaceControl);
     CHECK_VALID_RECT(destination);
 
     sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
     Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
 
-    transaction->setFrame(surfaceControl, static_cast<const Rect&>(destination));
+    transaction->setFrame(surfaceControl, static_cast<const Rect&>(destination));*/
 }
 
 void ASurfaceTransaction_setTransform(ASurfaceTransaction* aSurfaceTransaction,
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index 9c77c85..ac5cb70 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -68,7 +68,6 @@
     method public boolean bindProcessToNetwork(@Nullable android.net.Network);
     method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork();
-    method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public android.net.Network getActiveNetworkForUid(int);
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo();
     method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo();
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks();
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 513b630..8dfdd614 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -26,6 +26,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void startCaptivePortalApp(@NonNull android.net.Network);
     method public void systemReady();
+    field public static final String ACTION_CLEAR_DNS_CACHE = "android.net.action.CLEAR_DNS_CACHE";
     field public static final String ACTION_PROMPT_LOST_VALIDATION = "android.net.action.PROMPT_LOST_VALIDATION";
     field public static final String ACTION_PROMPT_PARTIAL_CONNECTIVITY = "android.net.action.PROMPT_PARTIAL_CONNECTIVITY";
     field public static final String ACTION_PROMPT_UNVALIDATED = "android.net.action.PROMPT_UNVALIDATED";
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index b77d821..3a9731f 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -450,6 +450,15 @@
             "android.net.action.PROMPT_PARTIAL_CONNECTIVITY";
 
     /**
+     * Clear DNS Cache Action: This is broadcast when networks have changed and old
+     * DNS entries should be cleared.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final String ACTION_CLEAR_DNS_CACHE = "android.net.action.CLEAR_DNS_CACHE";
+
+    /**
      * Invalid tethering type.
      * @see #startTethering(int, boolean, OnStartTetheringCallback)
      * @hide
@@ -1193,7 +1202,8 @@
      *
      * @return a {@link Network} object for the current default network for the
      *         given UID or {@code null} if no default network is currently active
-     * TODO: b/183465229 Cleanup getActiveNetworkForUid once b/165835257 is fixed
+     *
+     * @hide
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     @Nullable
@@ -3475,6 +3485,8 @@
          * not include location sensitive info.
          * </p>
          */
+        // Note: Some existing fields which are location sensitive may still be included without
+        // this flag if the app targets SDK < S (to maintain backwards compatibility).
         public static final int FLAG_INCLUDE_LOCATION_INFO = 1 << 0;
 
         /** @hide */
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 9c4c0e2..c4277c3 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -600,8 +600,9 @@
         // TODO: Consider adding unwanted capabilities to the public API and mention this
         // in the documentation.
         checkValidCapability(capability);
-        mNetworkCapabilities |= 1 << capability;
-        mUnwantedNetworkCapabilities &= ~(1 << capability);  // remove from unwanted capability list
+        mNetworkCapabilities |= 1L << capability;
+        // remove from unwanted capability list
+        mUnwantedNetworkCapabilities &= ~(1L << capability);
         return this;
     }
 
@@ -620,8 +621,8 @@
      */
     public void addUnwantedCapability(@NetCapability int capability) {
         checkValidCapability(capability);
-        mUnwantedNetworkCapabilities |= 1 << capability;
-        mNetworkCapabilities &= ~(1 << capability);  // remove from requested capabilities
+        mUnwantedNetworkCapabilities |= 1L << capability;
+        mNetworkCapabilities &= ~(1L << capability);  // remove from requested capabilities
     }
 
     /**
@@ -634,7 +635,7 @@
      */
     public @NonNull NetworkCapabilities removeCapability(@NetCapability int capability) {
         checkValidCapability(capability);
-        final long mask = ~(1 << capability);
+        final long mask = ~(1L << capability);
         mNetworkCapabilities &= mask;
         return this;
     }
@@ -649,7 +650,7 @@
      */
     public @NonNull NetworkCapabilities removeUnwantedCapability(@NetCapability int capability) {
         checkValidCapability(capability);
-        mUnwantedNetworkCapabilities &= ~(1 << capability);
+        mUnwantedNetworkCapabilities &= ~(1L << capability);
         return this;
     }
 
@@ -717,14 +718,14 @@
      */
     public boolean hasCapability(@NetCapability int capability) {
         return isValidCapability(capability)
-                && ((mNetworkCapabilities & (1 << capability)) != 0);
+                && ((mNetworkCapabilities & (1L << capability)) != 0);
     }
 
     /** @hide */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public boolean hasUnwantedCapability(@NetCapability int capability) {
         return isValidCapability(capability)
-                && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0);
+                && ((mUnwantedNetworkCapabilities & (1L << capability)) != 0);
     }
 
     /**
@@ -1126,7 +1127,9 @@
      * app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. If the
      * app targets SDK version greater than or equal to {@link Build.VERSION_CODES#S}, then they
      * also need to use {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their
-     * callback. The app will be blamed for location access if this field is included.
+     * callback. If the apps targets SDK version equal to {{@link Build.VERSION_CODES#R}, this field
+     * will always be included. The app will be blamed for location access if this field is
+     * included.
      * </p>
      */
     public int getOwnerUid() {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 4578597..95f180a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -16,6 +16,8 @@
 */
 package com.android.packageinstaller;
 
+import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
+import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import android.Manifest;
@@ -50,6 +52,8 @@
 import com.android.internal.app.AlertActivity;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This activity is launched when a new application is installed via side loading
@@ -92,6 +96,12 @@
     private String mCallingAttributionTag;
     ApplicationInfo mSourceInfo;
 
+    /**
+     * A collection of unknown sources listeners that are actively listening for app ops mode
+     * changes
+     */
+    private List<UnknownSourcesListener> mActiveUnknownSourcesListeners = new ArrayList<>(1);
+
     // ApplicationInfo object primarily used for already existing applications
     private ApplicationInfo mAppInfo = null;
 
@@ -381,6 +391,14 @@
         outState.putBoolean(ALLOW_UNKNOWN_SOURCES_KEY, mAllowUnknownSources);
     }
 
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        while (!mActiveUnknownSourcesListeners.isEmpty()) {
+            unregister(mActiveUnknownSourcesListeners.get(0));
+        }
+    }
+
     private void bindUi() {
         mAlert.setIcon(mAppSnippet.icon);
         mAlert.setTitle(mAppSnippet.label);
@@ -707,24 +725,61 @@
         }
     }
 
+    private class UnknownSourcesListener implements AppOpsManager.OnOpChangedListener {
+
+        @Override
+        public void onOpChanged(String op, String packageName) {
+            if (!mOriginatingPackage.equals(packageName)) {
+                return;
+            }
+            unregister(this);
+            mActiveUnknownSourcesListeners.remove(this);
+            if (isDestroyed()) {
+                return;
+            }
+            getMainThreadHandler().postDelayed(() -> {
+                if (!isDestroyed()) {
+                    startActivity(getIntent().addFlags(FLAG_ACTIVITY_REORDER_TO_FRONT));
+                }
+            }, 500);
+
+        }
+
+    }
+
+    private void register(UnknownSourcesListener listener) {
+        mAppOpsManager.startWatchingMode(
+                AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES, mOriginatingPackage,
+                listener);
+        mActiveUnknownSourcesListeners.add(listener);
+    }
+
+    private void unregister(UnknownSourcesListener listener) {
+        mAppOpsManager.stopWatchingMode(listener);
+        mActiveUnknownSourcesListeners.remove(listener);
+    }
+
     /**
      * An error dialog shown when external sources are not allowed
      */
     public static class ExternalSourcesBlockedDialog extends AppErrorDialog {
         static AppErrorDialog newInstance(@NonNull String originationPkg) {
-            ExternalSourcesBlockedDialog dialog = new ExternalSourcesBlockedDialog();
+            ExternalSourcesBlockedDialog dialog =
+                    new ExternalSourcesBlockedDialog();
             dialog.setArgument(originationPkg);
             return dialog;
         }
 
         @Override
         protected Dialog createDialog(@NonNull CharSequence argument) {
+
+            final PackageInstallerActivity activity = (PackageInstallerActivity)getActivity();
             try {
-                PackageManager pm = getActivity().getPackageManager();
+                PackageManager pm = activity.getPackageManager();
 
                 ApplicationInfo sourceInfo = pm.getApplicationInfo(argument.toString(), 0);
 
-                return new AlertDialog.Builder(getActivity())
+                return new AlertDialog.Builder(activity)
                         .setTitle(pm.getApplicationLabel(sourceInfo))
                         .setIcon(pm.getApplicationIcon(sourceInfo))
                         .setMessage(R.string.untrusted_external_source_warning)
@@ -735,8 +790,10 @@
                                             Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
                                     final Uri packageUri = Uri.parse("package:" + argument);
                                     settingsIntent.setData(packageUri);
+                                    settingsIntent.setFlags(FLAG_ACTIVITY_NO_HISTORY);
                                     try {
-                                        getActivity().startActivityForResult(settingsIntent,
+                                        activity.register(activity.new UnknownSourcesListener());
+                                        activity.startActivityForResult(settingsIntent,
                                                 REQUEST_TRUST_EXTERNAL_SOURCE);
                                     } catch (ActivityNotFoundException exc) {
                                         Log.e(TAG, "Settings activity not found for action: "
@@ -744,11 +801,11 @@
                                     }
                                 })
                         .setNegativeButton(R.string.cancel,
-                                (dialog, which) -> getActivity().finish())
+                                (dialog, which) -> activity.finish())
                         .create();
             } catch (NameNotFoundException e) {
                 Log.e(TAG, "Did not find app info for " + argument);
-                getActivity().finish();
+                activity.finish();
                 return null;
             }
         }
diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
index b0a9b95..397b0f9 100644
--- a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
+++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
@@ -165,9 +165,8 @@
     }
 
     private List<EmergencyNumber> getPromotedEmergencyNumbers(int categories) {
-        // TODO(b/171542607): Use platform API when its bug is fixed.
-        Map<Integer, List<EmergencyNumber>> allLists = filterEmergencyNumbersByCategories(
-                mTelephonyManager.getEmergencyNumberList(), categories);
+        Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(
+                categories);
         if (allLists == null || allLists.isEmpty()) {
             Log.w(TAG, "Unable to retrieve emergency number lists!");
             return new ArrayList<>();
@@ -212,28 +211,4 @@
         }
         return promotedEmergencyNumberLists.get(SubscriptionManager.getDefaultSubscriptionId());
     }
-
-    /**
-     * Filter emergency numbers with categories.
-     */
-    private Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories(
-            Map<Integer, List<EmergencyNumber>> emergencyNumberList, int categories) {
-        Map<Integer, List<EmergencyNumber>> filteredMap = new ArrayMap<>();
-        if (emergencyNumberList == null) {
-            return filteredMap;
-        }
-        for (Integer subscriptionId : emergencyNumberList.keySet()) {
-            List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get(
-                    subscriptionId);
-            List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>();
-            for (EmergencyNumber number : allNumbersForSub) {
-                if (number.isInEmergencyServiceCategories(categories)) {
-                    numbersForCategoriesPerSub.add(number);
-                }
-            }
-            filteredMap.put(
-                    subscriptionId, numbersForCategoriesPerSub);
-        }
-        return filteredMap;
-    }
 }
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
new file mode 100644
index 0000000..a2bbd2b
--- /dev/null
+++ b/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
@@ -0,0 +1,19 @@
+<?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
+  -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
diff --git a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
index f99fda0..6560a18 100644
--- a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
+++ b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
@@ -23,6 +23,8 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
+import androidx.core.os.BuildCompat;
+
 import com.google.android.material.transition.platform.MaterialSharedAxis;
 import com.google.android.material.transition.platform.SlideDistanceProvider;
 
@@ -47,8 +49,7 @@
         transition.setDuration(DURATION);
 
         final Interpolator interpolator =
-                AnimationUtils.loadInterpolator(context,
-                        android.R.interpolator.fast_out_extra_slow_in);
+                AnimationUtils.loadInterpolator(context, R.interpolator.fast_out_extra_slow_in);
         transition.setInterpolator(interpolator);
 
         // TODO(b/177480673): Update fade through threshold once (cl/362065364) is released
@@ -64,6 +65,9 @@
      * triggered when the page is launched/entering.
      */
     public static void applyForwardTransition(Activity activity) {
+        if (!BuildCompat.isAtLeastS()) {
+            return;
+        }
         if (activity == null) {
             Log.w(TAG, "applyForwardTransition: Invalid activity!");
             return;
@@ -87,6 +91,9 @@
      * previously-started Activity.
      */
     public static void applyBackwardTransition(Activity activity) {
+        if (!BuildCompat.isAtLeastS()) {
+            return;
+        }
         if (activity == null) {
             Log.w(TAG, "applyBackwardTransition: Invalid activity!");
             return;
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
index 0a70f72..673f243 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -41,6 +41,7 @@
     private static final long ONE_DAY_MILLIS = TimeUnit.DAYS.toMillis(1);
     private static final long TWO_DAYS_MILLIS = TimeUnit.DAYS.toMillis(2);
     private static final long ONE_HOUR_MILLIS = TimeUnit.HOURS.toMillis(1);
+    private static final long ONE_MIN_MILLIS = TimeUnit.MINUTES.toMillis(1);
 
     /**
      * This method produces the text used in various places throughout the system to describe the
@@ -63,7 +64,7 @@
                 // show a less than 15 min remaining warning if appropriate
                 CharSequence timeString = StringUtil.formatElapsedTime(context,
                         FIFTEEN_MINUTES_MILLIS,
-                        false /* withSeconds */);
+                        false /* withSeconds */, false /* collapseTimeUnit */);
                 return getUnderFifteenString(context, timeString, percentageString);
             } else if (drainTimeMs >= TWO_DAYS_MILLIS) {
                 // just say more than two day if over 48 hours
@@ -151,7 +152,7 @@
         final long roundedTimeMs = roundTimeToNearestThreshold(drainTimeMs, ONE_HOUR_MILLIS);
         CharSequence timeString = StringUtil.formatElapsedTime(context,
                 roundedTimeMs,
-                false /* withSeconds */);
+                false /* withSeconds */, true /* collapseTimeUnit */);
 
         if (TextUtils.isEmpty(percentageString)) {
             int id = basedOnUsage
@@ -170,7 +171,7 @@
             int resId) {
         final long roundedTimeMs = roundTimeToNearestThreshold(drainTimeMs, ONE_HOUR_MILLIS);
         CharSequence timeString = StringUtil.formatElapsedTime(context, roundedTimeMs,
-                false /* withSeconds */);
+                false /* withSeconds */, false /* collapseTimeUnit */);
 
         return context.getString(resId, timeString);
     }
@@ -193,17 +194,18 @@
     private static String getRegularTimeRemainingString(Context context, long drainTimeMs,
             String percentageString, boolean basedOnUsage) {
 
-        CharSequence timeString = getDateTimeStringFromMs(context, drainTimeMs);
+        CharSequence timeString = StringUtil.formatElapsedTime(context,
+                drainTimeMs, false /* withSeconds */, true /* collapseTimeUnit */);
 
         if (TextUtils.isEmpty(percentageString)) {
             int id = basedOnUsage
-                    ? R.string.power_discharge_by_only_enhanced
-                    : R.string.power_discharge_by_only;
+                    ? R.string.power_remaining_duration_only_enhanced
+                    : R.string.power_remaining_duration_only;
             return context.getString(id, timeString);
         } else {
             int id = basedOnUsage
-                    ? R.string.power_discharge_by_enhanced
-                    : R.string.power_discharge_by;
+                    ? R.string.power_discharging_duration_enhanced
+                    : R.string.power_discharging_duration;
             return context.getString(id, timeString, percentageString);
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
index 83ef4f9..b65637f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -40,6 +40,8 @@
     public static final int SECONDS_PER_HOUR = 60 * 60;
     public static final int SECONDS_PER_DAY = 24 * 60 * 60;
 
+    private static final int LIMITED_TIME_UNIT_COUNT = 2;
+
     /**
      * Returns elapsed time for the given millis, in the following format:
      * 2 days, 5 hr, 40 min, 29 sec
@@ -47,10 +49,12 @@
      * @param context     the application context
      * @param millis      the elapsed time in milli seconds
      * @param withSeconds include seconds?
+     * @param collapseTimeUnit limit the output to top 2 time unit
+     *                         e.g 2 days, 5 hr, 40 min, 29 sec will convert to 2 days, 5 hr
      * @return the formatted elapsed time
      */
     public static CharSequence formatElapsedTime(Context context, double millis,
-            boolean withSeconds) {
+            boolean withSeconds, boolean collapseTimeUnit) {
         SpannableStringBuilder sb = new SpannableStringBuilder();
         int seconds = (int) Math.floor(millis / 1000);
         if (!withSeconds) {
@@ -89,6 +93,12 @@
             // Everything addable was zero, so nothing was added. We add a zero.
             measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE));
         }
+
+        if (collapseTimeUnit && measureList.size() > LIMITED_TIME_UNIT_COUNT) {
+            // Limit the output to top 2 time unit.
+            measureList.subList(LIMITED_TIME_UNIT_COUNT, measureList.size()).clear();
+        }
+
         final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]);
 
         final Locale locale = context.getResources().getConfiguration().locale;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
index 695b5b6..40339de 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
@@ -23,6 +23,7 @@
 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.anyString;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.mock;
@@ -126,7 +127,7 @@
         List<EmergencyNumber> numbersForSubId = new ArrayList<>();
         numbersForSubId.add(emergencyNumber);
         numbers.put(subId, numbersForSubId);
-        when(mTelephonyManager.getEmergencyNumberList()).thenReturn(numbers);
+        when(mTelephonyManager.getEmergencyNumberList(anyInt())).thenReturn(numbers);
         when(emergencyNumber.getNumber()).thenReturn(TELEPHONY_EMERGENCY_NUMBER);
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 4b779ac..3029736 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -35,10 +35,12 @@
 @RunWith(RobolectricTestRunner.class)
 public class PowerUtilTest {
     private static final String TEST_BATTERY_LEVEL_10 = "10%";
+    private static final long TEN_SEC_MILLIS = Duration.ofSeconds(10).toMillis();
     private static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
     private static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
     private static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
     private static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
+    private static final long TEN_HOURS_MILLIS = Duration.ofHours(10).toMillis();
     private static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
     private static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about";
     private static final String ENHANCED_SUFFIX = " based on your usage";
@@ -152,11 +154,40 @@
                 THIRTY_HOURS_MILLIS,
                 TEST_BATTERY_LEVEL_10 /* percentageString */,
                 false /* basedOnUsage */);
+        String info3 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                THIRTY_HOURS_MILLIS + TEN_MINUTES_MILLIS,
+                null /* percentageString */,
+                false /* basedOnUsage */);
 
         // We only add special mention for the long string
         assertThat(info).isEqualTo("About 1 day, 6 hr left based on your usage");
         // shortened string should not have extra text
         assertThat(info2).isEqualTo("About 1 day, 6 hr left (10%)");
+        // present 2 time unit at most
+        assertThat(info3).isEqualTo("About 1 day, 6 hr left");
+    }
+
+    @Test
+    public void testGetBatteryRemainingStringFormatted_lessThanOneDay_usesCorrectString() {
+        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                TEN_HOURS_MILLIS,
+                null /* percentageString */,
+                true /* basedOnUsage */);
+        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                TEN_HOURS_MILLIS,
+                TEST_BATTERY_LEVEL_10 /* percentageString */,
+                false /* basedOnUsage */);
+        String info3 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                TEN_HOURS_MILLIS + TEN_MINUTES_MILLIS + TEN_SEC_MILLIS,
+                null /* percentageString */,
+                false /* basedOnUsage */);
+
+        // We only add special mention for the long string
+        assertThat(info).isEqualTo("About 10 hr left based on your usage");
+        // shortened string should not have extra text
+        assertThat(info2).isEqualTo("About 10 hr left (10%)");
+        // present 2 time unit at most
+        assertThat(info3).isEqualTo("About 10 hr, 10 min left");
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
index b503972..6a1d326 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -46,7 +46,7 @@
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
         final String expectedTime = "5 min, 30 sec";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true, false).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -55,7 +55,7 @@
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
         final String expectedTime = "6 min";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false, false).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -65,7 +65,17 @@
                 + 4 * DateUtils.HOUR_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
         final String expectedTime = "2 days, 4 hr, 15 min";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false, false).toString())
+                .isEqualTo(expectedTime);
+    }
+
+    @Test
+    public void testFormatElapsedTime_TimeMoreThanOneDayAndCollapseTimeUnit_ShowCorrectly() {
+        final double testMillis = 2 * DateUtils.DAY_IN_MILLIS
+                + 4 * DateUtils.HOUR_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
+        final String expectedTime = "2 days, 4 hr";
+
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false, true).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -74,7 +84,7 @@
         final double testMillis = 2 * DateUtils.DAY_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
         final String expectedTime = "2 days, 15 min";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false, false).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -83,7 +93,7 @@
         final double testMillis = 0;
         final String expectedTime = "0 sec";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true, false).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -92,7 +102,7 @@
         final double testMillis = 0;
         final String expectedTime = "0 min";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false, false).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -101,7 +111,7 @@
         final double testMillis = 15 * DateUtils.MINUTE_IN_MILLIS;
 
         final CharSequence charSequence =
-                StringUtil.formatElapsedTime(mContext, testMillis, false);
+                StringUtil.formatElapsedTime(mContext, testMillis, false, false);
         assertThat(charSequence).isInstanceOf(SpannableStringBuilder.class);
 
         final SpannableStringBuilder expectedString = (SpannableStringBuilder) charSequence;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5a3298d..16937b7 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -176,6 +176,7 @@
     <uses-permission android:name="android.permission.SET_TIME_ZONE" />
     <uses-permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS" />
     <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
+    <uses-permission android:name="android.permission.BYPASS_ROLE_QUALIFICATION" />
     <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" />
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
     <uses-permission android:name="android.permission.STATUS_BAR" />
@@ -265,10 +266,11 @@
     <!-- permissions required for CTS test - PhoneStateListenerTest -->
     <uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" />
 
-    <!-- Permissions required for ganting and logging -->
+    <!-- Permissions required for granting and logging -->
     <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
     <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
     <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/>
+    <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
 
     <!-- Permission required for CTS test - BatterySaverTest -->
     <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 85ecb1c..88d7710 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -275,6 +275,8 @@
     <!-- Permission to make accessibility service access Bubbles -->
     <uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" />
 
+    <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" />
+
 
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 6c7a5b8..0d18b8d 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -24,6 +24,30 @@
             "exclude-annotation": "android.platform.test.annotations.Postsubmit"
         }
       ]
+    },
+    {
+      // Permission indicators
+      "name": "CtsPermission4TestCases",
+      "options": [
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      // Permission indicators
+      "name": "CtsVoiceRecognitionTestCases",
+      "options": [
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
     }
   ],
 
diff --git a/packages/SystemUI/docs/falsing.md b/packages/SystemUI/docs/falsing.md
index 09215ac..0a60270 100644
--- a/packages/SystemUI/docs/falsing.md
+++ b/packages/SystemUI/docs/falsing.md
@@ -70,17 +70,17 @@
 
 ### Single Tap
 
-`FalsingManager#isFalseTap(boolean robustCheck, double falsePenalty)`. This
-method tells the `FalsingManager` that you want to validate a single tap. It
+`FalsingManager#isSimpleTape()`. This method
+performs a only very basic checking, checking that observed `MotionEvent`s are
+all within some small x & y region ("touch slop"). Useful for only the most simple of scenarios,
+you probably want `FalsingManager#isFalseTap` method for most cases.
+
+`FalsingManager#isFalseTap(@Penalty int penalty)`. This
+method tells the `FalsingManager` that you want to thoroughly validate a single tap. It
 returns true if it thinks the tap should be rejected (i.e. the tap looks more
 like a swipe) and false otherwise.
 
-`robustCheck` determines what heuristics are used. If set to false, the method
-performs a only very basic checking, checking that observed `MotionEvent`s are
-all within some small x & y region ("touch slop").
-
-When `robustCheck` is set to true, several more advanced rules are additionally
-applied:
+It runs through the following heuristics to validate a tap:
 
 1.  If the device recognizes a face (i.e. face-auth) the tap is **accepted**.
 2.  If the tap is the _second_ tap in recent history and looks like a valid Double Tap
@@ -90,19 +90,18 @@
 4.  Otherwise the tap is **accepted**.
 
 All the above rules are applied only after first confirming the gesture does
-in fact look like a basic tap.
+in fact look like a simple tap.
 
-`falsePenalty` is a measure of how much the `HistoryTracker`'s belief should be
+`penalty` is a measure of how much the `HistoryTracker`'s belief should be
 penalized in the event that the tap is rejected. This value is only used if
-`robustCheck` is set to true.
+the gesture fails to validate as a simple tap.
 
-A value of `0` means no change in belief. A value of `1` means a _very_ strong
-confidence in a false tap. In general, as a single tap on the screen is not
-verifiable, a small value should be supplied - on the order of `0.1`. Pass `0`
-if you don't want to penalize belief at all. Pass a higher value
-the earlier in the UX flow your interaction occurs. Once an owner is farther
-along in a UX flow (multiple taps or swipes), its safer to assume that a single
-accidental tap should cause less of a penalty.
+The `@FalsingManager.Penalty` values are fairly straightforward, but note that you
+should generally be choosing `LOW_PENALTY`. It is inherently difficult to know if a
+tap is truly false or not, so a single mis-tap should apply only a small penalty.
+If the owner is further along in a UX flow, and is still mis-tapping, it may make more
+sense to increase the penalty as mis-taps should be less likely to occur after
+several successful gestures.
 
 ### Double Tap
 
diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp
index d6204db..b3aba22 100644
--- a/packages/SystemUI/plugin/Android.bp
+++ b/packages/SystemUI/plugin/Android.bp
@@ -27,6 +27,7 @@
 
     srcs: [
         "src/**/*.java",
+        "src/**/*.kt",
         "bcsmartspace/src/**/*.java",
     ],
 
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index f8a9a045..35423a9 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -16,7 +16,9 @@
 
 package com.android.systemui.plugins;
 
+import android.app.smartspace.SmartspaceTarget;
 import android.os.Parcelable;
+import android.view.ViewGroup;
 
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
@@ -36,9 +38,25 @@
     /** Unregister a listener. */
     void unregisterListener(SmartspaceTargetListener listener);
 
+    /**
+     * Create a view to be shown within the parent. Do not add the view, as the parent
+     * will be responsible for correctly setting the LayoutParams
+     */
+    default SmartspaceView getView(ViewGroup parent) {
+        return null;
+    }
+
+    /** Updates Smartspace data and propagates it to any listeners. */
+    void onTargetsAvailable(List<SmartspaceTarget> targets);
+
     /** Provides Smartspace data to registered listeners. */
     interface SmartspaceTargetListener {
         /** Each Parcelable is a SmartspaceTarget that represents a card. */
         void onSmartspaceTargetsUpdated(List<? extends Parcelable> targets);
     }
+
+    /** View to which this plugin can be registered, in order to get updates. */
+    interface SmartspaceView {
+        void registerDataProvider(BcSmartspaceDataPlugin plugin);
+    }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 25a3fa2..055fe37 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -19,6 +19,7 @@
 import android.content.Intent;
 import android.view.View;
 
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
 /**
@@ -44,7 +45,15 @@
      * specifies an associated view that should be used for the activity launch animation.
      */
     void startPendingIntentDismissingKeyguard(PendingIntent intent,
-            Runnable intentSentUiThreadCallback, View associatedView);
+            Runnable intentSentUiThreadCallback, @Nullable View associatedView);
+
+    /**
+     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but also
+     * specifies an animation controller that should be used for the activity launch animation.
+     */
+    void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentUiThreadCallback,
+            @Nullable ActivityLaunchAnimator.Controller animationController);
 
     /**
      * The intent flag can be specified in startActivity().
@@ -54,7 +63,17 @@
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
     void startActivity(Intent intent, boolean dismissShade, Callback callback);
     void postStartActivityDismissingKeyguard(Intent intent, int delay);
+    void postStartActivityDismissingKeyguard(Intent intent, int delay,
+            @Nullable ActivityLaunchAnimator.Controller animationController);
     void postStartActivityDismissingKeyguard(PendingIntent intent);
+
+    /**
+     * Similar to {@link #postStartActivityDismissingKeyguard(PendingIntent)}, but also specifies an
+     * animation controller that should be used for the activity launch animation.
+     */
+    void postStartActivityDismissingKeyguard(PendingIntent intent,
+            @Nullable ActivityLaunchAnimator.Controller animationController);
+
     void postQSRunnableDismissingKeyguard(Runnable runnable);
 
     void dismissKeyguardThenExecute(OnDismissAction action, @Nullable Runnable cancel,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 1fde6c9..4142e51 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.plugins;
 
+import android.annotation.IntDef;
 import android.net.Uri;
 import android.view.MotionEvent;
 
@@ -24,6 +25,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Interface that decides whether a touch on the phone was accidental. i.e. Pocket Dialing.
@@ -34,6 +37,20 @@
 public interface FalsingManager {
     int VERSION = 6;
 
+    int NO_PENALTY = 0;
+    int LOW_PENALTY = 1;
+    int MODERATE_PENALTY = 2;
+    int HIGH_PENALTY = 3;
+
+    @IntDef({
+            NO_PENALTY,
+            LOW_PENALTY,
+            MODERATE_PENALTY,
+            HIGH_PENALTY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Penalty {}
+
     void onSuccessfulUnlock();
 
     boolean isUnlockingDisabled();
@@ -41,23 +58,31 @@
     /** Returns true if the gesture should be rejected. */
     boolean isFalseTouch(int interactionType);
 
+
     /**
-     * Returns true if the FalsingManager thinks the last gesure was not a valid tap.
+     * Does basic checking to see if gesture looks like a tap.
      *
-     * The first parameter, robustCheck, distinctly changes behavior. When set to false,
-     * this method simply looks at the last gesture and returns whether it is a tap or not, (as
-     * opposed to a swipe or other non-tap gesture). When set to true, a more thorough analysis
-     * is performed that can include historical interactions and other contextual cues to see
+     * Only does the most basic of checks. No penalty is applied if this method returns false.
+     *
+     * For more robust analysis, use {@link #isFalseTap(int)}.
+     */
+    boolean isSimpleTap();
+
+    /**
+     * Returns true if the FalsingManager thinks the last gesture was not a valid tap.
+     *
+     * This method runs a more thorough analysis than the similar {@link #isSimpleTap()},
+     * that can include historical interactions and other contextual cues to see
      * if the tap looks accidental.
      *
-     * Set robustCheck to true if you want to validate a tap for launching an action, like opening
-     * a notification. Set to false if you simply want to know if the last gesture looked like a
-     * tap.
+     * Use this method to validate a tap for launching an action, like opening
+     * a notification.
      *
-     * The second parameter, falsePenalty, indicates how much this should affect future gesture
-     * classifications if this tap looks like a false.
+     * The only parameter, penalty, indicates how much this should affect future gesture
+     * classifications if this tap looks like a false. As single taps are hard to confirm as false
+     * or otherwise, a low penalty value is encouraged unless context indicates otherwise.
      */
-    boolean isFalseTap(boolean robustCheck, double falsePenalty);
+    boolean isFalseTap(@Penalty int penalty);
 
     /**
      * Returns true if the last two gestures do not look like a double tap.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/ActivityLaunchAnimator.kt
new file mode 100644
index 0000000..5af8dab
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/ActivityLaunchAnimator.kt
@@ -0,0 +1,479 @@
+package com.android.systemui.plugins.animation
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.app.ActivityManager
+import android.app.PendingIntent
+import android.graphics.Matrix
+import android.graphics.Rect
+import android.os.RemoteException
+import android.util.MathUtils
+import android.view.IRemoteAnimationFinishedCallback
+import android.view.IRemoteAnimationRunner
+import android.view.RemoteAnimationAdapter
+import android.view.RemoteAnimationTarget
+import android.view.SyncRtSurfaceTransactionApplier
+import android.view.View
+import android.view.WindowManager
+import android.view.animation.LinearInterpolator
+import android.view.animation.PathInterpolator
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.policy.ScreenDecorationsUtils
+import kotlin.math.roundToInt
+
+/**
+ * A class that allows activities to be started in a seamless way from a view that is transforming
+ * nicely into the starting window.
+ */
+class ActivityLaunchAnimator {
+    companion object {
+        const val ANIMATION_DURATION = 400L
+        const val ANIMATION_DURATION_FADE_OUT_CONTENT = 67L
+        const val ANIMATION_DURATION_FADE_IN_WINDOW = 200L
+        private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
+        private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
+        private const val ANIMATION_DELAY_NAV_FADE_IN =
+                ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN
+        private const val LAUNCH_TIMEOUT = 1000L
+
+        // TODO(b/184121838): Use android.R.interpolator.fast_out_extra_slow_in instead.
+        // TODO(b/184121838): Move com.android.systemui.Interpolators in an animation library we can
+        // reuse here.
+        private val ANIMATION_INTERPOLATOR = PathInterpolator(0.4f, 0f, 0.2f, 1f)
+        private val LINEAR_INTERPOLATOR = LinearInterpolator()
+        private val ALPHA_IN_INTERPOLATOR = PathInterpolator(0.4f, 0f, 1f, 1f)
+        private val ALPHA_OUT_INTERPOLATOR = PathInterpolator(0f, 0f, 0.8f, 1f)
+        private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f)
+        private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
+
+        /**
+         * Given the [linearProgress] of a launch animation, return the linear progress of the
+         * sub-animation starting [delay] ms after the launch animation and that lasts [duration].
+         */
+        @JvmStatic
+        fun getProgress(linearProgress: Float, delay: Long, duration: Long): Float {
+            return MathUtils.constrain(
+                    (linearProgress * ANIMATION_DURATION - delay) / duration,
+                    0.0f,
+                    1.0f
+            )
+        }
+    }
+
+    /**
+     * Start an intent and animate the opening window. The intent will be started by running
+     * [intentStarter], which should use the provided [RemoteAnimationAdapter] and return the launch
+     * result. [controller] is responsible from animating the view from which the intent was started
+     * in [Controller.onLaunchAnimationProgress]. No animation will start if there is no window
+     * opening.
+     *
+     * If [controller] is null, then the intent will be started and no animation will run.
+     *
+     * This method will throw any exception thrown by [intentStarter].
+     */
+    inline fun startIntentWithAnimation(
+        controller: Controller?,
+        intentStarter: (RemoteAnimationAdapter?) -> Int
+    ) {
+        if (controller == null) {
+            intentStarter(null)
+            return
+        }
+
+        val runner = Runner(controller)
+        val animationAdapter = RemoteAnimationAdapter(
+            runner,
+            ANIMATION_DURATION,
+            ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
+        )
+        val launchResult = intentStarter(animationAdapter)
+        val willAnimate = launchResult == ActivityManager.START_TASK_TO_FRONT ||
+            launchResult == ActivityManager.START_SUCCESS
+        runner.context.mainExecutor.execute { controller.onIntentStarted(willAnimate) }
+
+        // If we expect an animation, post a timeout to cancel it in case the remote animation is
+        // never started.
+        if (willAnimate) {
+            runner.postTimeout()
+        }
+    }
+
+    /**
+     * Same as [startIntentWithAnimation] but allows [intentStarter] to throw a
+     * [PendingIntent.CanceledException] which must then be handled by the caller. This is useful
+     * for Java caller starting a [PendingIntent].
+     */
+    @Throws(PendingIntent.CanceledException::class)
+    fun startPendingIntentWithAnimation(
+        controller: Controller?,
+        intentStarter: PendingIntentStarter
+    ) {
+        startIntentWithAnimation(controller) { intentStarter.startPendingIntent(it) }
+    }
+
+    interface PendingIntentStarter {
+        /**
+         * Start a pending intent using the provided [animationAdapter] and return the launch
+         * result.
+         */
+        @Throws(PendingIntent.CanceledException::class)
+        fun startPendingIntent(animationAdapter: RemoteAnimationAdapter?): Int
+    }
+
+    /**
+     * A controller that takes care of applying the animation to an expanding view.
+     *
+     * Note that all callbacks (onXXX methods) are all called on the main thread.
+     */
+    interface Controller {
+        companion object {
+            /**
+             * Return a [Controller] that will animate and expand [view] into the opening window.
+             *
+             * Important: The view must be attached to the window when calling this function and
+             * during the animation.
+             */
+            @JvmStatic
+            fun fromView(view: View): Controller = GhostedViewLaunchAnimatorController(view)
+        }
+
+        /**
+         * Return the root [View] that contains the view that started the intent and will be
+         * animating together with the window.
+         *
+         * This view will be used to:
+         *  - Get the associated [Context].
+         *  - Compute whether we are expanding fully above the current window.
+         *  - Apply surface transactions in sync with RenderThread.
+         */
+        fun getRootView(): View
+
+        /**
+         * Return the [State] of the view that will be animated. We will animate from this state to
+         * the final window state.
+         *
+         * Note: This state will be mutated and passed to [onLaunchAnimationProgress] during the
+         * animation.
+         */
+        fun createAnimatorState(): State
+
+        /**
+         * The intent was started. If [willAnimate] is false, nothing else will happen and the
+         * animation will not be started.
+         */
+        fun onIntentStarted(willAnimate: Boolean) {}
+
+        /**
+         * The animation started. This is typically used to initialize any additional resource
+         * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding
+         * fully above the [root view][getRootView].
+         */
+        fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {}
+
+        /** The animation made progress and the expandable view [state] should be updated. */
+        fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {}
+
+        /**
+         * The animation ended. This will be called *if and only if* [onLaunchAnimationStart] was
+         * called previously. This is typically used to clean up the resources initialized when the
+         * animation was started.
+         */
+        fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {}
+
+        /**
+         * The animation was cancelled remotely. Note that [onLaunchAnimationEnd] will still be
+         * called after this if the animation was already started, i.e. if [onLaunchAnimationStart]
+         * was called before the cancellation.
+         */
+        fun onLaunchAnimationCancelled() {}
+
+        /**
+         * The remote animation was not started within the expected time. It timed out and will
+         * never [start][onLaunchAnimationStart].
+         */
+        fun onLaunchAnimationTimedOut() {}
+
+        /**
+         * The animation was aborted because the opening window was not found. It will never
+         * [start][onLaunchAnimationStart].
+         */
+        fun onLaunchAnimationAborted() {}
+    }
+
+    /** The state of an expandable view during an [ActivityLaunchAnimator] animation. */
+    open class State(
+        /** The position of the view in screen space coordinates. */
+        var top: Int,
+        var bottom: Int,
+        var left: Int,
+        var right: Int,
+
+        var topCornerRadius: Float = 0f,
+        var bottomCornerRadius: Float = 0f,
+
+        var contentAlpha: Float = 1f,
+        var backgroundAlpha: Float = 1f
+    ) {
+        private val startTop = top
+        private val startLeft = left
+        private val startRight = right
+
+        val width: Int
+            get() = right - left
+
+        val height: Int
+            get() = bottom - top
+
+        open val topChange: Int
+            get() = top - startTop
+
+        val leftChange: Int
+            get() = left - startLeft
+
+        val rightChange: Int
+            get() = right - startRight
+    }
+
+    @VisibleForTesting
+    class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() {
+        private val rootView = controller.getRootView()
+        @PublishedApi internal val context = rootView.context
+        private val transactionApplier = SyncRtSurfaceTransactionApplier(rootView)
+        private var animator: ValueAnimator? = null
+
+        private var windowCrop = Rect()
+        private var timedOut = false
+        private var cancelled = false
+
+        // A timeout to cancel the remote animation if it is not started within X milliseconds after
+        // the intent was started.
+        //
+        // Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise
+        // it will be automatically converted when posted and we wouldn't be able to remove it after
+        // posting it.
+        private var onTimeout = Runnable { onAnimationTimedOut() }
+
+        @PublishedApi
+        internal fun postTimeout() {
+            rootView.postDelayed(onTimeout, LAUNCH_TIMEOUT)
+        }
+
+        private fun removeTimeout() {
+            rootView.removeCallbacks(onTimeout)
+        }
+
+        override fun onAnimationStart(
+            @WindowManager.TransitionOldType transit: Int,
+            remoteAnimationTargets: Array<out RemoteAnimationTarget>,
+            remoteAnimationWallpaperTargets: Array<out RemoteAnimationTarget>,
+            remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>,
+            iRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback
+        ) {
+            removeTimeout()
+
+            // The animation was started too late and we already notified the controller that it
+            // timed out.
+            if (timedOut) {
+                invokeCallback(iRemoteAnimationFinishedCallback)
+                return
+            }
+
+            // This should not happen, but let's make sure we don't start the animation if it was
+            // cancelled before and we already notified the controller.
+            if (cancelled) {
+                return
+            }
+
+            context.mainExecutor.execute {
+                startAnimation(remoteAnimationTargets, iRemoteAnimationFinishedCallback)
+            }
+        }
+
+        private fun startAnimation(
+            remoteAnimationTargets: Array<out RemoteAnimationTarget>,
+            iCallback: IRemoteAnimationFinishedCallback
+        ) {
+            val window = remoteAnimationTargets.firstOrNull {
+                it.mode == RemoteAnimationTarget.MODE_OPENING
+            }
+
+            if (window == null) {
+                removeTimeout()
+                invokeCallback(iCallback)
+                controller.onLaunchAnimationAborted()
+                return
+            }
+
+            val navigationBar = remoteAnimationTargets.firstOrNull {
+                it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
+            }
+
+            // Start state.
+            val state = controller.createAnimatorState()
+
+            val startTop = state.top
+            val startBottom = state.bottom
+            val startLeft = state.left
+            val startRight = state.right
+
+            val startTopCornerRadius = state.topCornerRadius
+            val startBottomCornerRadius = state.bottomCornerRadius
+
+            // End state.
+            val windowBounds = window.screenSpaceBounds
+            val endTop = windowBounds.top
+            val endBottom = windowBounds.bottom
+            val endLeft = windowBounds.left
+            val endRight = windowBounds.right
+
+            // TODO(b/184121838): Ensure that we are launching on the same screen.
+            val rootViewLocation = rootView.locationOnScreen
+            val isExpandingFullyAbove = endTop <= rootViewLocation[1] &&
+                endBottom >= rootViewLocation[1] + rootView.height &&
+                endLeft <= rootViewLocation[0] &&
+                endRight >= rootViewLocation[0] + rootView.width
+
+            // TODO(b/184121838): We should somehow get the top and bottom radius of the window.
+            val endRadius = if (isExpandingFullyAbove) {
+                // Most of the time, expanding fully above the root view means expanding in full
+                // screen.
+                ScreenDecorationsUtils.getWindowCornerRadius(context.resources)
+            } else {
+                // This usually means we are in split screen mode, so 2 out of 4 corners will have
+                // a radius of 0.
+                0f
+            }
+
+            // Update state.
+            val animator = ValueAnimator.ofFloat(0f, 1f)
+            this.animator = animator
+            animator.duration = ANIMATION_DURATION
+            animator.interpolator = LINEAR_INTERPOLATOR
+
+            animator.addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
+                    controller.onLaunchAnimationStart(isExpandingFullyAbove)
+                }
+
+                override fun onAnimationEnd(animation: Animator?) {
+                    invokeCallback(iCallback)
+                    controller.onLaunchAnimationEnd(isExpandingFullyAbove)
+                }
+            })
+
+            animator.addUpdateListener { animation ->
+                if (cancelled) {
+                    return@addUpdateListener
+                }
+
+                val linearProgress = animation.animatedFraction
+                val progress = ANIMATION_INTERPOLATOR.getInterpolation(linearProgress)
+
+                state.top = lerp(startTop, endTop, progress).roundToInt()
+                state.bottom = lerp(startBottom, endBottom, progress).roundToInt()
+                state.left = lerp(startLeft, endLeft, progress).roundToInt()
+                state.right = lerp(startRight, endRight, progress).roundToInt()
+
+                state.topCornerRadius = MathUtils.lerp(startTopCornerRadius, endRadius, progress)
+                state.bottomCornerRadius =
+                    MathUtils.lerp(startBottomCornerRadius, endRadius, progress)
+
+                val contentAlphaProgress = getProgress(linearProgress, 0,
+                        ANIMATION_DURATION_FADE_OUT_CONTENT)
+                state.contentAlpha =
+                        1 - ALPHA_OUT_INTERPOLATOR.getInterpolation(contentAlphaProgress)
+
+                val backgroundAlphaProgress = getProgress(linearProgress,
+                        ANIMATION_DURATION_FADE_OUT_CONTENT, ANIMATION_DURATION_FADE_IN_WINDOW)
+                state.backgroundAlpha =
+                        1 - ALPHA_IN_INTERPOLATOR.getInterpolation(backgroundAlphaProgress)
+
+                applyStateToWindow(window, state)
+                navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
+                controller.onLaunchAnimationProgress(state, progress, linearProgress)
+            }
+
+            animator.start()
+        }
+
+        private fun applyStateToWindow(window: RemoteAnimationTarget, state: State) {
+            val m = Matrix()
+            m.postTranslate(0f, (state.top - window.sourceContainerBounds.top).toFloat())
+            windowCrop.set(state.left, 0, state.right, state.height)
+
+            val cornerRadius = minOf(state.topCornerRadius, state.bottomCornerRadius)
+            val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash)
+                    .withAlpha(1f)
+                    .withMatrix(m)
+                    .withWindowCrop(windowCrop)
+                    .withLayer(window.prefixOrderIndex)
+                    .withCornerRadius(cornerRadius)
+                    .withVisibility(true)
+                    .build()
+
+            transactionApplier.scheduleApply(params)
+        }
+
+        private fun applyStateToNavigationBar(
+            navigationBar: RemoteAnimationTarget,
+            state: State,
+            linearProgress: Float
+        ) {
+            val fadeInProgress = getProgress(linearProgress, ANIMATION_DELAY_NAV_FADE_IN,
+                    ANIMATION_DURATION_NAV_FADE_OUT)
+
+            val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(navigationBar.leash)
+            if (fadeInProgress > 0) {
+                val m = Matrix()
+                m.postTranslate(0f, (state.top - navigationBar.sourceContainerBounds.top).toFloat())
+                windowCrop.set(state.left, 0, state.right, state.height)
+                params
+                        .withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress))
+                        .withMatrix(m)
+                        .withWindowCrop(windowCrop)
+                        .withVisibility(true)
+            } else {
+                val fadeOutProgress = getProgress(linearProgress, 0,
+                        ANIMATION_DURATION_NAV_FADE_OUT)
+                params.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress))
+            }
+
+            transactionApplier.scheduleApply(params.build())
+        }
+
+        private fun onAnimationTimedOut() {
+            if (cancelled) {
+                return
+            }
+
+            timedOut = true
+            controller.onLaunchAnimationTimedOut()
+        }
+
+        override fun onAnimationCancelled() {
+            if (timedOut) {
+                return
+            }
+
+            cancelled = true
+            removeTimeout()
+            context.mainExecutor.execute {
+                animator?.cancel()
+                controller.onLaunchAnimationCancelled()
+            }
+        }
+
+        private fun invokeCallback(iCallback: IRemoteAnimationFinishedCallback) {
+            try {
+                iCallback.onAnimationFinished()
+            } catch (e: RemoteException) {
+                e.printStackTrace()
+            }
+        }
+
+        private fun lerp(start: Int, stop: Int, amount: Float): Float {
+            return MathUtils.lerp(start.toFloat(), stop.toFloat(), amount)
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/GhostedViewLaunchAnimatorController.kt
new file mode 100644
index 0000000..a5494ad
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/GhostedViewLaunchAnimatorController.kt
@@ -0,0 +1,329 @@
+package com.android.systemui.plugins.animation
+
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.PixelFormat
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffXfermode
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.LayerDrawable
+import android.view.GhostView
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+
+/**
+ * A base implementation of [ActivityLaunchAnimator.Controller] which creates a [ghost][GhostView]
+ * of [ghostedView] as well as an expandable background view, which are drawn and animated instead
+ * of the ghosted view.
+ *
+ * Important: [ghostedView] must be attached to the window when calling this function and during the
+ * animation.
+ *
+ * Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView]
+ * whenever possible instead.
+ */
+open class GhostedViewLaunchAnimatorController(
+    /** The view that will be ghosted and from which the background will be extracted. */
+    private val ghostedView: View
+) : ActivityLaunchAnimator.Controller {
+    /** The root view to which we will add the ghost view and expanding background. */
+    private val rootView = ghostedView.rootView as ViewGroup
+    private val rootViewOverlay = rootView.overlay
+
+    /** The ghost view that is drawn and animated instead of the ghosted view. */
+    private var ghostView: View? = null
+
+    /**
+     * The expanding background view that will be added to [rootView] (below [ghostView]) and
+     * animate.
+     */
+    private var backgroundView: FrameLayout? = null
+
+    /**
+     * The drawable wrapping the [ghostedView] background and used as background for
+     * [backgroundView].
+     */
+    private var backgroundDrawable: WrappedDrawable? = null
+    private var startBackgroundAlpha: Int = 0xFF
+
+    /**
+     * Return the background of the [ghostedView]. This background will be used to draw the
+     * background of the background view that is expanding up to the final animation position. This
+     * is called at the start of the animation.
+     *
+     * Note that during the animation, the alpha value value of this background will be set to 0,
+     * then set back to its initial value at the end of the animation.
+     */
+    protected open fun getBackground(): Drawable? = ghostedView.background
+
+    /**
+     * Set the corner radius of [background]. The background is the one that was returned by
+     * [getBackground].
+     */
+    protected open fun setBackgroundCornerRadius(
+        background: Drawable,
+        topCornerRadius: Float,
+        bottomCornerRadius: Float
+    ) {
+        // By default, we rely on WrappedDrawable to set/restore the background radii before/after
+        // each draw.
+        backgroundDrawable?.setBackgroundRadius(topCornerRadius, bottomCornerRadius)
+    }
+
+    /** Return the current top corner radius of the background. */
+    protected open fun getCurrentTopCornerRadius(): Float {
+        val drawable = getBackground() ?: return 0f
+        val gradient = findGradientDrawable(drawable) ?: return 0f
+
+        // TODO(b/184121838): Support more than symmetric top & bottom radius.
+        return gradient.cornerRadii?.get(CORNER_RADIUS_TOP_INDEX) ?: gradient.cornerRadius
+    }
+
+    /** Return the current bottom corner radius of the background. */
+    protected open fun getCurrentBottomCornerRadius(): Float {
+        val drawable = getBackground() ?: return 0f
+        val gradient = findGradientDrawable(drawable) ?: return 0f
+
+        // TODO(b/184121838): Support more than symmetric top & bottom radius.
+        return gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
+    }
+
+    override fun getRootView(): View {
+        return rootView
+    }
+
+    override fun createAnimatorState(): ActivityLaunchAnimator.State {
+        val location = ghostedView.locationOnScreen
+        return ActivityLaunchAnimator.State(
+            top = location[1],
+            bottom = location[1] + ghostedView.height,
+            left = location[0],
+            right = location[0] + ghostedView.width,
+            topCornerRadius = getCurrentTopCornerRadius(),
+            bottomCornerRadius = getCurrentBottomCornerRadius()
+        )
+    }
+
+    override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+        backgroundView = FrameLayout(rootView.context).apply {
+            forceHasOverlappingRendering(false)
+        }
+        rootViewOverlay.add(backgroundView)
+
+        // We wrap the ghosted view background and use it to draw the expandable background. Its
+        // alpha will be set to 0 as soon as we start drawing the expanding background.
+        val drawable = getBackground()
+        startBackgroundAlpha = drawable?.alpha ?: 0xFF
+        backgroundDrawable = WrappedDrawable(drawable)
+        backgroundView?.background = backgroundDrawable
+
+        // Create a ghost of the view that will be moving and fading out. This allows to fade out
+        // the content before fading out the background.
+        ghostView = GhostView.addGhost(ghostedView, rootView).apply {
+            setLayerType(View.LAYER_TYPE_HARDWARE, null)
+        }
+    }
+
+    override fun onLaunchAnimationProgress(
+        state: ActivityLaunchAnimator.State,
+        progress: Float,
+        linearProgress: Float
+    ) {
+        val ghostView = this.ghostView!!
+        ghostView.translationX = (state.leftChange + state.rightChange) / 2.toFloat()
+        ghostView.translationY = state.topChange.toFloat()
+        ghostView.alpha = state.contentAlpha
+
+        val backgroundView = this.backgroundView!!
+        backgroundView.top = state.top
+        backgroundView.bottom = state.bottom
+        backgroundView.left = state.left
+        backgroundView.right = state.right
+
+        val backgroundDrawable = backgroundDrawable!!
+        backgroundDrawable.alpha = (0xFF * state.backgroundAlpha).toInt()
+        backgroundDrawable.wrapped?.let {
+            setBackgroundCornerRadius(it, state.topCornerRadius, state.bottomCornerRadius)
+        }
+    }
+
+    override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+        backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
+
+        GhostView.removeGhost(ghostedView)
+        rootViewOverlay.remove(backgroundView)
+        ghostedView.invalidate()
+    }
+
+    companion object {
+        private const val CORNER_RADIUS_TOP_INDEX = 0
+        private const val CORNER_RADIUS_BOTTOM_INDEX = 4
+
+        /**
+         * Return the first [GradientDrawable] found in [drawable], or null if none is found. If
+         * [drawable] is a [LayerDrawable], this will return the first layer that is a
+         * [GradientDrawable].
+         */
+        private fun findGradientDrawable(drawable: Drawable): GradientDrawable? {
+            if (drawable is GradientDrawable) {
+                return drawable
+            }
+
+            if (drawable is LayerDrawable) {
+                for (i in 0 until drawable.numberOfLayers) {
+                    val maybeGradient = drawable.getDrawable(i)
+                    if (maybeGradient is GradientDrawable) {
+                        return maybeGradient
+                    }
+                }
+            }
+
+            return null
+        }
+    }
+
+    private class WrappedDrawable(val wrapped: Drawable?) : Drawable() {
+        companion object {
+            private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
+        }
+
+        private var currentAlpha = 0xFF
+        private var previousBounds = Rect()
+
+        private var cornerRadii = FloatArray(8) { -1f }
+        private var previousCornerRadii = FloatArray(8)
+
+        override fun draw(canvas: Canvas) {
+            val wrapped = this.wrapped ?: return
+
+            wrapped.copyBounds(previousBounds)
+
+            wrapped.alpha = currentAlpha
+            wrapped.bounds = bounds
+            setXfermode(wrapped, SRC_MODE)
+            applyBackgroundRadii()
+
+            wrapped.draw(canvas)
+
+            // The background view (and therefore this drawable) is drawn before the ghost view, so
+            // the ghosted view background alpha should always be 0 when it is drawn above the
+            // background.
+            wrapped.alpha = 0
+            wrapped.bounds = previousBounds
+            setXfermode(wrapped, null)
+            restoreBackgroundRadii()
+        }
+
+        override fun setAlpha(alpha: Int) {
+            if (alpha != currentAlpha) {
+                currentAlpha = alpha
+                invalidateSelf()
+            }
+        }
+
+        override fun getAlpha() = currentAlpha
+
+        override fun getOpacity(): Int {
+            val wrapped = this.wrapped ?: return PixelFormat.TRANSPARENT
+
+            val previousAlpha = wrapped.alpha
+            wrapped.alpha = currentAlpha
+            val opacity = wrapped.opacity
+            wrapped.alpha = previousAlpha
+            return opacity
+        }
+
+        override fun setColorFilter(filter: ColorFilter?) {
+            wrapped?.colorFilter = filter
+        }
+
+        private fun setXfermode(background: Drawable, mode: PorterDuffXfermode?) {
+            if (background !is LayerDrawable) {
+                background.setXfermode(mode)
+                return
+            }
+
+            // We set the xfermode on the first layer that is not a mask. Most of the time it will
+            // be the "background layer".
+            for (i in 0 until background.numberOfLayers) {
+                if (background.getId(i) != android.R.id.mask) {
+                    background.getDrawable(i).setXfermode(mode)
+                    break
+                }
+            }
+        }
+
+        fun setBackgroundRadius(topCornerRadius: Float, bottomCornerRadius: Float) {
+            updateRadii(cornerRadii, topCornerRadius, bottomCornerRadius)
+            invalidateSelf()
+        }
+
+        private fun updateRadii(
+            radii: FloatArray,
+            topCornerRadius: Float,
+            bottomCornerRadius: Float
+        ) {
+            radii[0] = topCornerRadius
+            radii[1] = topCornerRadius
+            radii[2] = topCornerRadius
+            radii[3] = topCornerRadius
+
+            radii[4] = bottomCornerRadius
+            radii[5] = bottomCornerRadius
+            radii[6] = bottomCornerRadius
+            radii[7] = bottomCornerRadius
+        }
+
+        private fun applyBackgroundRadii() {
+            if (cornerRadii[0] < 0 || wrapped == null) {
+                return
+            }
+
+            savePreviousBackgroundRadii(wrapped)
+            applyBackgroundRadii(wrapped, cornerRadii)
+        }
+
+        private fun savePreviousBackgroundRadii(background: Drawable) {
+            // TODO(b/184121838): This method assumes that all GradientDrawable in background will
+            // have the same radius. Should we save/restore the radii for each layer instead?
+            val gradient = findGradientDrawable(background) ?: return
+
+            // TODO(b/184121838): GradientDrawable#getCornerRadii clones its radii array. Should we
+            // try to avoid that?
+            val radii = gradient.cornerRadii
+            if (radii != null) {
+                radii.copyInto(previousCornerRadii)
+            } else {
+                // Copy the cornerRadius into previousCornerRadii.
+                val radius = gradient.cornerRadius
+                updateRadii(previousCornerRadii, radius, radius)
+            }
+        }
+
+        private fun applyBackgroundRadii(drawable: Drawable, radii: FloatArray) {
+            if (drawable is GradientDrawable) {
+                drawable.cornerRadii = radii
+                return
+            }
+
+            if (drawable !is LayerDrawable) {
+                return
+            }
+
+            for (i in 0 until drawable.numberOfLayers) {
+                (drawable.getDrawable(i) as? GradientDrawable)?.cornerRadii = radii
+            }
+        }
+
+        private fun restoreBackgroundRadii() {
+            if (cornerRadii[0] < 0 || wrapped == null) {
+                return
+            }
+
+            applyBackgroundRadii(wrapped, previousCornerRadii)
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index c9f2401..46237148 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -15,10 +15,12 @@
 package com.android.systemui.plugins.qs;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
 import android.service.quicksettings.Tile;
+import android.view.View;
 
 import com.android.internal.logging.InstanceId;
 import com.android.systemui.plugins.annotations.DependsOn;
@@ -53,10 +55,16 @@
     void removeCallbacks();
 
     QSIconView createTileView(Context context);
-    
+
     void click();
     void secondaryClick();
-    void longClick();
+
+    /**
+     * The tile was long clicked.
+     *
+     * @param view The view that was clicked.
+     */
+    void longClick(@Nullable View view);
 
     void userSwitch(int currentUser);
     int getMetricsCategory();
diff --git a/packages/SystemUI/res/drawable/circle_wallet_primary_50dp.xml b/packages/SystemUI/res/drawable/circle_wallet_primary_50dp.xml
index de4d882..41d88b4 100644
--- a/packages/SystemUI/res/drawable/circle_wallet_primary_50dp.xml
+++ b/packages/SystemUI/res/drawable/circle_wallet_primary_50dp.xml
@@ -18,5 +18,6 @@
     <size
         android:height="50dp"
         android:width="50dp" />
-        <solid android:color="#1A73E8" />
+    <solid android:color="@android:color/transparent" />
+    <stroke android:width="2dp" android:color="#AECBFA" />
 </shape>
diff --git a/packages/SystemUI/res/drawable/ic_move_magnification.xml b/packages/SystemUI/res/drawable/ic_move_magnification.xml
index ed97d0cc..642fc31 100644
--- a/packages/SystemUI/res/drawable/ic_move_magnification.xml
+++ b/packages/SystemUI/res/drawable/ic_move_magnification.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
   ~ Copyright (C) 2020 The Android Open Source Project
   ~
@@ -18,7 +19,7 @@
     <item>
         <shape android:shape="rectangle">
             <solid
-                android:color="@android:color/black" />
+                android:color="@color/magnification_switch_button_color" />
             <size
                 android:height="@dimen/magnification_drag_view_size"
                 android:width="@dimen/magnification_drag_view_size"/>
@@ -26,9 +27,14 @@
     </item>
     <item
         android:gravity="center">
-        <vector android:height="28dp" android:viewportHeight="512"
-            android:viewportWidth="512" android:width="28dp">
-            <path android:fillColor="#FFFFFF" android:pathData="M506.528,243.712l-96.224,-80c-4.8,-4 -11.456,-4.832 -17.056,-2.208C387.616,164.16 384,169.792 384,176v48h-96v-96h48c6.208,0 11.84,-3.616 14.496,-9.248c2.624,-5.632 1.792,-12.288 -2.208,-17.056l-80,-96.224c-6.08,-7.296 -18.496,-7.296 -24.608,0l-80,96.224c-3.968,4.8 -4.832,11.456 -2.176,17.056C164.128,124.384 169.792,128 176,128h48v96h-96v-48c0,-6.208 -3.616,-11.84 -9.248,-14.496c-5.6,-2.624 -12.256,-1.792 -17.056,2.208l-96.224,80c-7.296,6.08 -7.296,18.496 0,24.608l96.224,80c4.8,3.968 11.456,4.832 17.056,2.176C124.416,347.872 128,342.208 128,336v-48h96v96h-48c-6.208,0 -11.872,3.616 -14.496,9.248c-2.656,5.632 -1.792,12.288 2.176,17.056l80,96.224c6.08,7.296 18.496,7.296 24.608,0l80,-96.224c4,-4.8 4.832,-11.456 2.208,-17.056C347.84,387.616 342.208,384 336,384h-48v-96h96v48c0,6.208 3.616,11.872 9.248,14.496c5.632,2.656 12.288,1.792 17.056,-2.176l96.224,-80C513.824,262.208 513.824,249.792 506.528,243.712z"/>
+        <vector xmlns:android="http://schemas.android.com/apk/res/android"
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportWidth="24"
+            android:viewportHeight="24">
+            <path
+                android:pathData="M18.19,12.44l-3.24,-1.62c1.29,-1 2.12,-2.56 2.12,-4.32c0,-3.03 -2.47,-5.5 -5.5,-5.5s-5.5,2.47 -5.5,5.5c0,2.13 1.22,3.98 3,4.89v3.26c-2.11,-0.45 -2.01,-0.44 -2.26,-0.44c-0.53,0 -1.03,0.21 -1.41,0.59L4,16.22l5.09,5.09C9.52,21.75 10.12,22 10.74,22h6.3c0.98,0 1.81,-0.7 1.97,-1.67l0.8,-4.71C20.03,14.32 19.38,13.04 18.19,12.44zM17.84,15.29L17.04,20h-6.3c-0.09,0 -0.17,-0.04 -0.24,-0.1l-3.68,-3.68l4.25,0.89V6.5c0,-0.28 0.22,-0.5 0.5,-0.5c0.28,0 0.5,0.22 0.5,0.5v6h1.76l3.46,1.73C17.69,14.43 17.91,14.86 17.84,15.29zM8.07,6.5c0,-1.93 1.57,-3.5 3.5,-3.5s3.5,1.57 3.5,3.5c0,0.95 -0.38,1.81 -1,2.44V6.5c0,-1.38 -1.12,-2.5 -2.5,-2.5c-1.38,0 -2.5,1.12 -2.5,2.5v2.44C8.45,8.31 8.07,7.45 8.07,6.5z"
+                android:fillColor="#FFFFFF"/>
         </vector>
     </item>
 </layer-list>
diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
index 96e0193..5bb7390 100644
--- a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
+++ b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
@@ -30,9 +30,16 @@
             android:height="24dp"
             android:viewportWidth="24"
             android:viewportHeight="24">
-            <path
-                android:fillColor="@android:color/white"
-                android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z" />
+            <group>
+                <clip-path
+                    android:pathData="M0,0h24v24h-24z"/>
+                <path
+                    android:pathData="M11,6.05V8.05H14.59L8,14.64V11.05H6V18.05H13V16.05H9.41L16,9.46V13.05H18V6.05H11Z"
+                    android:fillColor="#ffffff"/>
+                <path
+                    android:pathData="M20,4.05V20.05H4V4.05H20ZM22,2.05H2V22.05H22V2.05Z"
+                    android:fillColor="#ffffff"/>
+            </group>
         </vector>
     </item>
 
diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
index 368c8df..2d8a5d9 100644
--- a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
+++ b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
@@ -26,30 +26,19 @@
 
     <item>
         <vector
-                android:width="24dp"
-                android:height="24dp"
-                android:viewportWidth="24"
-                android:viewportHeight="24">
-
-            <group
-                android:translateX="1.500000"
-                android:translateY="1.500000">
-                <path
-                    android:fillType="evenOdd"
-                    android:strokeColor="#FFFFFF"
-                    android:strokeWidth="2"
-                    android:pathData="M 3.5 1.5 L 17.5 1.5 Q 19.5 1.5 19.5 3.5 L 19.5 17.5 Q 19.5 19.5 17.5 19.5 L 3.5 19.5 Q 1.5 19.5 1.5 17.5 L 1.5 3.5 Q 1.5 1.5 3.5 1.5 Z" />
-                <path
-                    android:fillType="evenOdd"
-                    android:strokeColor="#FFFFFF"
-                    android:strokeWidth="1.5"
-                    android:pathData="M 4.25 4.25 H 8.75 V 8.75 H 4.25 V 4.25 Z" />
-                <path
-                    android:fillColor="#FFFFFF"
-                    android:fillType="evenOdd"
-                    android:strokeWidth="1"
-                    android:pathData="M 12.5 13.92 L 15.59 17 L 17 15.59 L 13.91 12.5 L 16.5 12.5 L 16.5 10.5 L 10.5 10.5 L 10.5 16.5 L 12.5 16.5 Z" />
-            </group>
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportWidth="24"
+            android:viewportHeight="24">
+            <path
+                android:pathData="M2,12.05V22.05H22V2.05H12V4.05H20V20.05H4V12.05H2Z"
+                android:fillColor="#ffffff"/>
+            <path
+                android:pathData="M10,2.05H2V10.05H10V2.05Z"
+                android:fillColor="#ffffff"/>
+            <path
+                android:pathData="M18,11.05V13.05H14.41L18.95,17.59L17.54,19L13,14.46V18.05H11V11.05H18Z"
+                android:fillColor="#ffffff"/>
         </vector>
     </item>
 
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index bbf69a9..1f90476 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -24,7 +24,7 @@
     android:minHeight="48dp"
     android:clickable="false"
     android:focusable="true"
-    android:theme="@style/QSHeaderTheme">
+    android:theme="@style/Theme.SystemUI.QuickSettings.Header">
 
     <com.android.systemui.statusbar.policy.Clock
         android:id="@+id/clock"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 1630244..a99edb9 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -51,6 +51,14 @@
         sysui:ignoreRightInset="true"
         />
 
+    <com.android.systemui.statusbar.ScrimView
+        android:id="@+id/scrim_notifications"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        sysui:ignoreRightInset="true"
+        />
+
     <com.android.systemui.statusbar.LightRevealScrim
             android:id="@+id/light_reveal_scrim"
             android:layout_width="match_parent"
@@ -100,4 +108,12 @@
             android:ellipsize="marquee"
             android:focusable="true" />
     </LinearLayout>
+
+    <com.android.systemui.biometrics.AuthRippleView
+        android:id="@+id/auth_ripple"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        sysui:ignoreRightInset="true"
+    />
 </com.android.systemui.statusbar.phone.NotificationShadeWindowView>
diff --git a/packages/SystemUI/res/layout/window_magnifier_view.xml b/packages/SystemUI/res/layout/window_magnifier_view.xml
index efd24c7..96de5a9 100644
--- a/packages/SystemUI/res/layout/window_magnifier_view.xml
+++ b/packages/SystemUI/res/layout/window_magnifier_view.xml
@@ -77,6 +77,8 @@
         android:layout_height="@dimen/magnification_drag_view_size"
         android:layout_margin="@dimen/magnification_outer_border_margin"
         android:layout_gravity="right|bottom"
+        android:paddingEnd="@dimen/magnifier_drag_handle_padding"
+        android:paddingBottom="@dimen/magnifier_drag_handle_padding"
         android:scaleType="center"
         android:importantForAccessibility="no"
         android:src="@drawable/ic_move_magnification"/>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 4fdeb6f..cd2395e 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -29,4 +29,10 @@
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
+    <!-- Screenshots -->
+    <style name="LongScreenshotActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
+        <item name="android:windowLightStatusBar">false</item>
+        <item name="android:windowLightNavigationBar">false</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index bd92299..0125144 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -642,4 +642,11 @@
 
     <!-- Whether to use window background blur for the volume dialog. -->
     <bool name="config_volumeDialogUseBackgroundBlur">false</bool>
+
+    <!-- The properties of the face auth camera in pixels -->
+    <integer-array name="config_face_auth_props">
+        <!-- sensorLocationX -->
+        <!-- sensorLocationY -->
+        <!--sensorRadius -->
+    </integer-array>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5a21b30..237edf8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -718,6 +718,7 @@
     <item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item>
 
     <item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
+    <dimen name="notification_scrim_corner_radius">32dp</dimen>
 
     <!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
     <dimen name="keyguard_min_swipe_amount">110dp</dimen>
@@ -1253,11 +1254,13 @@
     <dimen name="magnification_drag_view_size">38dp</dimen>
     <dimen name="magnification_controls_size">90dp</dimen>
     <dimen name="magnification_switch_button_size">60dp</dimen>
-    <dimen name="magnification_switch_button_padding">12dp</dimen>
+    <dimen name="magnification_switch_button_padding">6dp</dimen>
     <dimen name="magnifier_left_right_controls_width">35dp</dimen>
     <dimen name="magnifier_left_right_controls_height">45dp</dimen>
     <dimen name="magnifier_up_down_controls_width">45dp</dimen>
     <dimen name="magnifier_up_down_controls_height">40dp</dimen>
+    <!-- The extra padding to show the whole outer border -->
+    <dimen name="magnifier_drag_handle_padding">3dp</dimen>
 
     <!-- Home Controls -->
     <dimen name="controls_header_side_margin">4dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 0763109..c39db94 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -50,4 +50,6 @@
     <bool name="flag_charging_ripple">false</bool>
 
     <bool name="flag_ongoing_call_status_bar_chip">false</bool>
+
+    <bool name="flag_smartspace">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e6fc332..ba07829 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -375,9 +375,10 @@
         <item name="*android:dotColor">?android:attr/textColorSecondary</item>
     </style>
 
-    <style name="Theme.SystemUI.QuickSettings" parent="@*android:style/Theme.DeviceDefault.SystemUI">
+    <style name="Theme.SystemUI.QuickSettings" parent="@*android:style/Theme.DeviceDefault">
         <item name="lightIconTheme">@style/QSIconTheme</item>
         <item name="darkIconTheme">@style/QSIconTheme</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:colorError">@*android:color/error_color_material_dark</item>
         <item name="android:windowIsFloating">true</item>
         <item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
@@ -452,7 +453,7 @@
         <item name="singleToneColor">@color/dark_mode_qs_icon_color_single_tone</item>
     </style>
 
-    <style name="QSHeaderTheme" parent="@style/Theme.SystemUI">
+    <style name="Theme.SystemUI.QuickSettings.Header">
         <item name="lightIconTheme">@style/DualToneLightTheme</item>
         <item name="darkIconTheme">@style/QSHeaderDarkTheme</item>
     </style>
@@ -622,6 +623,8 @@
     <!-- Screenshots -->
     <style name="LongScreenshotActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
         <item name="android:windowNoTitle">true</item>
+        <item name="android:windowLightStatusBar">true</item>
+        <item name="android:windowLightNavigationBar">true</item>
     </style>
 
     <!-- Privacy dialog -->
diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
index 15312ad..c9dea46 100644
--- a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
+++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
@@ -18,11 +18,12 @@
 
 import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
 
+import static com.android.systemui.classifier.Classifier.DISABLED_UDFPS_AFFORDANCE;
+
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
 import android.hardware.biometrics.BiometricSourceType;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -33,7 +34,9 @@
 import com.android.systemui.R;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.ViewController;
 
@@ -53,14 +56,16 @@
     @NonNull private final KeyguardViewController mKeyguardViewController;
     @NonNull private final StatusBarStateController mStatusBarStateController;
     @NonNull private final KeyguardStateController mKeyguardStateController;
+    @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final Drawable mButton;
     @NonNull private final Drawable mUnlockIcon;
 
     private boolean mIsDozing;
     private boolean mIsBouncerShowing;
-    private boolean mIsKeyguardShowing;
     private boolean mRunningFPS;
     private boolean mCanDismissLockScreen;
+    private boolean mQsExpanded;
+    private int mStatusBarState;
 
     private boolean mShowButton;
     private boolean mShowUnlockIcon;
@@ -71,16 +76,19 @@
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
             @NonNull AuthController authController,
             @NonNull KeyguardViewController keyguardViewController,
-            @NonNull KeyguardStateController keyguardStateController
+            @NonNull KeyguardStateController keyguardStateController,
+            @NonNull FalsingManager falsingManager
     ) {
         super(view);
-        mView.setOnTouchListener(mOnTouchListener);
+        mView.setOnClickListener(v -> onAffordanceClick());
+        mView.setOnLongClickListener(v -> onAffordanceClick());
         mView.setSensorProperties(authController.getUdfpsProps().get(0));
 
         mStatusBarStateController = statusBarStateController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mKeyguardViewController = keyguardViewController;
         mKeyguardStateController = keyguardStateController;
+        mFalsingManager = falsingManager;
 
         final Context context = view.getContext();
         mButton = context.getResources().getDrawable(
@@ -94,10 +102,10 @@
     @Override
     protected void onViewAttached() {
         mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
-        mIsKeyguardShowing = mKeyguardStateController.isShowing();
         mIsDozing = mStatusBarStateController.isDozing();
         mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
         mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
+        mStatusBarState = mStatusBarStateController.getState();
         mUnlockIcon.setTint(Utils.getColorAttrDefaultColor(mView.getContext(),
                 R.attr.wallpaperTextColorAccent));
         updateVisibility();
@@ -114,6 +122,15 @@
         mKeyguardStateController.removeCallback(mKeyguardStateCallback);
     }
 
+    private boolean onAffordanceClick() {
+        if (mFalsingManager.isFalseTouch(DISABLED_UDFPS_AFFORDANCE)) {
+            return false;
+        }
+        mView.setVisibility(View.INVISIBLE);
+        mKeyguardViewController.showBouncer(/* scrim */ true);
+        return true;
+    }
+
     /**
      * Call when this controller is no longer needed. This will remove the view from its parent.
      */
@@ -123,6 +140,14 @@
         }
     }
 
+    /**
+     * Set whether qs is expanded. When QS is expanded, don't show a DisabledUdfps affordance.
+     */
+    public void setQsExpanded(boolean expanded) {
+        mQsExpanded = expanded;
+        updateVisibility();
+    }
+
     private void updateVisibility() {
         mShowButton = !mCanDismissLockScreen && !mRunningFPS && isLockScreen();
         mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
@@ -139,7 +164,10 @@
     }
 
     private boolean isLockScreen() {
-        return mIsKeyguardShowing && !mIsDozing && !mIsBouncerShowing;
+        return !mIsDozing
+                && !mIsBouncerShowing
+                && !mQsExpanded
+                && mStatusBarState == StatusBarState.KEYGUARD;
     }
 
     @Override
@@ -148,20 +176,13 @@
         pw.println("  mShowBouncerButton: " + mShowButton);
         pw.println("  mShowUnlockIcon: " + mShowUnlockIcon);
         pw.println("  mIsDozing: " + mIsDozing);
-        pw.println("  mIsKeyguardShowing: " + mIsKeyguardShowing);
         pw.println("  mIsBouncerShowing: " + mIsBouncerShowing);
         pw.println("  mRunningFPS: " + mRunningFPS);
         pw.println("  mCanDismissLockScreen: " + mCanDismissLockScreen);
+        pw.println("  mStatusBarState: " + StatusBarState.toShortString(mStatusBarState));
+        pw.println("  mQsExpanded: " + mQsExpanded);
     }
 
-    private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
-        @Override
-        public boolean onTouch(View v, MotionEvent event) {
-            mKeyguardViewController.showBouncer(/* scrim */ true);
-            return true;
-        }
-    };
-
     private StatusBarStateController.StateListener mStatusBarStateListener =
             new StatusBarStateController.StateListener() {
                 @Override
@@ -169,6 +190,12 @@
                     mIsDozing = isDozing;
                     updateVisibility();
                 }
+
+                @Override
+                public void onStateChanged(int statusBarState) {
+                    mStatusBarState = statusBarState;
+                    updateVisibility();
+                }
             };
 
     private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
@@ -193,21 +220,9 @@
     private final KeyguardStateController.Callback mKeyguardStateCallback =
             new KeyguardStateController.Callback() {
         @Override
-        public void onKeyguardShowingChanged() {
-            updateIsKeyguardShowing();
-            updateVisibility();
-        }
-
-        @Override
         public void onUnlockedChanged() {
-            updateIsKeyguardShowing();
             mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
             updateVisibility();
         }
-
-        private void updateIsKeyguardShowing() {
-            mIsKeyguardShowing = mKeyguardStateController.isShowing()
-                    && !mKeyguardStateController.isKeyguardGoingAway();
-        }
     };
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 0675200..24b7cd1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -16,8 +16,15 @@
 
 package com.android.keyguard;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
 import android.app.WallpaperManager;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceManager;
+import android.app.smartspace.SmartspaceSession;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.res.Resources;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -25,6 +32,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.clock.ClockManager;
@@ -32,8 +40,12 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 import com.android.systemui.plugins.ClockPlugin;
+import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -43,6 +55,7 @@
 
 import java.util.Locale;
 import java.util.TimeZone;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -68,6 +81,13 @@
     private AnimatableClockController mNewLockScreenLargeClockViewController;
     private FrameLayout mNewLockScreenLargeClockFrame;
 
+    private PluginManager mPluginManager;
+    private boolean mIsSmartspaceEnabled;
+    PluginListener mPluginListener;
+    private Executor mUiExecutor;
+    private SmartspaceSession mSmartspaceSession;
+    private SmartspaceSession.Callback mSmartspaceCallback;
+
     private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
     private final StatusBarStateController.StateListener mStateListener =
@@ -96,6 +116,9 @@
     private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
     private String mTimeFormat;
 
+    // If set, will replace keyguard_status_area
+    private BcSmartspaceDataPlugin.SmartspaceView mSmartspaceView;
+
     @Inject
     public KeyguardClockSwitchController(
             KeyguardClockSwitch keyguardClockSwitch,
@@ -105,7 +128,10 @@
             KeyguardSliceViewController keyguardSliceViewController,
             NotificationIconAreaController notificationIconAreaController,
             ContentResolver contentResolver,
-            BroadcastDispatcher broadcastDispatcher) {
+            BroadcastDispatcher broadcastDispatcher,
+            PluginManager pluginManager,
+            FeatureFlags featureFlags,
+            @Main Executor uiExecutor) {
         super(keyguardClockSwitch);
         mResources = resources;
         mStatusBarStateController = statusBarStateController;
@@ -115,6 +141,9 @@
         mNotificationIconAreaController = notificationIconAreaController;
         mBroadcastDispatcher = broadcastDispatcher;
         mTimeFormat = Settings.System.getString(contentResolver, Settings.System.TIME_12_24);
+        mPluginManager = pluginManager;
+        mIsSmartspaceEnabled = featureFlags.isSmartspaceEnabled();
+        mUiExecutor = uiExecutor;
     }
 
     /**
@@ -137,6 +166,63 @@
         updateAodIcons();
         mNewLockScreenClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view);
         mNewLockScreenLargeClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view_large);
+
+        // If a smartspace plugin is detected, replace the existing smartspace
+        // (keyguard_status_area), and initialize a new session
+        mPluginListener = new PluginListener<BcSmartspaceDataPlugin>() {
+
+            @Override
+            public void onPluginConnected(BcSmartspaceDataPlugin plugin, Context pluginContext) {
+                if (!mIsSmartspaceEnabled) return;
+
+                View ksa = mView.findViewById(R.id.keyguard_status_area);
+                int ksaIndex = mView.indexOfChild(ksa);
+                ksa.setVisibility(View.GONE);
+
+                mSmartspaceView = plugin.getView(mView);
+                mSmartspaceView.registerDataProvider(plugin);
+
+                RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+                        MATCH_PARENT, WRAP_CONTENT);
+                lp.addRule(RelativeLayout.BELOW, R.id.new_lockscreen_clock_view);
+                mView.addView((View) mSmartspaceView, ksaIndex, lp);
+
+                View nic = mView.findViewById(
+                        com.android.systemui.R.id.left_aligned_notification_icon_container);
+                lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
+                lp.addRule(RelativeLayout.BELOW, ((View) mSmartspaceView).getId());
+                nic.setLayoutParams(lp);
+
+                createSmartspaceSession(plugin);
+            }
+
+            @Override
+            public void onPluginDisconnected(BcSmartspaceDataPlugin plugin) {
+                if (!mIsSmartspaceEnabled) return;
+
+                mView.removeView((View) mSmartspaceView);
+                mView.findViewById(R.id.keyguard_status_area).setVisibility(View.VISIBLE);
+
+                View nic = mView.findViewById(
+                        com.android.systemui.R.id.left_aligned_notification_icon_container);
+                RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)
+                        nic.getLayoutParams();
+                lp.addRule(RelativeLayout.BELOW, R.id.keyguard_status_area);
+                nic.setLayoutParams(lp);
+
+                mSmartspaceView = null;
+            }
+
+            private void createSmartspaceSession(BcSmartspaceDataPlugin plugin) {
+                mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class)
+                        .createSmartspaceSession(
+                                new SmartspaceConfig.Builder(getContext(), "lockscreen").build());
+                mSmartspaceCallback = targets -> plugin.onTargetsAvailable(targets);
+                mSmartspaceSession.registerSmartspaceUpdates(mUiExecutor, mSmartspaceCallback);
+                mSmartspaceSession.requestSmartspaceUpdate();
+            }
+        };
+        mPluginManager.addPluginListener(mPluginListener, BcSmartspaceDataPlugin.class, false);
     }
 
     @Override
@@ -147,6 +233,13 @@
         mStatusBarStateController.removeCallback(mStateListener);
         mColorExtractor.removeOnColorsChangedListener(mColorsListener);
         mView.setClockPlugin(null, mStatusBarStateController.getState());
+
+        if (mSmartspaceSession != null) {
+            mSmartspaceSession.unregisterSmartspaceUpdates(mSmartspaceCallback);
+            mSmartspaceSession.destroy();
+            mSmartspaceSession = null;
+        }
+        mPluginManager.removePluginListener(mPluginListener);
     }
 
     /**
@@ -222,6 +315,11 @@
             PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_Y,
                     scale, props, animate);
         }
+
+        if (mSmartspaceView != null) {
+            PropertyAnimator.setProperty((View) mSmartspaceView, AnimatableProperty.TRANSLATION_X,
+                    x, props, animate);
+        }
         mKeyguardSliceViewController.updatePosition(x, props, animate);
         mNotificationIconAreaController.updatePosition(x, props, animate);
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index de09eaa6..2219cf4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -315,7 +315,6 @@
     private boolean mLogoutEnabled;
     // cached value to avoid IPCs
     private boolean mIsUdfpsEnrolled;
-    private boolean mKeyguardQsUserSwitchEnabled;
     // If the user long pressed the lock icon, disabling face auth for the current session.
     private boolean mLockIconPressed;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -1953,7 +1952,7 @@
             return isFaceAuthEnabledForUser(KeyguardUpdateMonitor.getCurrentUser())
                     && !isUdfpsEnrolled();
         }
-        return !isKeyguardQsUserSwitchEnabled();
+        return true;
     }
 
     /**
@@ -1963,17 +1962,6 @@
         return mIsUdfpsEnrolled;
     }
 
-    /**
-     * @return true if the keyguard qs user switcher shortcut is enabled
-     */
-    public boolean isKeyguardQsUserSwitchEnabled() {
-        return mKeyguardQsUserSwitchEnabled;
-    }
-
-    public void setKeyguardQsUserSwitchEnabled(boolean enabled) {
-        mKeyguardQsUserSwitchEnabled = enabled;
-    }
-
     private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() {
         @Override
         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 3d6d381..5507ffa 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -18,8 +18,11 @@
 import android.content.Intent;
 import android.view.View;
 
+import androidx.annotation.Nullable;
+
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.util.Optional;
@@ -51,18 +54,27 @@
 
     @Override
     public void startPendingIntentDismissingKeyguard(PendingIntent intent,
-            Runnable intentSentCallback) {
+            Runnable intentSentUiThreadCallback) {
         mActualStarter.ifPresent(
                 starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
-                        intentSentCallback));
+                        intentSentUiThreadCallback));
     }
 
     @Override
     public void startPendingIntentDismissingKeyguard(PendingIntent intent,
-            Runnable intentSentCallback, View associatedView) {
+            Runnable intentSentUiThreadCallback, View associatedView) {
         mActualStarter.ifPresent(
                 starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
-                        intentSentCallback, associatedView));
+                        intentSentUiThreadCallback, associatedView));
+    }
+
+    @Override
+    public void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentUiThreadCallback,
+            ActivityLaunchAnimator.Controller animationController) {
+        mActualStarter.ifPresent(
+                starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
+                        intentSentUiThreadCallback, animationController));
     }
 
     @Override
@@ -97,12 +109,27 @@
     }
 
     @Override
+    public void postStartActivityDismissingKeyguard(Intent intent, int delay,
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
+        mActualStarter.ifPresent(
+                starter -> starter.get().postStartActivityDismissingKeyguard(intent, delay,
+                        animationController));
+    }
+
+    @Override
     public void postStartActivityDismissingKeyguard(PendingIntent intent) {
         mActualStarter.ifPresent(
                 starter -> starter.get().postStartActivityDismissingKeyguard(intent));
     }
 
     @Override
+    public void postStartActivityDismissingKeyguard(PendingIntent intent,
+            ActivityLaunchAnimator.Controller animationController) {
+        mActualStarter.ifPresent(starter ->
+                starter.get().postStartActivityDismissingKeyguard(intent, animationController));
+    }
+
+    @Override
     public void postQSRunnableDismissingKeyguard(Runnable runnable) {
         mActualStarter.ifPresent(
                 starter -> starter.get().postQSRunnableDismissingKeyguard(runnable));
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index 2eba952..c23bcb8 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -40,6 +40,7 @@
             new PathInterpolator(0.8f, 0f, 0.6f, 1f);
     public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
     public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
+    public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
     public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
     public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
     public static final Interpolator LINEAR = new LinearInterpolator();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 9d00262..cc167b9 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -46,6 +46,7 @@
     private GlobalRootComponent mRootComponent;
     private WMComponent mWMComponent;
     private SysUIComponent mSysUIComponent;
+    private boolean mInitializeComponents;
 
     public static <T extends SystemUIFactory> T getInstance() {
         return (T) mFactory;
@@ -88,13 +89,13 @@
     public void init(Context context, boolean fromTest)
             throws ExecutionException, InterruptedException {
         // Only initialize components for the main system ui process running as the primary user
-        final boolean initializeComponents = !fromTest
+        mInitializeComponents = !fromTest
                 && android.os.Process.myUserHandle().isSystem()
                 && ActivityThread.currentProcessName().equals(ActivityThread.currentPackageName());
         mRootComponent = buildGlobalRootComponent(context);
         // Stand up WMComponent
         mWMComponent = mRootComponent.getWMComponentBuilder().build();
-        if (initializeComponents) {
+        if (mInitializeComponents) {
             // Only initialize when not starting from tests since this currently initializes some
             // components that shouldn't be run in the test environment
             mWMComponent.init();
@@ -102,7 +103,7 @@
 
         // And finally, retrieve whatever SysUI needs from WMShell and build SysUI.
         SysUIComponent.Builder builder = mRootComponent.getSysUIComponent();
-        if (initializeComponents) {
+        if (mInitializeComponents) {
             // Only initialize when not starting from tests since this currently initializes some
             // components that shouldn't be run in the test environment
             builder = prepareSysUIComponentBuilder(builder, mWMComponent)
@@ -134,7 +135,7 @@
                     .setStartingSurface(Optional.ofNullable(null));
         }
         mSysUIComponent = builder.build();
-        if (initializeComponents) {
+        if (mInitializeComponents) {
             mSysUIComponent.init();
         }
 
@@ -160,6 +161,9 @@
                 .build();
     }
 
+    protected boolean shouldInitializeComponents() {
+        return mInitializeComponents;
+    }
 
     public GlobalRootComponent getRootComponent() {
         return mRootComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index cdd6942..4f5fdc9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -195,6 +195,13 @@
     }
 
     @Override
+    public void onAccessibilityActionPerformed(int displayId) {
+        if (mWindowMagnificationConnectionImpl != null) {
+            mWindowMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
+        }
+    }
+
+    @Override
     public void requestWindowMagnificationConnection(boolean connect) {
         if (connect) {
             setWindowMagnificationConnection();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
index be7d757..2d620ab 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
@@ -120,4 +120,14 @@
             }
         }
     }
+
+    void onAccessibilityActionPerformed(int displayId) {
+        if (mConnectionCallback != null) {
+            try {
+                mConnectionCallback.onAccessibilityActionPerformed(displayId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to inform an accessibility action is already performed", e);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 2b666f1..48beea3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -756,31 +756,23 @@
                 final float scale = mScale + A11Y_CHANGE_SCALE_DIFFERENCE;
                 mWindowMagnifierCallback.onPerformScaleAction(mDisplayId,
                         A11Y_ACTION_SCALE_RANGE.clamp(scale));
-                return true;
-            }
-            if (action == R.id.accessibility_action_zoom_out) {
+            } else if (action == R.id.accessibility_action_zoom_out) {
                 final float scale = mScale - A11Y_CHANGE_SCALE_DIFFERENCE;
                 mWindowMagnifierCallback.onPerformScaleAction(mDisplayId,
                         A11Y_ACTION_SCALE_RANGE.clamp(scale));
-                return true;
-            }
-            if (action == R.id.accessibility_action_move_up) {
+            } else if (action == R.id.accessibility_action_move_up) {
                 move(0, -mSourceBounds.height());
-                return true;
-            }
-            if (action == R.id.accessibility_action_move_down) {
+            } else if (action == R.id.accessibility_action_move_down) {
                 move(0, mSourceBounds.height());
-                return true;
-            }
-            if (action == R.id.accessibility_action_move_left) {
+            } else if (action == R.id.accessibility_action_move_left) {
                 move(-mSourceBounds.width(), 0);
-                return true;
-            }
-            if (action == R.id.accessibility_action_move_right) {
+            } else if (action == R.id.accessibility_action_move_right) {
                 move(mSourceBounds.width(), 0);
-                return true;
+            } else {
+                return false;
             }
-            return false;
+            mWindowMagnifierCallback.onAccessibilityActionPerformed(mDisplayId);
+            return true;
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
index fb1d1b6..628a5e8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
@@ -46,4 +46,11 @@
      * @param scale the target scale, or {@link Float#NaN} to leave unchanged
      */
     void onPerformScaleAction(int displayId, float scale);
+
+    /**
+     * Called when the accessibility action is performed.
+     *
+     * @param displayId The logical display id.
+     */
+    void onAccessibilityActionPerformed(int displayId);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 74f855b..994401d 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -25,24 +25,20 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
 import android.media.AudioManager;
 import android.media.AudioRecordingConfiguration;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
-import android.provider.DeviceConfig;
+import android.permission.PermissionManager;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
 
 import androidx.annotation.WorkerThread;
 
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.systemui.Dumpable;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
@@ -83,19 +79,12 @@
     private final Context mContext;
     private final AppOpsManager mAppOps;
     private final AudioManager mAudioManager;
-    private final LocationManager mLocationManager;
     private final IndividualSensorPrivacyController mSensorPrivacyController;
     private final SystemClock mClock;
 
-    // mLocationProviderPackages are cached and updated only occasionally
-    private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000;
-    private long mLastLocationProviderPackageUpdate;
-    private List<String> mLocationProviderPackages;
-
     private H mBGHandler;
     private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
     private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
-    private final PermissionFlagsCache mFlagsCache;
     private boolean mListening;
     private boolean mMicMuted;
     private boolean mCameraDisabled;
@@ -124,7 +113,6 @@
             Context context,
             @Background Looper bgLooper,
             DumpManager dumpManager,
-            PermissionFlagsCache cache,
             AudioManager audioManager,
             IndividualSensorPrivacyController sensorPrivacyController,
             BroadcastDispatcher dispatcher,
@@ -132,7 +120,6 @@
     ) {
         mDispatcher = dispatcher;
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-        mFlagsCache = cache;
         mBGHandler = new H(bgLooper);
         final int numOps = OPS.length;
         for (int i = 0; i < numOps; i++) {
@@ -143,7 +130,6 @@
         mMicMuted = audioManager.isMicrophoneMute()
                 || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
         mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
-        mLocationManager = context.getSystemService(LocationManager.class);
         mContext = context;
         mClock = clock;
         dumpManager.registerDumpable(TAG, this);
@@ -308,102 +294,8 @@
         return createdNew;
     }
 
-    /**
-     * Does the app-op code refer to a user sensitive permission for the specified user id
-     * and package. Only user sensitive permission should be shown to the user by default.
-     *
-     * @param appOpCode The code of the app-op.
-     * @param uid The uid of the user.
-     * @param packageName The name of the package.
-     *
-     * @return {@code true} iff the app-op item is user sensitive
-     */
-    private boolean isUserSensitive(int appOpCode, int uid, String packageName) {
-        String permission = AppOpsManager.opToPermission(appOpCode);
-        if (permission == null) {
-            return false;
-        }
-        int permFlags = mFlagsCache.getPermissionFlags(permission,
-                packageName, uid);
-        return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
-    }
-
-    /**
-     * Does the app-op item refer to an operation that should be shown to the user.
-     * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive
-     * permission should be shown to the user by default.
-     *
-     * @param item The item
-     *
-     * @return {@code true} iff the app-op item should be shown to the user
-     */
-    private boolean isUserVisible(AppOpItem item) {
-        return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
-    }
-
-    /**
-     * Checks if a package is the current location provider.
-     *
-     * <p>Data is cached to avoid too many calls into system server
-     *
-     * @param packageName The package that might be the location provider
-     *
-     * @return {@code true} iff the package is the location provider.
-     */
-    private boolean isLocationProvider(String packageName) {
-        long now = System.currentTimeMillis();
-
-        if (mLastLocationProviderPackageUpdate + LOCATION_PROVIDER_UPDATE_FREQUENCY_MS < now) {
-            mLastLocationProviderPackageUpdate = now;
-            mLocationProviderPackages = mLocationManager.getProviderPackages(
-                    LocationManager.FUSED_PROVIDER);
-        }
-
-        return mLocationProviderPackages.contains(packageName);
-    }
-
-    private boolean isSpeechRecognizerUsage(int opCode, String packageName) {
-        if (AppOpsManager.OP_RECORD_AUDIO != opCode) {
-            return false;
-        }
-
-        return packageName.equals(
-                mContext.getString(R.string.config_systemSpeechRecognizer));
-    }
-
-    // TODO ntmyren: remove after teamfood is finished
-    private boolean showSystemApps() {
-        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false);
-    }
-
-    /**
-     * Does the app-op, uid and package name, refer to an operation that should be shown to the
-     * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or
-     * ops that refer to user sensitive permission should be shown to the user by default.
-     *
-     * @param item The item
-     *
-     * @return {@code true} iff the app-op for should be shown to the user
-     */
-    private boolean isUserVisible(int appOpCode, int uid, String packageName) {
-        // currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION
-        // does not correspond to a platform permission
-        // which may be user sensitive, so for now always show it to the user.
-        if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW
-                || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION
-                || appOpCode == AppOpsManager.OP_PHONE_CALL_CAMERA
-                || appOpCode == AppOpsManager.OP_PHONE_CALL_MICROPHONE) {
-            return true;
-        }
-        // TODO ntmyren: Replace this with more robust check if this moves beyond teamfood
-        if (((showSystemApps() && !packageName.equals("android"))
-                || appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName))
-                || isSpeechRecognizerUsage(appOpCode, packageName)) {
-            return true;
-        }
-
-        return isUserSensitive(appOpCode, uid, packageName);
+    private boolean isUserVisible(String packageName) {
+        return PermissionManager.shouldShowPackageForIndicatorCached(mContext, packageName);
     }
 
     /**
@@ -438,7 +330,7 @@
                 AppOpItem item = mActiveItems.get(i);
                 if ((userId == UserHandle.USER_ALL
                         || UserHandle.getUserId(item.getUid()) == userId)
-                        && isUserVisible(item) && !item.isDisabled()) {
+                        && isUserVisible(item.getPackageName()) && !item.isDisabled()) {
                     list.add(item);
                 }
             }
@@ -449,7 +341,7 @@
                 AppOpItem item = mNotedItems.get(i);
                 if ((userId == UserHandle.USER_ALL
                         || UserHandle.getUserId(item.getUid()) == userId)
-                        && isUserVisible(item)) {
+                        && isUserVisible(item.getPackageName())) {
                     list.add(item);
                 }
             }
@@ -503,7 +395,7 @@
     }
 
     private void notifySuscribersWorker(int code, int uid, String packageName, boolean active) {
-        if (mCallbacksByCode.contains(code) && isUserVisible(code, uid, packageName)) {
+        if (mCallbacksByCode.contains(code) && isUserVisible(packageName)) {
             if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
             for (Callback cb: mCallbacksByCode.get(code)) {
                 cb.onActiveStateChanged(code, uid, packageName, active);
diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
deleted file mode 100644
index 3fd838b..0000000
--- a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.appops
-
-import android.content.pm.PackageManager
-import android.os.UserHandle
-import androidx.annotation.WorkerThread
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.util.Assert
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-private data class PermissionFlagKey(
-    val permission: String,
-    val packageName: String,
-    val uid: Int
-)
-
-/**
- * Cache for PackageManager's PermissionFlags.
- *
- * After a specific `{permission, package, uid}` has been requested, updates to it will be tracked,
- * and changes to the uid will trigger new requests (in the background).
- */
-@SysUISingleton
-class PermissionFlagsCache @Inject constructor(
-    private val packageManager: PackageManager,
-    @Background private val executor: Executor
-) : PackageManager.OnPermissionsChangedListener {
-
-    private val permissionFlagsCache =
-            mutableMapOf<Int, MutableMap<PermissionFlagKey, Int>>()
-    private var listening = false
-
-    override fun onPermissionsChanged(uid: Int) {
-        executor.execute {
-            // Only track those that we've seen before
-            val keys = permissionFlagsCache.get(uid)
-            if (keys != null) {
-                keys.mapValuesTo(keys) {
-                    getFlags(it.key)
-                }
-            }
-        }
-    }
-
-    /**
-     * Retrieve permission flags from cache or PackageManager. There parameters will be passed
-     * directly to [PackageManager].
-     *
-     * Calls to this method should be done from a background thread (though it will only be
-     * enforced if the cache is not hit).
-     */
-    @WorkerThread
-    fun getPermissionFlags(permission: String, packageName: String, uid: Int): Int {
-        if (!listening) {
-            listening = true
-            packageManager.addOnPermissionsChangeListener(this)
-        }
-        val key = PermissionFlagKey(permission, packageName, uid)
-        return permissionFlagsCache.getOrPut(uid, { mutableMapOf() }).get(key) ?: run {
-            getFlags(key).also {
-                Assert.isNotMainThread()
-                permissionFlagsCache.get(uid)?.put(key, it)
-            }
-        }
-    }
-
-    private fun getFlags(key: PermissionFlagKey): Int {
-        return packageManager.getPermissionFlags(key.permission, key.packageName,
-                UserHandle.getUserHandleForUid(key.uid))
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 94b4c5f..2802742 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -29,6 +29,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Configuration;
+import android.graphics.PointF;
 import android.graphics.RectF;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricPrompt;
@@ -82,6 +83,7 @@
     @Nullable private final List<FingerprintSensorPropertiesInternal> mFpProps;
     @Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
     @Nullable private final List<FingerprintSensorPropertiesInternal> mUdfpsProps;
+    @Nullable private final PointF mFaceAuthSensorLocation;
 
     // TODO: These should just be saved from onSaveState
     private SomeArgs mCurrentDialogArgs;
@@ -261,10 +263,34 @@
     }
 
     /**
-     * @return where the UDFPS exists on the screen in pixels.
+     * @return where the UDFPS exists on the screen in pixels in portrait mode.
      */
     public RectF getUdfpsRegion() {
-        return mUdfpsController == null ? null : mUdfpsController.getSensorLocation();
+        return mUdfpsController == null
+                ? null
+                : mUdfpsController.getSensorLocation();
+    }
+
+    /**
+     * @return where the UDFPS exists on the screen in pixels in portrait mode.
+     */
+    public PointF getUdfpsSensorLocation() {
+        if (mUdfpsController == null) {
+            return null;
+        }
+        return new PointF(mUdfpsController.getSensorLocation().centerX(),
+                mUdfpsController.getSensorLocation().centerY());
+    }
+
+    /**
+     * @return where the face authentication sensor exists relative to the screen in pixels in
+     * portrait mode.
+     */
+    public PointF getFaceAuthSensorLocation() {
+        if (mFaceProps == null || mFaceAuthSensorLocation == null) {
+            return null;
+        }
+        return new PointF(mFaceAuthSensorLocation.x, mFaceAuthSensorLocation.y);
     }
 
     /**
@@ -339,6 +365,15 @@
             }
         }
         mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
+        int[] faceAuthLocation = context.getResources().getIntArray(
+                com.android.systemui.R.array.config_face_auth_props);
+        if (faceAuthLocation == null || faceAuthLocation.length < 2) {
+            mFaceAuthSensorLocation = null;
+        } else {
+            mFaceAuthSensorLocation = new PointF(
+                    (float) faceAuthLocation[0],
+                    (float) faceAuthLocation[1]);
+        }
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index a1149fd..110351e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -17,17 +17,19 @@
 package com.android.systemui.biometrics
 
 import android.content.Context
+import android.content.res.Configuration
+import android.graphics.PointF
 import android.hardware.biometrics.BiometricSourceType
-import android.view.View
-import android.view.ViewGroup
-import com.android.internal.annotations.VisibleForTesting
+import androidx.annotation.VisibleForTesting
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.settingslib.Utils
-import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.ViewController
 import java.io.PrintWriter
 import javax.inject.Inject
 
@@ -35,30 +37,82 @@
  * Controls the ripple effect that shows when authentication is successful.
  * The ripple uses the accent color of the current theme.
  */
-@SysUISingleton
+@StatusBarScope
 class AuthRippleController @Inject constructor(
-    commandRegistry: CommandRegistry,
-    configurationController: ConfigurationController,
-    private val context: Context,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor
-) {
-    @VisibleForTesting
-    var rippleView: AuthRippleView = AuthRippleView(context, attrs = null)
+    private val sysuiContext: Context,
+    private val authController: AuthController,
+    private val configurationController: ConfigurationController,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+    private val commandRegistry: CommandRegistry,
+    private val notificationShadeWindowController: NotificationShadeWindowController,
+    rippleView: AuthRippleView?
+) : ViewController<AuthRippleView>(rippleView) {
+    private var fingerprintSensorLocation: PointF? = null
+    private var faceSensorLocation: PointF? = null
 
-    val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
-        override fun onBiometricAuthenticated(
-            userId: Int,
-            biometricSourceType: BiometricSourceType?,
-            isStrongBiometric: Boolean
-        ) {
-            if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
-                rippleView.startRipple()
-            }
+    @VisibleForTesting
+    public override fun onViewAttached() {
+        updateRippleColor()
+        updateSensorLocation()
+        configurationController.addCallback(configurationChangedListener)
+        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+        commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
+    }
+
+    @VisibleForTesting
+    public override fun onViewDetached() {
+        keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
+        configurationController.removeCallback(configurationChangedListener)
+        commandRegistry.unregisterCommand("auth-ripple")
+
+        notificationShadeWindowController.setForcePluginOpen(false, this)
+    }
+
+    private fun showRipple(biometricSourceType: BiometricSourceType?) {
+        if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
+            fingerprintSensorLocation != null) {
+            mView.setSensorLocation(fingerprintSensorLocation!!)
+            showRipple()
+        } else if (biometricSourceType == BiometricSourceType.FACE &&
+            faceSensorLocation != null) {
+            mView.setSensorLocation(faceSensorLocation!!)
+            showRipple()
         }
     }
 
-    init {
-        val configurationChangedListener = object : ConfigurationController.ConfigurationListener {
+    private fun showRipple() {
+        notificationShadeWindowController.setForcePluginOpen(true, this)
+        mView.startRipple(Runnable {
+            notificationShadeWindowController.setForcePluginOpen(false, this)
+        })
+    }
+
+    private fun updateSensorLocation() {
+        fingerprintSensorLocation = authController.udfpsSensorLocation
+        faceSensorLocation = authController.faceAuthSensorLocation
+    }
+
+    private fun updateRippleColor() {
+        mView.setColor(
+            Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor)
+    }
+
+    val keyguardUpdateMonitorCallback =
+        object : KeyguardUpdateMonitorCallback() {
+            override fun onBiometricAuthenticated(
+                userId: Int,
+                biometricSourceType: BiometricSourceType?,
+                isStrongBiometric: Boolean
+            ) {
+                showRipple(biometricSourceType)
+            }
+    }
+
+    val configurationChangedListener =
+        object : ConfigurationController.ConfigurationListener {
+            override fun onConfigChanged(newConfig: Configuration?) {
+                updateSensorLocation()
+            }
             override fun onUiModeChanged() {
                 updateRippleColor()
             }
@@ -68,43 +122,50 @@
             override fun onOverlayChanged() {
                 updateRippleColor()
             }
-        }
-        configurationController.addCallback(configurationChangedListener)
-
-        commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
-    }
-
-    fun setSensorLocation(x: Float, y: Float) {
-        rippleView.setSensorLocation(x, y)
-    }
-
-    fun setViewHost(viewHost: View) {
-        // Add the ripple view to its host layout
-        viewHost.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
-            override fun onViewDetachedFromWindow(view: View?) {}
-
-            override fun onViewAttachedToWindow(view: View?) {
-                (viewHost as ViewGroup).addView(rippleView)
-                keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
-                viewHost.removeOnAttachStateChangeListener(this)
-            }
-        })
-
-        updateRippleColor()
-    }
-
-    private fun updateRippleColor() {
-        rippleView.setColor(
-            Utils.getColorAttr(context, android.R.attr.colorAccent).defaultColor)
     }
 
     inner class AuthRippleCommand : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
-            rippleView.startRipple()
+            if (args.isEmpty()) {
+                invalidCommand(pw)
+            } else {
+                when (args[0]) {
+                    "fingerprint" -> {
+                        pw.println("fingerprint ripple sensorLocation=$fingerprintSensorLocation")
+                        showRipple(BiometricSourceType.FINGERPRINT)
+                    }
+                    "face" -> {
+                        pw.println("face ripple sensorLocation=$faceSensorLocation")
+                        showRipple(BiometricSourceType.FACE)
+                    }
+                    "custom" -> {
+                        if (args.size != 3 ||
+                            args[1].toFloatOrNull() == null ||
+                            args[2].toFloatOrNull() == null) {
+                            invalidCommand(pw)
+                            return
+                        }
+                        pw.println("custom ripple sensorLocation=" + args[1].toFloat() + ", " +
+                            args[2].toFloat())
+                        mView.setSensorLocation(PointF(args[1].toFloat(), args[2].toFloat()))
+                        showRipple()
+                    }
+                    else -> invalidCommand(pw)
+                }
+            }
         }
 
         override fun help(pw: PrintWriter) {
-            pw.println("Usage: adb shell cmd statusbar auth-ripple")
+            pw.println("Usage: adb shell cmd statusbar auth-ripple <command>")
+            pw.println("Available commands:")
+            pw.println("  fingerprint")
+            pw.println("  face")
+            pw.println("  custom <x-location: int> <y-location: int>")
+        }
+
+        fun invalidCommand(pw: PrintWriter) {
+            pw.println("invalid command")
+            help(pw)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 1270677..374ddae 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -17,6 +17,7 @@
 
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
 import android.animation.ValueAnimator
 import android.content.Context
 import android.graphics.Canvas
@@ -24,9 +25,11 @@
 import android.graphics.PointF
 import android.util.AttributeSet
 import android.view.View
+import android.view.animation.PathInterpolator
+import com.android.internal.graphics.ColorUtils
 import com.android.systemui.statusbar.charging.RippleShader
 
-private const val RIPPLE_ANIMATION_DURATION: Long = 950
+private const val RIPPLE_ANIMATION_DURATION: Long = 1533
 private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
 
 /**
@@ -36,42 +39,64 @@
 class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
     private var rippleInProgress: Boolean = false
     private val rippleShader = RippleShader()
-    private val defaultColor: Int = 0xffffffff.toInt()
     private val ripplePaint = Paint()
 
     init {
-        rippleShader.color = defaultColor
+        rippleShader.color = 0xffffffff.toInt() // default color
         rippleShader.progress = 0f
         rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
         ripplePaint.shader = rippleShader
-        visibility = View.GONE
+        visibility = GONE
     }
 
-    fun setSensorLocation(x: Float, y: Float) {
-        rippleShader.origin = PointF(x, y)
-        rippleShader.radius = maxOf(x, y, width - x, height - y).toFloat()
+    fun setSensorLocation(location: PointF) {
+        rippleShader.origin = location
+        rippleShader.radius = maxOf(location.x, location.y, width - location.x, height - location.y)
+            .toFloat()
     }
 
-    fun startRipple() {
+    fun startRipple(onAnimationEnd: Runnable?) {
         if (rippleInProgress) {
             return // Ignore if ripple effect is already playing
         }
+
         val animator = ValueAnimator.ofFloat(0f, 1f)
+        animator.interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
         animator.duration = RIPPLE_ANIMATION_DURATION
         animator.addUpdateListener { animator ->
             val now = animator.currentPlayTime
             rippleShader.progress = animator.animatedValue as Float
             rippleShader.time = now.toFloat()
+            rippleShader.distortionStrength = 1 - rippleShader.progress
             invalidate()
         }
-        animator.addListener(object : AnimatorListenerAdapter() {
+        val alphaInAnimator = ValueAnimator.ofInt(0, 127)
+        alphaInAnimator.duration = 167
+        alphaInAnimator.addUpdateListener { alphaInAnimator ->
+            rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
+                alphaInAnimator.animatedValue as Int)
+            invalidate()
+        }
+        val alphaOutAnimator = ValueAnimator.ofInt(127, 0)
+        alphaOutAnimator.startDelay = 417
+        alphaOutAnimator.duration = 1116
+        alphaOutAnimator.addUpdateListener { alphaOutAnimator ->
+            rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
+                alphaOutAnimator.animatedValue as Int)
+            invalidate()
+        }
+
+        val animatorSet = AnimatorSet()
+        animatorSet.playTogether(animator, alphaInAnimator, alphaOutAnimator)
+        animatorSet.addListener(object : AnimatorListenerAdapter() {
             override fun onAnimationEnd(animation: Animator?) {
+                onAnimationEnd?.run()
                 rippleInProgress = false
-                visibility = View.GONE
+                visibility = GONE
             }
         })
-        animator.start()
-        visibility = View.VISIBLE
+        animatorSet.start()
+        visibility = VISIBLE
         rippleInProgress = true
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index f4993f4..d9e1b50 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -23,7 +23,6 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.ViewController;
 
@@ -47,7 +46,6 @@
     @NonNull final DumpManager mDumpManger;
 
     private boolean mNotificationShadeExpanded;
-    private int mStatusBarState;
 
     protected UdfpsAnimationViewController(
             T view,
@@ -86,7 +84,6 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("mStatusBarState=" + StatusBarState.toShortString(mStatusBarState));
         pw.println("mNotificationShadeExpanded=" + mNotificationShadeExpanded);
         pw.println("shouldPauseAuth()=" + shouldPauseAuth());
         pw.println("isPauseAuth=" + mView.isPauseAuth());
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 2bdbf51..aa818bf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -36,6 +37,7 @@
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -53,6 +55,7 @@
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -89,9 +92,9 @@
     @NonNull private final StatusBarStateController mStatusBarStateController;
     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
     @NonNull private final DumpManager mDumpManager;
-    @NonNull private final AuthRippleController mAuthRippleController;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
+    @NonNull private FalsingManager mFalsingManager;
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
     @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -241,6 +244,8 @@
                 }
                 // TODO: move isWithinSensorArea to UdfpsController.
                 if (udfpsView.isWithinSensorArea(event.getX(), event.getY())) {
+                    Trace.beginAsyncSection(
+                            "UdfpsController.mOnTouchListener#isWithinSensorArea", 1);
                     // The pointer that causes ACTION_DOWN is always at index 0.
                     // We need to persist its ID to track it during ACTION_MOVE that could include
                     // data for many other pointers because of multi-touch support.
@@ -267,6 +272,8 @@
                                 minor, major, v);
                         final long sinceLastLog = SystemClock.elapsedRealtime() - mTouchLogTime;
                         if (!isFingerDown) {
+                            Trace.endAsyncSection(
+                                    "UdfpsController.mOnTouchListener#isWithinSensorArea", 1);
                             onFingerDown((int) x, (int) y, minor, major);
                             Log.v(TAG, "onTouch | finger down: " + touchInfo);
                             mTouchLogTime = SystemClock.elapsedRealtime();
@@ -292,6 +299,8 @@
                     Log.v(TAG, "onTouch | finger up");
                     onFingerUp();
                 }
+                mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
+
                 break;
 
             default:
@@ -311,9 +320,9 @@
             @NonNull StatusBar statusBar,
             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             @NonNull DumpManager dumpManager,
-            @NonNull AuthRippleController authRippleController,
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
-            @NonNull KeyguardViewMediator keyguardViewMediator) {
+            @NonNull KeyguardViewMediator keyguardViewMediator,
+            @NonNull FalsingManager falsingManager) {
         mContext = context;
         mInflater = inflater;
         // The fingerprint manager is queried for UDFPS before this class is constructed, so the
@@ -325,9 +334,9 @@
         mStatusBarStateController = statusBarStateController;
         mKeyguardViewManager = statusBarKeyguardViewManager;
         mDumpManager = dumpManager;
-        mAuthRippleController = authRippleController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mKeyguardViewMediator = keyguardViewMediator;
+        mFalsingManager = falsingManager;
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -353,10 +362,6 @@
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         context.registerReceiver(mBroadcastReceiver, filter);
-
-        mAuthRippleController.setViewHost(mStatusBar.getNotificationShadeWindowView());
-        mAuthRippleController.setSensorLocation(getSensorLocation().centerX(),
-                getSensorLocation().centerY());
     }
 
     @Nullable
@@ -584,8 +589,11 @@
             Log.w(TAG, "Null view in onFingerDown");
             return;
         }
-        mView.startIllumination(() ->
-                mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major));
+        Trace.beginAsyncSection("UdfpsController#startIllumination", 1);
+        mView.startIllumination(() -> {
+            Trace.endAsyncSection("UdfpsController#startIllumination", 1);
+            mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major);
+        });
     }
 
     // This method can be called from the UI thread.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index dc0c685..35678e6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -91,6 +91,7 @@
         mStatusBarStateController.addCallback(mStateListener);
         mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
         mStateListener.onStateChanged(mStatusBarStateController.getState());
+        mAlternateAuthInterceptor.setQsExpanded(mKeyguardViewManager.isQsExpanded());
         mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
     }
 
@@ -117,6 +118,8 @@
         pw.println("mShowBouncer=" + mShowBouncer);
         pw.println("mFaceDetectRunning=" + mFaceDetectRunning);
         pw.println("mTransitioningFromHomeToKeyguard=" + mTransitioningFromHome);
+        pw.println("mStatusBarState" + StatusBarState.toShortString(mStatusBarState));
+        pw.println("mQsExpanded=" + mQsExpanded);
     }
 
     /**
@@ -215,6 +218,7 @@
         public void onStateChanged(int statusBarState) {
             mStatusBarState = statusBarState;
             mView.setStatusBarState(statusBarState);
+            updatePauseAuth();
         }
     };
 
@@ -283,7 +287,7 @@
 
                 @Override
                 public void dump(PrintWriter pw) {
-                    pw.print(getTag());
+                    pw.println(getTag());
                 }
             };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index f7beaf1..fb7fabb 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -114,9 +114,11 @@
             params.height = WindowManager.LayoutParams.MATCH_PARENT;
             params.width = WindowManager.LayoutParams.MATCH_PARENT;
             params.format = PixelFormat.TRANSLUCENT;
-
             params.type = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
             params.setTitle("Charging Animation");
+            params.layoutInDisplayCutoutMode =
+                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+            params.setFitInsetsTypes(0 /* ignore all system bar insets */);
             params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                     | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 5bdc7a4..e2c62ed 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -218,18 +218,43 @@
     }
 
     @Override
-    public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
+    public boolean isSimpleTap() {
+        FalsingClassifier.Result result = mSingleTapClassifier.isTap(
+                mDataProvider.getRecentMotionEvents(), 0);
+        mPriorResults = Collections.singleton(result);
+
+        return !result.isFalse();
+    }
+
+    @Override
+    public boolean isFalseTap(@Penalty int penalty) {
         if (skipFalsing()) {
             return false;
         }
 
+        double falsePenalty = 0;
+        switch(penalty) {
+            case NO_PENALTY:
+                falsePenalty = 0;
+                break;
+            case LOW_PENALTY:
+                falsePenalty = 0.1;
+                break;
+            case MODERATE_PENALTY:
+                falsePenalty = 0.3;
+                break;
+            case HIGH_PENALTY:
+                falsePenalty = 0.6;
+                break;
+        }
+
         FalsingClassifier.Result singleTapResult =
                 mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents().isEmpty()
                         ? mDataProvider.getPriorMotionEvents()
-                        : mDataProvider.getRecentMotionEvents());
+                        : mDataProvider.getRecentMotionEvents(), falsePenalty);
         mPriorResults = Collections.singleton(singleTapResult);
 
-        if (!singleTapResult.isFalse() && robustCheck) {
+        if (!singleTapResult.isFalse()) {
             if (mDataProvider.isJustUnlockedWithFace()) {
                 // Immediately pass if a face is detected.
                 mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 47b41f5..4dd8780 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -38,6 +38,8 @@
     public static final int BOUNCER_UNLOCK = 8;
     public static final int PULSE_EXPAND = 9;
     public static final int BRIGHTNESS_SLIDER = 10;
+    public static final int UDFPS_AUTHENTICATION = 11;
+    public static final int DISABLED_UDFPS_AFFORDANCE = 12;
 
     @IntDef({
             QUICK_SETTINGS,
@@ -50,7 +52,9 @@
             GENERIC,
             BOUNCER_UNLOCK,
             PULSE_EXPAND,
-            BRIGHTNESS_SLIDER
+            BRIGHTNESS_SLIDER,
+            UDFPS_AUTHENTICATION,
+            DISABLED_UDFPS_AFFORDANCE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InteractionType {}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index 80d7863..6a70622 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -148,7 +148,9 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == Classifier.BRIGHTNESS_SLIDER) {
+        if (interactionType == Classifier.BRIGHTNESS_SLIDER
+                || interactionType == Classifier.UDFPS_AUTHENTICATION
+                || interactionType == Classifier.DISABLED_UDFPS_AFFORDANCE) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
index e7c9d18..b6d529d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
@@ -66,13 +66,13 @@
     public boolean isDoubleTap(List<MotionEvent> firstEvents, List<MotionEvent> secondEvents,
             StringBuilder reason) {
 
-        Result firstTap = mSingleTapClassifier.isTap(firstEvents);
+        Result firstTap = mSingleTapClassifier.isTap(firstEvents, 0.5);
         if (firstTap.isFalse()) {
             reason.append("First gesture is not a tap. ").append(firstTap.getReason());
             return false;
         }
 
-        Result secondTap = mSingleTapClassifier.isTap(secondEvents);
+        Result secondTap = mSingleTapClassifier.isTap(secondEvents, 0.5);
         if (secondTap.isFalse()) {
             reason.append("Second gesture is not a tap. ").append(secondTap.getReason());
             return false;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index d39f124..e557773 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -32,7 +32,7 @@
  */
 public class FalsingManagerFake implements FalsingManager {
     private boolean mIsFalseTouch;
-    private boolean mIsFalseTap;
+    private boolean mIsSimpleTap;
     private boolean mIsFalseDoubleTap;
     private boolean mIsUnlockingDisabled;
     private boolean mIsClassiferEnabled;
@@ -67,12 +67,12 @@
         return mIsFalseTouch;
     }
 
-    public void setFalseRobustTap(boolean falseRobustTap) {
+    public void setFalseTap(boolean falseRobustTap) {
         mIsFalseRobustTap = falseRobustTap;
     }
 
-    public void setFalseTap(boolean falseTap) {
-        mIsFalseTap = falseTap;
+    public void setSimpleTap(boolean isSimpleTape) {
+        mIsSimpleTap = isSimpleTape;
     }
 
     public void setFalseDoubleTap(boolean falseDoubleTap) {
@@ -80,8 +80,13 @@
     }
 
     @Override
-    public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
-        return robustCheck ? mIsFalseRobustTap : mIsFalseTap;
+    public boolean isSimpleTap() {
+        return mIsSimpleTap;
+    }
+
+    @Override
+    public boolean isFalseTap(@Penalty int penalty) {
+        return mIsFalseRobustTap;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 9c29f27..1723291 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -131,8 +131,13 @@
     }
 
     @Override
-    public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
-        return mInternalFalsingManager.isFalseTap(robustCheck, falsePenalty);
+    public boolean isSimpleTap() {
+        return mInternalFalsingManager.isSimpleTap();
+    }
+
+    @Override
+    public boolean isFalseTap(@Penalty int penalty) {
+        return mInternalFalsingManager.isFalseTap(penalty);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
index 68a9e5f..bd6fbfb 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
@@ -42,11 +42,11 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        return isTap(getRecentMotionEvents());
+        return isTap(getRecentMotionEvents(), 0.5);
     }
 
     /** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */
-    public Result isTap(List<MotionEvent> motionEvents) {
+    public Result isTap(List<MotionEvent> motionEvents, double falsePenalty) {
         if (motionEvents.isEmpty()) {
             return falsed(0, "no motion events");
         }
@@ -60,13 +60,13 @@
                         + Math.abs(event.getX() - downX)
                         + "vs "
                         + mTouchSlop;
-                return falsed(0.5, reason);
+                return falsed(falsePenalty, reason);
             } else if (Math.abs(event.getY() - downY) >= mTouchSlop) {
                 reason = "dY too big for a tap: "
                         + Math.abs(event.getY() - downY)
                         + " vs "
                         + mTouchSlop;
-                return falsed(0.5, reason);
+                return falsed(falsePenalty, reason);
             }
         }
         return Result.passed(0);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index f665565..50e94b3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -42,6 +42,11 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
+        if (interactionType == Classifier.UDFPS_AUTHENTICATION
+                || interactionType == Classifier.DISABLED_UDFPS_AFFORDANCE) {
+            return Result.passed(0);
+        }
+
         boolean vertical = isVertical();
         boolean up = isUp();
         boolean right = isRight();
diff --git a/packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt
index 10b36e9..bc45113 100644
--- a/packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt
@@ -28,6 +28,7 @@
 import android.graphics.Outline
 import android.graphics.Paint
 import android.graphics.PixelFormat
+import android.graphics.Xfermode
 import android.graphics.drawable.Drawable
 import android.util.AttributeSet
 import android.util.MathUtils
@@ -48,7 +49,15 @@
 class IlluminationDrawable : Drawable() {
 
     private var themeAttrs: IntArray? = null
-    private var cornerRadius = 0f
+    private var cornerRadiusOverride = -1f
+    var cornerRadius = 0f
+    get() {
+        return if (cornerRadiusOverride >= 0) {
+            cornerRadiusOverride
+        } else {
+            field
+        }
+    }
     private var highlightColor = Color.TRANSPARENT
     private var tmpHsl = floatArrayOf(0f, 0f, 0f)
     private var paint = Paint()
@@ -122,8 +131,28 @@
         throw UnsupportedOperationException("Color filters are not supported")
     }
 
-    override fun setAlpha(value: Int) {
-        throw UnsupportedOperationException("Alpha is not supported")
+    override fun setAlpha(alpha: Int) {
+        if (alpha == paint.alpha) {
+            return
+        }
+
+        paint.alpha = alpha
+        invalidateSelf()
+
+        lightSources.forEach { it.alpha = alpha }
+    }
+
+    override fun getAlpha(): Int {
+        return paint.alpha
+    }
+
+    override fun setXfermode(mode: Xfermode?) {
+        if (mode == paint.xfermode) {
+            return
+        }
+
+        paint.xfermode = mode
+        invalidateSelf()
     }
 
     /**
@@ -171,9 +200,19 @@
 
     fun registerLightSource(lightSource: View) {
         if (lightSource.background is LightSourceDrawable) {
-            lightSources.add(lightSource.background as LightSourceDrawable)
+            registerLightSource(lightSource.background as LightSourceDrawable)
         } else if (lightSource.foreground is LightSourceDrawable) {
-            lightSources.add(lightSource.foreground as LightSourceDrawable)
+            registerLightSource(lightSource.foreground as LightSourceDrawable)
         }
     }
+
+    private fun registerLightSource(lightSource: LightSourceDrawable) {
+        lightSource.alpha = paint.alpha
+        lightSources.add(lightSource)
+    }
+
+    /** Set or remove the corner radius override. This is typically set during animations. */
+    fun setCornerRadiusOverride(cornerRadius: Float?) {
+        cornerRadiusOverride = cornerRadius ?: -1f
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/LightSourceDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/LightSourceDrawable.kt
index cee7101..e6035f3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/LightSourceDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/LightSourceDrawable.kt
@@ -184,8 +184,13 @@
         throw UnsupportedOperationException("Color filters are not supported")
     }
 
-    override fun setAlpha(value: Int) {
-        throw UnsupportedOperationException("Alpha is not supported")
+    override fun setAlpha(alpha: Int) {
+        if (alpha == paint.alpha) {
+            return
+        }
+
+        paint.alpha = alpha
+        invalidateSelf()
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index d3ae932..4fd8fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -49,6 +49,8 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
+import com.android.systemui.plugins.animation.GhostedViewLaunchAnimatorController;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.util.animation.TransitionLayout;
 
@@ -101,11 +103,12 @@
     // This will provide the corners for the album art.
     private final ViewOutlineProvider mViewOutlineProvider;
     private final MediaOutputDialogFactory mMediaOutputDialogFactory;
+
     /**
      * Initialize a new control panel
-     * @param context
+     *
      * @param backgroundExecutor background executor, used for processing artwork
-     * @param activityStarter activity starter
+     * @param activityStarter    activity starter
      */
     @Inject
     public MediaControlPanel(Context context, @Background Executor backgroundExecutor,
@@ -147,6 +150,7 @@
 
     /**
      * Get the view holder used to display media controls
+     *
      * @return the view holder
      */
     @Nullable
@@ -156,6 +160,7 @@
 
     /**
      * Get the view controller used to display media controls
+     *
      * @return the media view controller
      */
     @NonNull
@@ -165,7 +170,7 @@
 
     /**
      * Sets the listening state of the player.
-     *
+     * <p>
      * Should be set to true when the QS panel is open. Otherwise, false. This is a signal to avoid
      * unnecessary work when the QS panel is closed.
      *
@@ -177,6 +182,7 @@
 
     /**
      * Get the context
+     *
      * @return context
      */
     public Context getContext() {
@@ -244,7 +250,8 @@
         if (clickIntent != null) {
             mViewHolder.getPlayer().setOnClickListener(v -> {
                 if (mMediaViewController.isGutsVisible()) return;
-                mActivityStarter.postStartActivityDismissingKeyguard(clickIntent);
+                mActivityStarter.postStartActivityDismissingKeyguard(clickIntent,
+                        buildLaunchAnimatorController(mViewHolder.getPlayer()));
             });
         }
 
@@ -396,8 +403,42 @@
         mMediaViewController.refreshState();
     }
 
+    @Nullable
+    private ActivityLaunchAnimator.Controller buildLaunchAnimatorController(
+            TransitionLayout player) {
+        // TODO(b/174236650): Make sure that the carousel indicator also fades out.
+        // TODO(b/174236650): Instrument the animation to measure jank.
+        return new GhostedViewLaunchAnimatorController(player) {
+            @Override
+            protected float getCurrentTopCornerRadius() {
+                return ((IlluminationDrawable) player.getBackground()).getCornerRadius();
+            }
+
+            @Override
+            protected float getCurrentBottomCornerRadius() {
+                // TODO(b/184121838): Make IlluminationDrawable support top and bottom radius.
+                return getCurrentTopCornerRadius();
+            }
+
+            @Override
+            protected void setBackgroundCornerRadius(Drawable background, float topCornerRadius,
+                    float bottomCornerRadius) {
+                // TODO(b/184121838): Make IlluminationDrawable support top and bottom radius.
+                float radius = Math.min(topCornerRadius, bottomCornerRadius);
+                ((IlluminationDrawable) background).setCornerRadiusOverride(radius);
+            }
+
+            @Override
+            public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
+                super.onLaunchAnimationEnd(isExpandingFullyAbove);
+                ((IlluminationDrawable) player.getBackground()).setCornerRadiusOverride(null);
+            }
+        };
+    }
+
     /**
      * Close the guts for this player.
+     *
      * @param immediate {@code true} if it should be closed without animation
      */
     public void closeGuts(boolean immediate) {
@@ -427,7 +468,7 @@
         if (bounds.width() > mAlbumArtSize || bounds.height() > mAlbumArtSize) {
             float offsetX = (bounds.width() - mAlbumArtSize) / 2.0f;
             float offsetY = (bounds.height() - mAlbumArtSize) / 2.0f;
-            bounds.offset((int) -offsetX,(int) -offsetY);
+            bounds.offset((int) -offsetX, (int) -offsetY);
         }
         drawable.setBounds(bounds);
         return drawable;
@@ -435,6 +476,7 @@
 
     /**
      * Get the current media controller
+     *
      * @return the controller
      */
     public MediaController getController() {
@@ -443,6 +485,7 @@
 
     /**
      * Check whether the media controlled by this player is currently playing
+     *
      * @return whether it is playing, or false if no controller information
      */
     public boolean isPlaying() {
@@ -451,6 +494,7 @@
 
     /**
      * Check whether the given controller is currently playing
+     *
      * @param controller media controller to check
      * @return whether it is playing, or false if no controller information
      */
@@ -468,7 +512,7 @@
     }
 
     private void setVisibleAndAlpha(ConstraintSet set, int actionId, boolean visible) {
-        set.setVisibility(actionId, visible? ConstraintSet.VISIBLE : ConstraintSet.GONE);
+        set.setVisibility(actionId, visible ? ConstraintSet.VISIBLE : ConstraintSet.GONE);
         set.setAlpha(actionId, visible ? 1.0f : 0.0f);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 5f6d95f..0fbe1f7 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -319,6 +319,7 @@
         CharSequence content = (isMissedCall && !hasMessageText)
                 ? context.getString(R.string.missed_call) : message.getText();
         Uri dataUri = message != null ? message.getDataUri() : null;
+        if (DEBUG) Log.d(TAG, "Notification message has text: " + hasMessageText);
 
         return tile
                 .toBuilder()
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 96fbe69..3bc91bc 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -391,6 +391,7 @@
             statusText = getStatusTextByType(status.getActivity());
         }
         views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
+        views.setViewVisibility(R.id.messages_count, View.GONE);
         setMaxLines(views);
         // Secondary text color for statuses.
         TypedValue typedValue = new TypedValue();
@@ -530,6 +531,7 @@
         if (mLayoutSize == LAYOUT_SMALL) {
             views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
             views.setViewVisibility(R.id.name, View.GONE);
+            views.setViewVisibility(R.id.messages_count, View.GONE);
         } else {
             views.setViewVisibility(R.id.predefined_icon, View.GONE);
             views.setViewVisibility(R.id.name, View.VISIBLE);
@@ -544,6 +546,7 @@
         if (mLayoutSize == LAYOUT_SMALL) {
             views.setViewVisibility(R.id.name, View.VISIBLE);
             views.setViewVisibility(R.id.predefined_icon, View.GONE);
+            views.setViewVisibility(R.id.messages_count, View.GONE);
         }
         String status = PeopleSpaceUtils.getLastInteractionString(mContext,
                 mTile.getLastInteractionTimestamp());
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 5be2d4a..117be47 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -285,7 +285,7 @@
                 updateTilesByUri(key, sbn, action);
             }
         } catch (Exception e) {
-            Log.e(TAG, "Exception: " + e);
+            Log.e(TAG, "Throwing exception: " + e);
         }
     }
 
@@ -498,7 +498,7 @@
         if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
             if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
             storedTile = augmentTileFromNotification(mContext, storedTile, sbn);
-        } else if (storedTile.getNotificationKey().equals(sbn.getKey())) {
+        } else if (Objects.equals(storedTile.getNotificationKey(), sbn.getKey())) {
             if (DEBUG) {
                 Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 9a889e0..980024e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -225,16 +225,7 @@
             mQsPanelCallback.onScanStateChanged(false);
         }
         sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-
-        if (mShouldAnimate) {
-            animateDetailVisibleDiff(x, y, visibleDiff, listener);
-        } else {
-            if (showingDetail) {
-                showImmediately();
-            } else {
-                hideImmediately();
-            }
-        }
+        animateDetailVisibleDiff(x, y, visibleDiff, listener);
     }
 
     protected void animateDetailVisibleDiff(int x, int y, boolean visibleDiff, AnimatorListener listener) {
@@ -242,27 +233,16 @@
             mAnimatingOpen = mDetailAdapter != null;
             if (mFullyExpanded || mDetailAdapter != null) {
                 setAlpha(1);
-                mClipper.animateCircularClip(x, y, mDetailAdapter != null, listener);
+                mClipper.updateCircularClip(mShouldAnimate, x, y, mDetailAdapter != null, listener);
             } else {
                 animate().alpha(0)
-                        .setDuration(FADE_DURATION)
+                        .setDuration(mShouldAnimate ? FADE_DURATION : 0)
                         .setListener(listener)
                         .start();
             }
         }
     }
 
-    void showImmediately() {
-        setVisibility(VISIBLE);
-        mClipper.cancelAnimator();
-        mClipper.showBackground();
-    }
-
-    public void hideImmediately() {
-        mClipper.cancelAnimator();
-        setVisibility(View.GONE);
-    }
-
     protected void setupDetailFooter(DetailAdapter adapter) {
         final Intent settingsIntent = adapter.getSettingsIntent();
         mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index daf8ca3..63cedd0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -37,6 +37,19 @@
     }
 
     public void animateCircularClip(int x, int y, boolean in, AnimatorListener listener) {
+        updateCircularClip(true /* animate */, x, y, in, listener);
+    }
+
+    /**
+     * @param animate whether or not animation has a duration of 0. Either way, {@code listener}
+     *               will be called.
+     * @param x x position where animation should originate
+     * @param y y position where animation should originate
+     * @param in whether animating in or out
+     * @param listener Animation listener. Called whether or not {@code animate} is true.
+     */
+    public void updateCircularClip(boolean animate, int x, int y, boolean in,
+            AnimatorListener listener) {
         if (mAnimator != null) {
             mAnimator.cancel();
         }
@@ -58,15 +71,16 @@
         } else {
             mAnimator = ViewAnimationUtils.createCircularReveal(mDetail, x, y, r, innerR);
         }
-        mAnimator.setDuration((long)(mAnimator.getDuration() * 1.5));
+        mAnimator.setDuration(animate ? (long) (mAnimator.getDuration() * 1.5) : 0);
         if (listener != null) {
             mAnimator.addListener(listener);
         }
         if (in) {
-            mBackground.startTransition((int)(mAnimator.getDuration() * 0.6));
+            mBackground.startTransition(animate ? (int) (mAnimator.getDuration() * 0.6) : 0);
             mAnimator.addListener(mVisibleOnStart);
         } else {
-            mDetail.postDelayed(mReverseBackground, (long)(mAnimator.getDuration() * 0.65));
+            mDetail.postDelayed(mReverseBackground,
+                    animate ? (long) (mAnimator.getDuration() * 0.65) : 0);
             mAnimator.addListener(mGoneOnEnd);
         }
         mAnimator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 52e05a4..3467838 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -34,6 +34,7 @@
 import com.android.systemui.R;
 import com.android.systemui.globalactions.GlobalActionsDialogLite;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.MultiUserSwitch;
@@ -62,6 +63,7 @@
     private final QuickQSPanelController mQuickQSPanelController;
     private final TunerService mTunerService;
     private final MetricsLogger mMetricsLogger;
+    private final FalsingManager mFalsingManager;
     private final SettingsButton mSettingsButton;
     private final TextView mBuildText;
     private final View mEdit;
@@ -83,8 +85,9 @@
     private final View.OnClickListener mSettingsOnClickListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            // Don't do anything until view are unhidden
-            if (!mExpanded) {
+            // Don't do anything until views are unhidden. Don't do anything if the tap looks
+            // suspicious.
+            if (!mExpanded || mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
                 return;
             }
 
@@ -132,7 +135,7 @@
             DeviceProvisionedController deviceProvisionedController, UserTracker userTracker,
             QSPanelController qsPanelController, QSDetailDisplayer qsDetailDisplayer,
             QuickQSPanelController quickQSPanelController,
-            TunerService tunerService, MetricsLogger metricsLogger,
+            TunerService tunerService, MetricsLogger metricsLogger, FalsingManager falsingManager,
             @Named(PM_LITE_ENABLED) boolean showPMLiteButton,
             GlobalActionsDialogLite globalActionsDialog) {
         super(view);
@@ -146,6 +149,7 @@
         mQuickQSPanelController = quickQSPanelController;
         mTunerService = tunerService;
         mMetricsLogger = metricsLogger;
+        mFalsingManager = falsingManager;
 
         mSettingsButton = mView.findViewById(R.id.settings_button);
         mBuildText = mView.findViewById(R.id.build);
@@ -184,9 +188,13 @@
             return false;
         });
 
-        mEdit.setOnClickListener(view ->
-                mActivityStarter.postQSRunnableDismissingKeyguard(() ->
-                        mQsPanelController.showEdit(view)));
+        mEdit.setOnClickListener(view -> {
+            if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+                return;
+            }
+            mActivityStarter.postQSRunnableDismissingKeyguard(() ->
+                    mQsPanelController.showEdit(view));
+        });
 
         mMultiUserSwitch.setQSDetailDisplayer(mQsDetailDisplayer);
         mQsPanelController.setFooterPageIndicator(mPageIndicator);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index a45b1319..abe3219 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -173,7 +173,7 @@
     @Override
     public void init(QSTile tile) {
         init(v -> tile.click(), v -> tile.secondaryClick(), view -> {
-            tile.longClick();
+            tile.longClick(this);
             return true;
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index e8fc245b..375c79f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -31,6 +31,7 @@
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
@@ -43,6 +44,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.View;
 
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleOwner;
@@ -58,6 +60,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
@@ -276,7 +279,7 @@
         mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_CLICK, 0, getMetricsSpec(),
                 getInstanceId());
         mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
-        if (!mFalsingManager.isFalseTap(true, 0.1)) {
+        if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
             mHandler.sendEmptyMessage(H.CLICK);
         }
     }
@@ -292,14 +295,15 @@
         mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
     }
 
-    public void longClick() {
+    @Override
+    public void longClick(@Nullable View view) {
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)
                 .addTaggedData(FIELD_STATUS_BAR_STATE,
                         mStatusBarStateController.getState())));
         mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_LONG_PRESS, 0, getMetricsSpec(),
                 getInstanceId());
         mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
-        mHandler.sendEmptyMessage(H.LONG_CLICK);
+        mHandler.obtainMessage(H.LONG_CLICK, view).sendToTarget();
     }
 
     public LogMaker populate(LogMaker logMaker) {
@@ -374,10 +378,15 @@
 
     /**
      * Handles long click on the tile by launching the {@link Intent} defined in
-     * {@link QSTileImpl#getLongClickIntent}
+     * {@link QSTileImpl#getLongClickIntent}.
+     *
+     * @param view The view from which the opening window will be animated.
      */
-    protected void handleLongClick() {
-        mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0);
+    protected void handleLongClick(@Nullable View view) {
+        ActivityLaunchAnimator.Controller animationController =
+                view != null ? ActivityLaunchAnimator.Controller.fromView(view) : null;
+        mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
+                animationController);
     }
 
     /**
@@ -614,7 +623,7 @@
                     handleSecondaryClick();
                 } else if (msg.what == LONG_CLICK) {
                     name = "handleLongClick";
-                    handleLongClick();
+                    handleLongClick((View) msg.obj);
                 } else if (msg.what == REFRESH_STATE) {
                     name = "handleRefreshState";
                     handleRefreshState(msg.obj);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index 7a8b2c6..b953323 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -118,7 +118,11 @@
     override fun setClickable(clickable: Boolean) {
         super.setClickable(clickable)
         background = if (clickable && mShowRippleEffect) {
-            mTileBackground
+            mRipple?.also {
+                // In case that the colorBackgroundDrawable was used as the background, make sure
+                // it has the correct callback instead of null
+                colorBackgroundDrawable?.callback = it
+            }
         } else {
             colorBackgroundDrawable
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index fa99eed..d78dbae9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -142,7 +142,7 @@
     }
 
     @Override
-    protected void handleLongClick() {
+    protected void handleLongClick(View view) {
         handleClick();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 31a98db..b7cb615 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -22,6 +22,7 @@
 import android.os.Looper;
 import android.provider.MediaStore;
 import android.service.quicksettings.Tile;
+import android.view.View;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
@@ -107,7 +108,7 @@
     }
 
     @Override
-    protected void handleLongClick() {
+    protected void handleLongClick(View view) {
         handleClick();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index bf96558..47212d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.wallet.ui.WalletActivity;
 
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -117,8 +118,22 @@
 
     @Override
     protected void handleClick() {
-        mActivityStarter.postStartActivityDismissingKeyguard(
-                mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0);
+        mUiHandler.post(() -> {
+            mHost.collapsePanels();
+            if (mHasCard) {
+                Intent intent = new Intent(mContext, WalletActivity.class)
+                        .setAction(Intent.ACTION_VIEW)
+                        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+                mContext.startActivity(intent);
+            } else {
+                if (mQuickAccessWalletClient.createWalletIntent() == null) {
+                    Log.w(TAG, "Could not get intent of the wallet app.");
+                    return;
+                }
+                mActivityStarter.postStartActivityDismissingKeyguard(
+                        mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0);
+            }
+        });
     }
 
     @Override
@@ -147,9 +162,7 @@
         } else {
             state.state = Tile.STATE_UNAVAILABLE;
         }
-        if (!isDeviceLocked) {
-            state.sideViewDrawable = mCardViewDrawable;
-        }
+        state.sideViewDrawable = isDeviceLocked ? null : mCardViewDrawable;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 8e6398f..9525975 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -105,9 +105,7 @@
             }
         } else if (view.getLayerType() == View.LAYER_TYPE_HARDWARE
                 && view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) {
-            if (view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) {
-                view.setLayerType(View.LAYER_TYPE_NONE, null);
-            }
+            view.setLayerType(View.LAYER_TYPE_NONE, null);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index f51fbed..ec3a857 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -97,4 +97,8 @@
     public boolean isOngoingCallStatusBarChipEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_ongoing_call_status_bar_chip);
     }
+
+    public boolean isSmartspaceEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_smartspace);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 2856ebb..23e6a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -576,6 +576,7 @@
             mKeysKeptForRemoteInputHistory.remove(key);
         }
         if (mRemoteInputController.isRemoteInputActive(entry)) {
+            entry.mRemoteEditImeVisible = false;
             mRemoteInputController.removeRemoteInput(entry, null);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index e27c1a2..bb6165a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -35,7 +35,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
 import com.android.systemui.statusbar.phone.DozeParameters
@@ -107,11 +107,18 @@
                 else 0)
         }
 
+    var qsPanelExpansion = 0f
+        set(value) {
+            if (field == value) return
+            field = value
+            scheduleUpdate()
+        }
+
     /**
      * When launching an app from the shade, the animations progress should affect how blurry the
      * shade is, overriding the expansion amount.
      */
-    var notificationLaunchAnimationParams: ActivityLaunchAnimator.ExpandAnimationParameters? = null
+    var notificationLaunchAnimationParams: ExpandAnimationParameters? = null
         set(value) {
             field = value
             if (value != null) {
@@ -158,8 +165,9 @@
         updateScheduled = false
         val normalizedBlurRadius = MathUtils.constrain(shadeAnimation.radius,
                 blurUtils.minBlurRadius, blurUtils.maxBlurRadius)
-        val combinedBlur = (shadeSpring.radius * INTERACTION_BLUR_FRACTION +
+        var combinedBlur = (shadeSpring.radius * INTERACTION_BLUR_FRACTION +
                 normalizedBlurRadius * ANIMATION_BLUR_FRACTION).toInt()
+        combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsPanelExpansion))
         var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius).toFloat()
         shadeRadius *= 1f - brightnessMirrorSpring.ratio
         val launchProgress = notificationLaunchAnimationParams?.linearProgress ?: 0f
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 24515f7..b6357b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -90,9 +90,14 @@
     /** Sets the state of whether the user activities are forced or not. */
     default void setForceUserActivity(boolean forceUserActivity) {}
 
-    /** Sets the state of whether the user activities are forced or not. */
+    /** Sets the state of whether an activity is launching or not. */
     default void setLaunchingActivity(boolean launching) {}
 
+    /** Get whether an activity is launching or not. */
+    default boolean isLaunchingActivity() {
+        return false;
+    }
+
     /** Sets the state of whether the scrim is visible or not. */
     default void setScrimsVisibility(int scrimsVisibility) {}
 
@@ -137,7 +142,7 @@
     default void setDozing(boolean dozing) {}
 
     /** Sets the state of whether plugin open is forced or not. */
-    default void setForcePluginOpen(boolean forcePluginOpen) {}
+    default void setForcePluginOpen(boolean forcePluginOpen, Object token) {}
 
     /** Gets whether we are forcing plugin open or not. */
     default boolean getForcePluginOpen() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index 6023b7f..c811fdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -26,17 +26,18 @@
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
-import android.os.Handler;
 import android.os.Looper;
 import android.util.AttributeSet;
 import android.view.View;
 
+import androidx.annotation.DimenRes;
 import androidx.core.graphics.ColorUtils;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.colorextraction.drawable.ScrimDrawable;
+import com.android.systemui.R;
 
 import java.util.concurrent.Executor;
 
@@ -47,6 +48,10 @@
  * need to be careful to synchronize when necessary.
  */
 public class ScrimView extends View {
+
+    @DimenRes
+    private static final int CORNER_RADIUS = R.dimen.notification_scrim_corner_radius;
+
     private final Object mColorLock = new Object();
 
     @GuardedBy("mColorLock")
@@ -260,4 +265,27 @@
             mExecutor.execute(r);
         }
     }
+
+    /**
+     * Make bottom edge concave so overlap between layers is not visible for alphas between 0 and 1
+     * @return height of concavity
+     */
+    public float enableBottomEdgeConcave() {
+        if (mDrawable instanceof ScrimDrawable) {
+            float radius = getResources().getDimensionPixelSize(CORNER_RADIUS);
+            ((ScrimDrawable) mDrawable).setBottomEdgeConcave(radius);
+            return radius;
+        }
+        return 0;
+    }
+
+    /**
+     * Enable view to have rounded corners with radius of {@link #CORNER_RADIUS}
+     */
+    public void enableRoundedCorners() {
+        if (mDrawable instanceof ScrimDrawable) {
+            int radius = getResources().getDimensionPixelSize(CORNER_RADIUS);
+            ((ScrimDrawable) mDrawable).setRoundedCorners(radius);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
index 77b4186..3196eba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
@@ -33,11 +33,11 @@
  * Expanding ripple effect that shows when charging begins.
  */
 class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
-    private var rippleInProgress: Boolean = false
     private val rippleShader = RippleShader()
     private val defaultColor: Int = 0xffffffff.toInt()
     private val ripplePaint = Paint()
 
+    var rippleInProgress: Boolean = false
     var radius: Float = 0.0f
         set(value) { rippleShader.radius = value }
     var origin: PointF = PointF()
@@ -62,7 +62,8 @@
         super.onAttachedToWindow()
     }
 
-    fun startRipple() {
+    @JvmOverloads
+    fun startRipple(onAnimationEnd: Runnable? = null) {
         if (rippleInProgress) {
             return // Ignore if ripple effect is already playing
         }
@@ -80,6 +81,7 @@
             override fun onAnimationEnd(animation: Animator?) {
                 rippleInProgress = false
                 visibility = View.GONE
+                onAnimationEnd?.run()
             }
         })
         animator.start()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 2900462..71af271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -17,11 +17,11 @@
 package com.android.systemui.statusbar.charging
 
 import android.content.Context
-import android.content.res.Configuration
+import android.graphics.PixelFormat
 import android.graphics.PointF
 import android.util.DisplayMetrics
 import android.view.View
-import android.view.ViewGroupOverlay
+import android.view.WindowManager
 import com.android.internal.annotations.VisibleForTesting
 import com.android.settingslib.Utils
 import com.android.systemui.dagger.SysUISingleton
@@ -30,9 +30,7 @@
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.KeyguardStateController
 import java.io.PrintWriter
-import java.lang.Integer.max
 import javax.inject.Inject
 
 /***
@@ -45,11 +43,22 @@
     batteryController: BatteryController,
     configurationController: ConfigurationController,
     featureFlags: FeatureFlags,
-    private val context: Context,
-    private val keyguardStateController: KeyguardStateController
+    private val context: Context
 ) {
     private var charging: Boolean? = null
     private val rippleEnabled: Boolean = featureFlags.isChargingRippleEnabled
+    private val windowLayoutParams = WindowManager.LayoutParams().apply {
+        width = WindowManager.LayoutParams.MATCH_PARENT
+        height = WindowManager.LayoutParams.MATCH_PARENT
+        layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+        format = PixelFormat.TRANSLUCENT
+        type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
+        fitInsetsTypes = 0 // Ignore insets from all system bars
+        title = "Wired Charging Animation"
+        flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
+    }
+
     @VisibleForTesting
     var rippleView: ChargingRippleView = ChargingRippleView(context, attrs = null)
 
@@ -68,8 +77,8 @@
                 val wasCharging = charging
                 charging = nowCharging
                 // Only triggers when the keyguard is active and the device is just plugged in.
-                if (wasCharging == false && nowCharging && keyguardStateController.isShowing) {
-                    rippleView.startRipple()
+                if ((wasCharging == null || !wasCharging) && nowCharging) {
+                    startRipple()
                 }
             }
         }
@@ -85,46 +94,41 @@
             override fun onOverlayChanged() {
                 updateRippleColor()
             }
-            override fun onConfigChanged(newConfig: Configuration?) {
-                layoutRippleView()
-            }
         }
         configurationController.addCallback(configurationChangedListener)
 
         commandRegistry.registerCommand("charging-ripple") { ChargingRippleCommand() }
-    }
-
-    fun setViewHost(viewHost: View) {
-        // Add the ripple view as an overlay of the root view so that it always
-        // shows on top.
-        viewHost.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
-            override fun onViewDetachedFromWindow(view: View?) {}
-
-            override fun onViewAttachedToWindow(view: View?) {
-                (viewHost.viewRootImpl.view.overlay as ViewGroupOverlay).add(rippleView)
-                layoutRippleView()
-                viewHost.removeOnAttachStateChangeListener(this)
-            }
-        })
-
         updateRippleColor()
     }
 
-    private fun layoutRippleView() {
-        // Overlays are not auto measured and laid out so we do it manually here.
+    fun startRipple() {
+        if (rippleView.rippleInProgress) {
+            return
+        }
+        val mWM = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+        windowLayoutParams.packageName = context.opPackageName
+        rippleView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
+            override fun onViewDetachedFromWindow(view: View?) {}
+
+            override fun onViewAttachedToWindow(view: View?) {
+                layoutRipple()
+                rippleView.startRipple(Runnable {
+                    mWM.removeView(rippleView)
+                })
+                rippleView.removeOnAttachStateChangeListener(this)
+            }
+        })
+        mWM.addView(rippleView, windowLayoutParams)
+    }
+
+    private fun layoutRipple() {
+        // TODO(shanh): Set origin base on phone orientation.
         val displayMetrics = DisplayMetrics()
         context.display.getRealMetrics(displayMetrics)
         val width = displayMetrics.widthPixels
         val height = displayMetrics.heightPixels
-        if (width != rippleView.width || height != rippleView.height) {
-            rippleView.apply {
-                measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
-                        View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))
-                layout(0, 0, width, height)
-                origin = PointF(width / 2f, height.toFloat())
-                radius = max(width, height).toFloat()
-            }
-        }
+        rippleView.origin = PointF(width / 2f, height.toFloat())
+        rippleView.radius = Integer.max(width, height).toFloat()
     }
 
     private fun updateRippleColor() {
@@ -134,7 +138,7 @@
 
     inner class ChargingRippleCommand : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
-            rippleView.startRipple()
+            startRipple()
         }
 
         override fun help(pw: PrintWriter) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
deleted file mode 100644
index 23d13d3..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ /dev/null
@@ -1,490 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.app.ActivityManager;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.util.MathUtils;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
-import android.view.SyncRtSurfaceTransactionApplier;
-import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.systemui.Interpolators;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
-
-import java.util.concurrent.Executor;
-
-/**
- * A class that allows activities to be launched in a seamless way where the notification
- * transforms nicely into the starting window.
- */
-public class ActivityLaunchAnimator {
-
-    private static final int ANIMATION_DURATION = 400;
-    public static final long ANIMATION_DURATION_FADE_CONTENT = 67;
-    public static final long ANIMATION_DURATION_FADE_APP = 200;
-    public static final long ANIMATION_DELAY_ICON_FADE_IN = ANIMATION_DURATION -
-            CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY
-            - 16;
-    private static final int ANIMATION_DURATION_NAV_FADE_IN = 266;
-    private static final int ANIMATION_DURATION_NAV_FADE_OUT = 133;
-    private static final long ANIMATION_DELAY_NAV_FADE_IN =
-            ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN;
-    private static final Interpolator NAV_FADE_IN_INTERPOLATOR =
-            new PathInterpolator(0f, 0f, 0f, 1f);
-    private static final Interpolator NAV_FADE_OUT_INTERPOLATOR =
-            new PathInterpolator(0.2f, 0f, 1f, 1f);
-    private static final long LAUNCH_TIMEOUT = 500;
-    private final NotificationPanelViewController mNotificationPanel;
-    private final NotificationListContainer mNotificationContainer;
-    private final float mWindowCornerRadius;
-    private final NotificationShadeWindowViewController mNotificationShadeWindowViewController;
-    private final NotificationShadeDepthController mDepthController;
-    private final Executor mMainExecutor;
-    private Callback mCallback;
-    private final Runnable mTimeoutRunnable = () -> {
-        setAnimationPending(false);
-        mCallback.onExpandAnimationTimedOut();
-    };
-    private boolean mAnimationPending;
-    private boolean mAnimationRunning;
-    private boolean mIsLaunchForActivity;
-
-    public ActivityLaunchAnimator(
-            NotificationShadeWindowViewController notificationShadeWindowViewController,
-            Callback callback,
-            NotificationPanelViewController notificationPanel,
-            NotificationShadeDepthController depthController,
-            NotificationListContainer container,
-            Executor mainExecutor) {
-        mNotificationPanel = notificationPanel;
-        mNotificationContainer = container;
-        mDepthController = depthController;
-        mNotificationShadeWindowViewController = notificationShadeWindowViewController;
-        mCallback = callback;
-        mMainExecutor = mainExecutor;
-        mWindowCornerRadius = ScreenDecorationsUtils
-                .getWindowCornerRadius(mNotificationShadeWindowViewController.getView()
-                        .getResources());
-    }
-
-    public RemoteAnimationAdapter getLaunchAnimation(
-            View sourceView, boolean occluded) {
-        if (!(sourceView instanceof ExpandableNotificationRow)
-                || !mCallback.areLaunchAnimationsEnabled() || occluded) {
-            return null;
-        }
-        AnimationRunner animationRunner = new AnimationRunner(
-                (ExpandableNotificationRow) sourceView);
-        return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION,
-                ANIMATION_DURATION - 150 /* statusBarTransitionDelay */);
-    }
-
-    public boolean isAnimationPending() {
-        return mAnimationPending;
-    }
-
-    /**
-     * Set the launch result the intent requested
-     *
-     * @param launchResult the launch result
-     * @param wasIntentActivity was this launch for an activity
-     */
-    public void setLaunchResult(int launchResult, boolean wasIntentActivity) {
-        mIsLaunchForActivity = wasIntentActivity;
-        setAnimationPending((launchResult == ActivityManager.START_TASK_TO_FRONT
-                || launchResult == ActivityManager.START_SUCCESS)
-                        && mCallback.areLaunchAnimationsEnabled());
-    }
-
-    public boolean isLaunchForActivity() {
-        return mIsLaunchForActivity;
-    }
-
-    private void setAnimationPending(boolean pending) {
-        mAnimationPending = pending;
-        mNotificationShadeWindowViewController.setExpandAnimationPending(pending);
-        if (pending) {
-            mNotificationShadeWindowViewController.getView().postDelayed(mTimeoutRunnable,
-                    LAUNCH_TIMEOUT);
-        } else {
-            mNotificationShadeWindowViewController.getView().removeCallbacks(mTimeoutRunnable);
-        }
-    }
-
-    public boolean isAnimationRunning() {
-        return mAnimationRunning;
-    }
-
-    class AnimationRunner extends IRemoteAnimationRunner.Stub {
-
-        private final ExpandableNotificationRow mSourceNotification;
-        private final ExpandAnimationParameters mParams;
-        private final Rect mWindowCrop = new Rect();
-        private boolean mIsFullScreenLaunch = true;
-        private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
-
-        private final float mNotificationStartTopCornerRadius;
-        private final float mNotificationStartBottomCornerRadius;
-
-        AnimationRunner(ExpandableNotificationRow sourceNotification) {
-            mSourceNotification = sourceNotification;
-            mParams = new ExpandAnimationParameters();
-            mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);
-            mNotificationStartTopCornerRadius = mSourceNotification.getCurrentBackgroundRadiusTop();
-            mNotificationStartBottomCornerRadius =
-                    mSourceNotification.getCurrentBackgroundRadiusBottom();
-        }
-
-        @Override
-        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
-                RemoteAnimationTarget[] remoteAnimationTargets,
-                RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
-                RemoteAnimationTarget[] remoteAnimationNonAppTargets,
-                IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
-                    throws RemoteException {
-            mMainExecutor.execute(() -> {
-                RemoteAnimationTarget primary = getPrimaryRemoteAnimationTarget(
-                        remoteAnimationTargets);
-                if (primary == null) {
-                    setAnimationPending(false);
-                    invokeCallback(iRemoteAnimationFinishedCallback);
-                    mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
-                    return;
-                }
-
-                setExpandAnimationRunning(true);
-                mIsFullScreenLaunch = primary.position.y == 0
-                        && primary.sourceContainerBounds.height()
-                                >= mNotificationPanel.getHeight();
-                if (!mIsFullScreenLaunch) {
-                    mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);
-                }
-                ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
-                mParams.startPosition = mSourceNotification.getLocationOnScreen();
-                mParams.startTranslationZ = mSourceNotification.getTranslationZ();
-                mParams.startClipTopAmount = mSourceNotification.getClipTopAmount();
-                if (mSourceNotification.isChildInGroup()) {
-                    int parentClip = mSourceNotification
-                            .getNotificationParent().getClipTopAmount();
-                    mParams.parentStartClipTopAmount = parentClip;
-                    // We need to calculate how much the child is clipped by the parent
-                    // because children always have 0 clipTopAmount
-                    if (parentClip != 0) {
-                        float childClip = parentClip
-                                - mSourceNotification.getTranslationY();
-                        if (childClip > 0.0f) {
-                            mParams.startClipTopAmount = (int) Math.ceil(childClip);
-                        }
-                    }
-                }
-                int targetWidth = primary.sourceContainerBounds.width();
-                // If the notification panel is collapsed, the clip may be larger than the height.
-                int notificationHeight = Math.max(mSourceNotification.getActualHeight()
-                        - mSourceNotification.getClipBottomAmount(), 0);
-                int notificationWidth = mSourceNotification.getWidth();
-                final RemoteAnimationTarget navigationBarTarget =
-                        getNavBarRemoteAnimationTarget(remoteAnimationNonAppTargets);
-                anim.setDuration(ANIMATION_DURATION);
-                anim.setInterpolator(Interpolators.LINEAR);
-                anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        mParams.linearProgress = animation.getAnimatedFraction();
-                        float progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
-                                        mParams.linearProgress);
-                        int newWidth = (int) MathUtils.lerp(notificationWidth,
-                                targetWidth, progress);
-                        mParams.left = (int) ((targetWidth - newWidth) / 2.0f);
-                        mParams.right = mParams.left + newWidth;
-                        mParams.top = (int) MathUtils.lerp(mParams.startPosition[1],
-                                primary.position.y, progress);
-                        mParams.bottom = (int) MathUtils.lerp(mParams.startPosition[1]
-                                        + notificationHeight,
-                                primary.position.y + primary.sourceContainerBounds.bottom,
-                                progress);
-                        mParams.topCornerRadius = MathUtils.lerp(mNotificationStartTopCornerRadius,
-                                mWindowCornerRadius, progress);
-                        mParams.bottomCornerRadius = MathUtils.lerp(
-                                mNotificationStartBottomCornerRadius,
-                                mWindowCornerRadius, progress);
-                        applyParamsToWindow(primary);
-                        applyParamsToNotification(mParams);
-                        applyParamsToNotificationShade(mParams);
-                        applyNavigationBarParamsToWindow(navigationBarTarget);
-                    }
-                });
-                anim.addListener(new AnimatorListenerAdapter() {
-                    private boolean mWasCancelled;
-
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        InteractionJankMonitor.getInstance().begin(mSourceNotification,
-                                CUJ_NOTIFICATION_APP_START);
-                    }
-
-                    @Override
-                    public void onAnimationCancel(Animator animation) {
-                        mWasCancelled = true;
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        setExpandAnimationRunning(false);
-                        invokeCallback(iRemoteAnimationFinishedCallback);
-                        if (!mWasCancelled) {
-                            InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_APP_START);
-                        } else {
-                            InteractionJankMonitor.getInstance().cancel(CUJ_NOTIFICATION_APP_START);
-                        }
-                    }
-                });
-                anim.start();
-                setAnimationPending(false);
-            });
-        }
-
-        private void invokeCallback(IRemoteAnimationFinishedCallback callback) {
-            try {
-                callback.onAnimationFinished();
-            } catch (RemoteException e) {
-                e.printStackTrace();
-            }
-        }
-
-        private RemoteAnimationTarget getPrimaryRemoteAnimationTarget(
-                RemoteAnimationTarget[] remoteAnimationTargets) {
-            RemoteAnimationTarget primary = null;
-            for (RemoteAnimationTarget app : remoteAnimationTargets) {
-                if (app.mode == RemoteAnimationTarget.MODE_OPENING) {
-                    primary = app;
-                    break;
-                }
-            }
-            return primary;
-        }
-
-        private RemoteAnimationTarget getNavBarRemoteAnimationTarget(
-                RemoteAnimationTarget[] remoteAnimationTargets) {
-            RemoteAnimationTarget navBar = null;
-            for (RemoteAnimationTarget target : remoteAnimationTargets) {
-                if (target.windowType == TYPE_NAVIGATION_BAR) {
-                    navBar = target;
-                    break;
-                }
-            }
-            return navBar;
-        }
-
-        private void setExpandAnimationRunning(boolean running) {
-            mNotificationPanel.setLaunchingNotification(running);
-            mSourceNotification.setExpandAnimationRunning(running);
-            mNotificationShadeWindowViewController.setExpandAnimationRunning(running);
-            mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null);
-            mAnimationRunning = running;
-            if (!running) {
-                mCallback.onExpandAnimationFinished(mIsFullScreenLaunch);
-                applyParamsToNotification(null);
-                applyParamsToNotificationShade(null);
-            }
-
-        }
-
-        private void applyParamsToNotificationShade(ExpandAnimationParameters params) {
-            mNotificationContainer.applyExpandAnimationParams(params);
-            mNotificationPanel.applyExpandAnimationParams(params);
-            mDepthController.setNotificationLaunchAnimationParams(params);
-        }
-
-        private void applyParamsToNotification(ExpandAnimationParameters params) {
-            mSourceNotification.applyExpandAnimationParams(params);
-        }
-
-        private void applyParamsToWindow(RemoteAnimationTarget app) {
-            Matrix m = new Matrix();
-            m.postTranslate(0, (float) (mParams.top - app.position.y));
-            mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
-            float cornerRadius = Math.min(mParams.topCornerRadius, mParams.bottomCornerRadius);
-            SurfaceParams params = new SurfaceParams.Builder(app.leash)
-                    .withAlpha(1f)
-                    .withMatrix(m)
-                    .withWindowCrop(mWindowCrop)
-                    .withLayer(app.prefixOrderIndex)
-                    .withCornerRadius(cornerRadius)
-                    .withVisibility(true)
-                    .build();
-            mSyncRtTransactionApplier.scheduleApply(params);
-        }
-
-        private void applyNavigationBarParamsToWindow(RemoteAnimationTarget navBarTarget) {
-            if (navBarTarget == null) {
-                return;
-            }
-
-            // calculate navigation bar fade-out progress
-            final float fadeOutProgress = mParams.getProgress(0,
-                    ANIMATION_DURATION_NAV_FADE_OUT);
-
-            // calculate navigation bar fade-in progress
-            final float fadeInProgress = mParams.getProgress(ANIMATION_DELAY_NAV_FADE_IN,
-                    ANIMATION_DURATION_NAV_FADE_OUT);
-
-            final SurfaceParams.Builder builder = new SurfaceParams.Builder(navBarTarget.leash);
-            if (fadeInProgress > 0) {
-                Matrix m = new Matrix();
-                m.postTranslate(0, (float) (mParams.top - navBarTarget.position.y));
-                mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
-                builder.withMatrix(m)
-                        .withWindowCrop(mWindowCrop)
-                        .withVisibility(true);
-                builder.withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress));
-            } else {
-                builder.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress));
-            }
-            mSyncRtTransactionApplier.scheduleApply(builder.build());
-        }
-
-        @Override
-        public void onAnimationCancelled() throws RemoteException {
-            mMainExecutor.execute(() -> {
-                setAnimationPending(false);
-                mCallback.onLaunchAnimationCancelled();
-            });
-        }
-    };
-
-    public static class ExpandAnimationParameters {
-        public float linearProgress;
-        int[] startPosition;
-        float startTranslationZ;
-        int left;
-        int top;
-        int right;
-        int bottom;
-        int startClipTopAmount;
-        int parentStartClipTopAmount;
-        float topCornerRadius;
-        float bottomCornerRadius;
-
-        public ExpandAnimationParameters() {
-        }
-
-        public int getTop() {
-            return top;
-        }
-
-        public int getBottom() {
-            return bottom;
-        }
-
-        public int getWidth() {
-            return right - left;
-        }
-
-        public int getHeight() {
-            return bottom - top;
-        }
-
-        public int getTopChange() {
-            // We need this compensation to ensure that the QS moves in sync.
-            int clipTopAmountCompensation = 0;
-            if (startClipTopAmount != 0.0f) {
-                clipTopAmountCompensation = (int) MathUtils.lerp(0, startClipTopAmount,
-                        Interpolators.FAST_OUT_SLOW_IN.getInterpolation(linearProgress));
-            }
-            return Math.min(top - startPosition[1] - clipTopAmountCompensation, 0);
-        }
-
-        public float getProgress() {
-            return linearProgress;
-        }
-
-        public float getProgress(long delay, long duration) {
-            return MathUtils.constrain((linearProgress * ANIMATION_DURATION - delay)
-                    / duration, 0.0f, 1.0f);
-        }
-
-        public int getStartClipTopAmount() {
-            return startClipTopAmount;
-        }
-
-        public int getParentStartClipTopAmount() {
-            return parentStartClipTopAmount;
-        }
-
-        public float getStartTranslationZ() {
-            return startTranslationZ;
-        }
-
-        public float getTopCornerRadius() {
-            return topCornerRadius;
-        }
-
-        public float getBottomCornerRadius() {
-            return bottomCornerRadius;
-        }
-    }
-
-    public interface Callback {
-
-        /**
-         * Called when the launch animation was cancelled.
-         */
-        void onLaunchAnimationCancelled();
-
-        /**
-         * Called when the launch animation has timed out without starting an actual animation.
-         */
-        void onExpandAnimationTimedOut();
-
-        /**
-         * Called when the expand animation has finished.
-         *
-         * @param launchIsFullScreen True if this launch was fullscreen, such that now the window
-         *                           fills the whole screen
-         */
-        void onExpandAnimationFinished(boolean launchIsFullScreen);
-
-        /**
-         * Are animations currently enabled.
-         */
-        boolean areLaunchAnimationsEnabled();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
new file mode 100644
index 0000000..d5835fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
@@ -0,0 +1,44 @@
+package com.android.systemui.statusbar.notification
+
+import android.util.MathUtils
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.Interpolators
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator
+import kotlin.math.min
+
+/** Parameters for the notifications expand animations. */
+class ExpandAnimationParameters(
+    top: Int,
+    bottom: Int,
+    left: Int,
+    right: Int,
+
+    topCornerRadius: Float = 0f,
+    bottomCornerRadius: Float = 0f
+) : ActivityLaunchAnimator.State(top, bottom, left, right, topCornerRadius, bottomCornerRadius) {
+    @VisibleForTesting
+    constructor() : this(
+        top = 0, bottom = 0, left = 0, right = 0, topCornerRadius = 0f, bottomCornerRadius = 0f
+    )
+
+    var startTranslationZ = 0f
+    var startClipTopAmount = 0
+    var parentStartClipTopAmount = 0
+    var progress = 0f
+    var linearProgress = 0f
+
+    override val topChange: Int
+        get() {
+            // We need this compensation to ensure that the QS moves in sync.
+            var clipTopAmountCompensation = 0
+            if (startClipTopAmount.toFloat() != 0.0f) {
+                clipTopAmountCompensation = MathUtils.lerp(0f, startClipTopAmount.toFloat(),
+                        Interpolators.FAST_OUT_SLOW_IN.getInterpolation(linearProgress)).toInt()
+            }
+            return min(super.topChange - clipTopAmountCompensation, 0)
+        }
+
+    fun getProgress(delay: Long, duration: Long): Float {
+        return ActivityLaunchAnimator.getProgress(linearProgress, delay, duration)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
new file mode 100644
index 0000000..2f966b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -0,0 +1,136 @@
+package com.android.systemui.statusbar.notification
+
+import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.statusbar.NotificationShadeDepthController
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator
+import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController
+import kotlin.math.ceil
+import kotlin.math.max
+
+/** A provider of [NotificationLaunchAnimatorController]. */
+class NotificationLaunchAnimatorControllerProvider(
+    private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
+    private val notificationPanelViewController: NotificationPanelViewController,
+    private val notificationListContainer: NotificationListContainer,
+    private val depthController: NotificationShadeDepthController
+) {
+    fun getAnimatorController(
+        notification: ExpandableNotificationRow
+    ): NotificationLaunchAnimatorController {
+        return NotificationLaunchAnimatorController(
+            notificationShadeWindowViewController,
+            notificationPanelViewController,
+            notificationListContainer,
+            depthController,
+            notification
+        )
+    }
+}
+
+/**
+ * An [ActivityLaunchAnimator.Controller] that animates an [ExpandableNotificationRow]. An instance
+ * of this class can be passed to [ActivityLaunchAnimator.startIntentWithAnimation] to animate a
+ * notification expanding into an opening window.
+ */
+class NotificationLaunchAnimatorController(
+    private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
+    private val notificationPanelViewController: NotificationPanelViewController,
+    private val notificationListContainer: NotificationListContainer,
+    private val depthController: NotificationShadeDepthController,
+    private val notification: ExpandableNotificationRow
+) : ActivityLaunchAnimator.Controller {
+    override fun getRootView(): View = notification.rootView
+
+    override fun createAnimatorState(): ActivityLaunchAnimator.State {
+        // If the notification panel is collapsed, the clip may be larger than the height.
+        val height = max(0, notification.actualHeight - notification.clipBottomAmount)
+        val location = notification.locationOnScreen
+
+        val params = ExpandAnimationParameters(
+                top = location[1],
+                bottom = location[1] + height,
+                left = location[0],
+                right = location[0] + notification.width,
+                topCornerRadius = notification.currentBackgroundRadiusTop,
+                bottomCornerRadius = notification.currentBackgroundRadiusBottom
+        )
+
+        params.startTranslationZ = notification.translationZ
+        params.startClipTopAmount = notification.clipTopAmount
+        if (notification.isChildInGroup) {
+            val parentClip = notification.notificationParent.clipTopAmount
+            params.parentStartClipTopAmount = parentClip
+
+            // We need to calculate how much the child is clipped by the parent because children
+            // always have 0 clipTopAmount
+            if (parentClip != 0) {
+                val childClip = parentClip - notification.translationY
+                if (childClip > 0) {
+                    params.startClipTopAmount = ceil(childClip.toDouble()).toInt()
+                }
+            }
+        }
+
+        return params
+    }
+
+    override fun onIntentStarted(willAnimate: Boolean) {
+        notificationShadeWindowViewController.setExpandAnimationRunning(willAnimate)
+    }
+
+    override fun onLaunchAnimationCancelled() {
+        // TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started
+        // here?
+        notificationShadeWindowViewController.setExpandAnimationRunning(false)
+    }
+
+    override fun onLaunchAnimationTimedOut() {
+        notificationShadeWindowViewController.setExpandAnimationRunning(false)
+    }
+
+    override fun onLaunchAnimationAborted() {
+        notificationShadeWindowViewController.setExpandAnimationRunning(false)
+    }
+
+    override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+        notificationPanelViewController.setLaunchingNotification(true)
+        notification.isExpandAnimationRunning = true
+        notificationListContainer.setExpandingNotification(notification)
+
+        InteractionJankMonitor.getInstance().begin(notification,
+            InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
+    }
+
+    override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+        InteractionJankMonitor.getInstance().end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
+
+        notificationPanelViewController.setLaunchingNotification(false)
+        notification.isExpandAnimationRunning = false
+        notificationShadeWindowViewController.setExpandAnimationRunning(false)
+        notificationListContainer.setExpandingNotification(null)
+        applyParams(null)
+    }
+
+    private fun applyParams(params: ExpandAnimationParameters?) {
+        notification.applyExpandAnimationParams(params)
+        notificationListContainer.applyExpandAnimationParams(params)
+        notificationPanelViewController.applyExpandAnimationParams(params)
+        depthController.notificationLaunchAnimationParams = params
+    }
+
+    override fun onLaunchAnimationProgress(
+        state: ActivityLaunchAnimator.State,
+        progress: Float,
+        linearProgress: Float
+    ) {
+        val params = state as ExpandAnimationParameters
+        params.progress = progress
+        params.linearProgress = linearProgress
+
+        applyParams(params)
+    }
+}
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 18e5ead..17f70cb 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
@@ -720,6 +720,10 @@
             mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
             mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
             targetValue = 1.0f;
+            if (!mIsHeadsUpAnimation && isChildInGroup()) {
+                // slower fade in of children to avoid visibly overlapping with other children
+                mCurrentAlphaInterpolator = Interpolators.SLOW_OUT_LINEAR_IN;
+            }
         } else {
             mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
             mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
@@ -819,6 +823,10 @@
         if (mIsHeadsUpAnimation && !mIsAppearing) {
             startWidthFraction = 0;
         }
+        if (mIsAppearing && !mIsHeadsUpAnimation && isChildInGroup()) {
+            // Children in a group (when not heads up) should simply fade in.
+            startWidthFraction = 1;
+        }
         float width = MathUtils.lerp(startWidthFraction, 1.0f, 1.0f - widthFraction)
                         * getWidth();
         float left;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 3f7b8af..edd97af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -125,7 +125,7 @@
                 result = mNotificationTapHelper.onTouchEvent(ev, mView.getActualHeight());
             } else if (ev.getAction() == MotionEvent.ACTION_UP) {
                 // If this is a false tap, capture the even so it doesn't result in a click.
-                return mFalsingManager.isFalseTap(true, 0.1);
+                return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
             }
             return result;
         }
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 a3a4014..2ada281 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
@@ -19,7 +19,6 @@
 import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
 
@@ -79,6 +78,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -86,7 +86,7 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -881,7 +881,7 @@
         // Other parts of the system may intercept and handle all the falsing.
         // Otherwise, if we see motion and follow-on events, try to classify them as a tap.
         if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
-            mFalsingManager.isFalseTap(true, 0.3);
+            mFalsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY);
         }
         return super.onInterceptTouchEvent(ev);
     }
@@ -2045,9 +2045,8 @@
         float extraWidthForClipping = params.getWidth() - getWidth();
         setExtraWidthForClipping(extraWidthForClipping);
         int top = params.getTop();
-        float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
         int startClipTopAmount = params.getStartClipTopAmount();
-        int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
+        int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, params.getProgress());
         if (mNotificationParent != null) {
             float parentY = mNotificationParent.getTranslationY();
             top -= parentY;
@@ -2096,7 +2095,7 @@
         if (expandAnimationRunning) {
             contentView.animate()
                     .alpha(0f)
-                    .setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT)
+                    .setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_OUT_CONTENT)
                     .setInterpolator(Interpolators.ALPHA_OUT);
             setAboveShelf(true);
             mExpandAnimationRunning = true;
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 9588563..07d1e68 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
@@ -31,7 +31,8 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 
 /**
  * A view that can be used for both the dimmed and normal background of an notification.
@@ -277,13 +278,14 @@
         invalidate();
     }
 
-    public void setExpandAnimationParams(ActivityLaunchAnimator.ExpandAnimationParameters params) {
+    /** Set the current expand animation parameters. */
+    public void setExpandAnimationParams(ExpandAnimationParameters params) {
         mActualHeight = params.getHeight();
         mActualWidth = params.getWidth();
         float alphaProgress = Interpolators.ALPHA_IN.getInterpolation(
                 params.getProgress(
-                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT /* delay */,
-                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_APP /* duration */));
+                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_OUT_CONTENT /* delay */,
+                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_IN_WINDOW /* duration */));
         mBackground.setAlpha((int) (mDrawableAlpha * (1.0f - alphaProgress)));
         invalidate();
     }
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 72f3216..2a2e733f7 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
@@ -16,14 +16,13 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
-
 import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.annotation.Nullable;
 
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
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 40c0b89..ad06e7d 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
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.notification.stack;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
@@ -91,6 +90,7 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+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.NotificationUtils;
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 7baad1c..ce7b397 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
@@ -75,8 +75,8 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -1497,8 +1497,7 @@
         }
 
         @Override
-        public void applyExpandAnimationParams(
-                ActivityLaunchAnimator.ExpandAnimationParameters params) {
+        public void applyExpandAnimationParams(ExpandAnimationParameters params) {
             mView.applyExpandAnimationParams(params);
         }
 
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 555df5c..364b532 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -26,7 +26,6 @@
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 
 import static java.lang.Float.isNaN;
@@ -100,6 +99,7 @@
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -111,16 +111,17 @@
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -178,6 +179,10 @@
      * Fling until QS is completely hidden.
      */
     private static final int FLING_HIDE = 2;
+    private static final long ANIMATION_DELAY_ICON_FADE_IN =
+            ActivityLaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION
+                    - CollapsedStatusBarFragment.FADE_IN_DELAY - 16;
+
     private final DozeParameters mDozeParameters;
     private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener();
     private final OnClickListener mOnClickListener = new OnClickListener();
@@ -477,6 +482,7 @@
     private final UserManager mUserManager;
     private final ShadeController mShadeController;
     private final MediaDataManager mMediaDataManager;
+    private NotificationShadeDepthController mDepthController;
     private int mDisplayId;
 
     /**
@@ -576,6 +582,7 @@
             ScrimController scrimController,
             UserManager userManager,
             MediaDataManager mediaDataManager,
+            NotificationShadeDepthController notificationShadeDepthController,
             AmbientState ambientState,
             FeatureFlags featureFlags) {
         super(view, falsingManager, dozeLog, keyguardStateController,
@@ -594,6 +601,7 @@
         mNotificationIconAreaController = notificationIconAreaController;
         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
         mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
+        mDepthController = notificationShadeDepthController;
         mFeatureFlags = featureFlags;
         mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
         mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
@@ -603,7 +611,6 @@
         mKeyguardQsUserSwitchEnabled =
                 mKeyguardUserSwitcherEnabled && mResources.getBoolean(
                         R.bool.config_keyguard_user_switch_opens_qs_details);
-        keyguardUpdateMonitor.setKeyguardQsUserSwitchEnabled(mKeyguardQsUserSwitchEnabled);
         mShouldUseSplitNotificationShade =
                 Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
         mView.setWillNotDraw(!DEBUG);
@@ -1844,6 +1851,10 @@
             mPulseExpansionHandler.setQsExpanded(expanded);
             mKeyguardBypassController.setQSExpanded(expanded);
             mStatusBarKeyguardViewManager.setQsExpanded(expanded);
+
+            if (mDisabledUdfpsController != null) {
+                mDisabledUdfpsController.setQsExpanded(expanded);
+            }
         }
     }
 
@@ -1991,8 +2002,21 @@
         float qsExpansionFraction = getQsExpansionFraction();
         mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
         mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
-        mScrimController.setQsExpansion(qsExpansionFraction);
+        mScrimController.setQsPosition(qsExpansionFraction,
+                calculateQsBottomPosition(qsExpansionFraction));
         mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+        mDepthController.setQsPanelExpansion(qsExpansionFraction);
+    }
+
+    private int calculateQsBottomPosition(float qsExpansionFraction) {
+        int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
+        if (qsExpansionFraction != 0.0) {
+            qsBottomY = (int) MathUtils.lerp(
+                    qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction);
+        }
+        // to account for shade overshooting animation, see setSectionPadding method
+        if (mSectionPadding > 0) qsBottomY += mSectionPadding;
+        return qsBottomY;
     }
 
     private String determineAccessibilityPaneTitle() {
@@ -3208,8 +3232,7 @@
             return;
         }
 
-        boolean hideIcons = params.getProgress(
-                ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
+        boolean hideIcons = params.getProgress(ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
         if (hideIcons != mHideIconsDuringNotificationLaunch) {
             mHideIconsDuringNotificationLaunch = hideIcons;
             if (!hideIcons) {
@@ -3544,7 +3567,8 @@
                     mUpdateMonitor,
                     mAuthController,
                     mStatusBarKeyguardViewManager,
-                    mKeyguardStateController);
+                    mKeyguardStateController,
+                    mFalsingManager);
             mDisabledUdfpsController.init();
         } else if (mDisabledUdfpsController != null && !udfpsEnrolled) {
             mDisabledUdfpsController.destroy();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index d074e64..5aecb72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -527,6 +527,11 @@
     }
 
     @Override
+    public boolean isLaunchingActivity() {
+        return mCurrentState.mLaunchingActivity;
+    }
+
+    @Override
     public void setScrimsVisibility(int scrimsVisibility) {
         mCurrentState.mScrimsVisibility = scrimsVisibility;
         apply(mCurrentState);
@@ -606,12 +611,21 @@
         apply(mCurrentState);
     }
 
+    private final Set<Object> mForceOpenTokens = new HashSet<>();
     @Override
-    public void setForcePluginOpen(boolean forcePluginOpen) {
-        mCurrentState.mForcePluginOpen = forcePluginOpen;
-        apply(mCurrentState);
-        if (mForcePluginOpenListener != null) {
-            mForcePluginOpenListener.onChange(forcePluginOpen);
+    public void setForcePluginOpen(boolean forceOpen, Object token) {
+        if (forceOpen) {
+            mForceOpenTokens.add(token);
+        } else {
+            mForceOpenTokens.remove(token);
+        }
+        final boolean previousForceOpenState = mCurrentState.mForcePluginOpen;
+        mCurrentState.mForcePluginOpen = !mForceOpenTokens.isEmpty();
+        if (previousForceOpenState != mCurrentState.mForcePluginOpen) {
+            apply(mCurrentState);
+            if (mForcePluginOpenListener != null) {
+                mForcePluginOpenListener.onChange(mCurrentState.mForcePluginOpen);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 2ff7c99..72f7ff8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -92,7 +92,6 @@
     private View mBrightnessMirror;
     private boolean mTouchActive;
     private boolean mTouchCancelled;
-    private boolean mExpandAnimationPending;
     private boolean mExpandAnimationRunning;
     private NotificationStackScrollLayout mStackScrollLayout;
     private PhoneStatusBarView mStatusBarView;
@@ -235,7 +234,7 @@
                         || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
                     setTouchActive(false);
                 }
-                if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) {
+                if (mTouchCancelled || mExpandAnimationRunning) {
                     return false;
                 }
                 mFalsingCollector.onTouchEvent(ev);
@@ -435,8 +434,6 @@
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.print("  mExpandAnimationPending=");
-        pw.println(mExpandAnimationPending);
         pw.print("  mExpandAnimationRunning=");
         pw.println(mExpandAnimationRunning);
         pw.print("  mTouchCancelled=");
@@ -445,19 +442,10 @@
         pw.println(mTouchActive);
     }
 
-    public void setExpandAnimationPending(boolean pending) {
-        if (mExpandAnimationPending != pending) {
-            mExpandAnimationPending = pending;
-            mNotificationShadeWindowController
-                    .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
-        }
-    }
-
     public void setExpandAnimationRunning(boolean running) {
         if (mExpandAnimationRunning != running) {
             mExpandAnimationRunning = running;
-            mNotificationShadeWindowController
-                    .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
+            mNotificationShadeWindowController.setLaunchingActivity(mExpandAnimationRunning);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
index 66df936..95ecd3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
@@ -64,7 +64,7 @@
                 mTrackTouch = event.getY() <= maxTouchableHeight;
                 break;
             case MotionEvent.ACTION_MOVE:
-                if (mTrackTouch && mFalsingManager.isFalseTap(false, 0)) {
+                if (mTrackTouch && !mFalsingManager.isSimpleTap()) {
                     makeInactive();
                     mTrackTouch = false;
                 }
@@ -78,10 +78,10 @@
 
                 // 1) See if we have confidence that we can activate after a single tap.
                 // 2) Else, see if it looks like a tap at all and check for a double-tap.
-                if (!mFalsingManager.isFalseTap(true, 0)) {
+                if (!mFalsingManager.isFalseTap(FalsingManager.NO_PENALTY)) {
                     makeInactive();
                     return mDoubleTapListener.onDoubleTap();
-                } else if (!mFalsingManager.isFalseTap(false, 0)) {
+                } else if (mFalsingManager.isSimpleTap()) {
                     if (mSlideBackListener != null && mSlideBackListener.onSlideBack()) {
                         return true;
                     }
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 270a0f8..681f450 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -24,7 +24,6 @@
 import android.annotation.IntDef;
 import android.app.AlarmManager;
 import android.graphics.Color;
-import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Trace;
 import android.util.Log;
@@ -49,7 +48,6 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
-import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -142,6 +140,7 @@
     private ScrimState mState = ScrimState.UNINITIALIZED;
 
     private ScrimView mScrimInFront;
+    private ScrimView mNotificationsScrim;
     private ScrimView mScrimBehind;
     @Nullable
     private ScrimView mScrimForBubble;
@@ -156,7 +155,6 @@
     private final KeyguardVisibilityCallback mKeyguardVisibilityCallback;
     private final Handler mHandler;
     private final Executor mMainExecutor;
-    private final BlurUtils mBlurUtils;
 
     private GradientColors mColors;
     private boolean mNeedsDrawableColorUpdate;
@@ -180,6 +178,7 @@
 
     private float mInFrontAlpha = NOT_INITIALIZED;
     private float mBehindAlpha = NOT_INITIALIZED;
+    private float mNotificationsAlpha = NOT_INITIALIZED;
     private float mBubbleAlpha = NOT_INITIALIZED;
 
     private int mInFrontTint;
@@ -209,12 +208,11 @@
             AlarmManager alarmManager, KeyguardStateController keyguardStateController,
             DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
             KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager,
-            BlurUtils blurUtils, ConfigurationController configurationController,
+            ConfigurationController configurationController,
             FeatureFlags featureFlags, @Main Executor mainExecutor) {
         mScrimStateListener = lightBarController::setScrimState;
         mDefaultScrimAlpha = featureFlags.isShadeOpaque() ? BUSY_SCRIM_ALPHA : GAR_SCRIM_ALPHA;
         ScrimState.BUBBLE_EXPANDED.setBubbleAlpha(BUBBLE_SCRIM_ALPHA);
-        mBlurUtils = blurUtils;
 
         mKeyguardStateController = keyguardStateController;
         mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
@@ -259,13 +257,16 @@
     /**
      * Attach the controller to the supplied views.
      */
-    public void attachViews(
-            ScrimView scrimBehind, ScrimView scrimInFront, @Nullable ScrimView scrimForBubble) {
-        mScrimBehind = scrimBehind;
+    public void attachViews(ScrimView behindScrim, ScrimView notificationsScrim,
+                            ScrimView scrimInFront, @Nullable ScrimView scrimForBubble) {
+        mNotificationsScrim = notificationsScrim;
+        mScrimBehind = behindScrim;
         mScrimInFront = scrimInFront;
         mScrimForBubble = scrimForBubble;
         updateThemeColors();
 
+        mNotificationsScrim.enableRoundedCorners();
+
         if (mScrimBehindChangeRunnable != null) {
             mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable, mMainExecutor);
             mScrimBehindChangeRunnable = null;
@@ -280,6 +281,7 @@
         }
 
         mScrimBehind.setDefaultFocusHighlightEnabled(false);
+        mNotificationsScrim.setDefaultFocusHighlightEnabled(false);
         mScrimInFront.setDefaultFocusHighlightEnabled(false);
         if (mScrimForBubble != null) {
             mScrimForBubble.setDefaultFocusHighlightEnabled(false);
@@ -344,6 +346,7 @@
         // We need to disable focus otherwise AOD would end up with a gray overlay.
         mScrimInFront.setFocusable(!state.isLowPowerState());
         mScrimBehind.setFocusable(!state.isLowPowerState());
+        mNotificationsScrim.setFocusable(!state.isLowPowerState());
 
         // Cancel blanking transitions that were pending before we requested a new state
         if (mPendingFrameCallback != null) {
@@ -484,18 +487,20 @@
     }
 
     /**
-     * Current state of the QuickSettings expansion when pulling it from the top.
+     * Current state of the QuickSettings when pulling it from the top.
      *
-     * @param fraction From 0 to 1 where 0 means collapsed and 1 expanded.
+     * @param expansionFraction From 0 to 1 where 0 means collapsed and 1 expanded.
+     * @param qsPanelBottomY absolute Y position of qs panel bottom
      */
-    public void setQsExpansion(float fraction) {
-        if (isNaN(fraction)) {
+    public void setQsPosition(float expansionFraction, int qsPanelBottomY) {
+        if (isNaN(expansionFraction)) {
             return;
         }
-        if (mQsExpansion != fraction) {
-            mQsExpansion = fraction;
+        shiftNotificationsScrim(qsPanelBottomY);
+        updateNotificationsScrimAlpha(qsPanelBottomY);
+        if (mQsExpansion != expansionFraction) {
+            mQsExpansion = expansionFraction;
             Log.d(TAG, "set qs fraction");
-
             boolean relevantState = (mState == ScrimState.SHADE_LOCKED
                     || mState == ScrimState.KEYGUARD
                     || mState == ScrimState.PULSING
@@ -507,6 +512,32 @@
         }
     }
 
+    private void shiftNotificationsScrim(int qsPanelBottomY) {
+        if (qsPanelBottomY > 0) {
+            mNotificationsScrim.setTranslationY(qsPanelBottomY);
+        } else {
+            mNotificationsScrim.setTranslationY(0);
+        }
+    }
+
+    private void updateNotificationsScrimAlpha(int qsPanelBottomY) {
+        float newAlpha = 0;
+        if (qsPanelBottomY > 0) {
+            float interpolator = 0;
+            if (mState == ScrimState.UNLOCKED || mState == ScrimState.SHADE_LOCKED) {
+                interpolator = getInterpolatedFraction();
+            } else {
+                interpolator = mQsExpansion;
+            }
+            newAlpha = MathUtils.lerp(0, 1, interpolator);
+        }
+        if (newAlpha != mNotificationsAlpha) {
+            mNotificationsAlpha = newAlpha;
+            // update alpha without animating
+            mNotificationsScrim.setViewAlpha(newAlpha);
+        }
+    }
+
     private void setOrAdaptCurrentAnimation(@Nullable View scrim) {
         if (scrim == null) {
             return;
@@ -575,6 +606,7 @@
             return;
         }
         setOrAdaptCurrentAnimation(mScrimBehind);
+        setOrAdaptCurrentAnimation(mNotificationsScrim);
         setOrAdaptCurrentAnimation(mScrimInFront);
         setOrAdaptCurrentAnimation(mScrimForBubble);
         dispatchScrimState(mScrimBehind.getViewAlpha());
@@ -591,14 +623,6 @@
     }
 
     /**
-     * Sets the given drawable as the background of the scrim that shows up behind the
-     * notifications.
-     */
-    public void setScrimBehindDrawable(Drawable drawable) {
-        mScrimBehind.setDrawable(drawable);
-    }
-
-    /**
      * Sets the front scrim opacity in AOD so it's not as bright.
      * <p>
      * Displays usually don't support multiple dimming settings when in low power mode.
@@ -667,10 +691,13 @@
             mNeedsDrawableColorUpdate = false;
             // Only animate scrim color if the scrim view is actually visible
             boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0 && !mBlankScreen;
-            boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen;
+            boolean animateBehindScrim = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen;
+            boolean animateScrimNotifications = mNotificationsScrim.getViewAlpha() != 0
+                    && !mBlankScreen;
 
             mScrimInFront.setColors(mColors, animateScrimInFront);
-            mScrimBehind.setColors(mColors, animateScrimBehind);
+            mScrimBehind.setColors(mColors, animateScrimNotifications);
+            mNotificationsScrim.setColors(mColors, animateScrimNotifications);
 
             dispatchScrimState(mScrimBehind.getViewAlpha());
         }
@@ -687,6 +714,7 @@
         }
         setScrimAlpha(mScrimInFront, mInFrontAlpha);
         setScrimAlpha(mScrimBehind, mBehindAlpha);
+        setScrimAlpha(mNotificationsScrim, mNotificationsAlpha);
 
         if (mScrimForBubble != null) {
             boolean animateScrimForBubble = mScrimForBubble.getViewAlpha() != 0 && !mBlankScreen;
@@ -744,7 +772,9 @@
         if (scrim == mScrimInFront) {
             return "front_scrim";
         } else if (scrim == mScrimBehind) {
-            return "back_scrim";
+            return "behind_scrim";
+        } else if (scrim == mNotificationsScrim) {
+            return "notifications_scrim";
         } else if (scrim == mScrimForBubble) {
             return "bubble_scrim";
         }
@@ -817,6 +847,8 @@
             return mInFrontAlpha;
         } else if (scrim == mScrimBehind) {
             return mBehindAlpha;
+        } else if (scrim == mNotificationsScrim) {
+            return mNotificationsAlpha;
         } else if (scrim == mScrimForBubble) {
             return mBubbleAlpha;
         } else {
@@ -829,6 +861,8 @@
             return mInFrontTint;
         } else if (scrim == mScrimBehind) {
             return mBehindTint;
+        } else if (scrim == mNotificationsScrim) {
+            return Color.TRANSPARENT;
         } else if (scrim == mScrimForBubble) {
             return mBubbleTint;
         } else {
@@ -858,8 +892,9 @@
 
         }
         if (isAnimating(mScrimBehind)
-            || isAnimating(mScrimInFront)
-            || isAnimating(mScrimForBubble)) {
+                || isAnimating(mNotificationsScrim)
+                || isAnimating(mScrimInFront)
+                || isAnimating(mScrimForBubble)) {
             if (callback != null && callback != mCallback) {
                 // Since we only notify the callback that we're finished once everything has
                 // finished, we need to make sure that any changing callbacks are also invoked
@@ -884,7 +919,7 @@
         // At the end of the animation we need to remove the tint.
         if (mState == ScrimState.UNLOCKED) {
             mInFrontTint = Color.TRANSPARENT;
-            mBehindTint = Color.TRANSPARENT;
+            mBehindTint = mState.getBehindTint();
             mBubbleTint = Color.TRANSPARENT;
             updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint);
             updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint);
@@ -996,12 +1031,6 @@
         mScrimBehind.postOnAnimationDelayed(callback, 32 /* delayMillis */);
     }
 
-    public int getBackgroundColor() {
-        int color = mColors.getMainColor();
-        return Color.argb((int) (mScrimBehind.getViewAlpha() * Color.alpha(color)),
-                Color.red(color), Color.green(color), Color.blue(color));
-    }
-
     public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
         // TODO: remove this. This is necessary because of an order-of-operations limitation.
         // The fix is to move more of these class into @StatusBarScope
@@ -1047,7 +1076,7 @@
         pw.print(" tint=0x");
         pw.println(Integer.toHexString(mScrimInFront.getTint()));
 
-        pw.print("  backScrim:");
+        pw.print("  behindScrim:");
         pw.print(" viewAlpha=");
         pw.print(mScrimBehind.getViewAlpha());
         pw.print(" alpha=");
@@ -1055,6 +1084,14 @@
         pw.print(" tint=0x");
         pw.println(Integer.toHexString(mScrimBehind.getTint()));
 
+        pw.print("  notificationsScrim:");
+        pw.print(" viewAlpha=");
+        pw.print(mNotificationsScrim.getViewAlpha());
+        pw.print(" alpha=");
+        pw.print(mNotificationsAlpha);
+        pw.print(" tint=0x");
+        pw.println(Integer.toHexString(mNotificationsScrim.getTint()));
+
         pw.print("  bubbleScrim:");
         pw.print(" viewAlpha=");
         pw.print(mScrimForBubble.getViewAlpha());
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 b82863e..a9774d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -129,6 +129,13 @@
             mBehindAlpha = mDefaultScrimAlpha;
             mBubbleAlpha = 0f;
             mFrontAlpha = 0f;
+            mBehindTint = Color.BLACK;
+        }
+
+        // to make sure correct color is returned before "prepare" is called
+        @Override
+        public int getBehindTint() {
+            return Color.BLACK;
         }
     },
 
@@ -228,7 +235,7 @@
             mAnimateChange = !mLaunchingAffordanceWithPreview;
 
             mFrontTint = Color.TRANSPARENT;
-            mBehindTint = Color.TRANSPARENT;
+            mBehindTint = Color.BLACK;
             mBubbleTint = Color.TRANSPARENT;
             mBlankScreen = false;
 
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 8ed9cd6..7e433e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -173,6 +173,7 @@
 import com.android.systemui.plugins.OverlayPlugin;
 import com.android.systemui.plugins.PluginDependencyProvider;
 import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -206,11 +207,10 @@
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.charging.ChargingRippleView;
 import com.android.systemui.statusbar.charging.WiredChargingRippleController;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -263,7 +263,7 @@
         ActivityStarter, KeyguardStateController.Callback,
         OnHeadsUpChangedListener, CommandQueue.Callbacks,
         ColorExtractor.OnColorsChangedListener, ConfigurationListener,
-        StatusBarStateController.StateListener, ActivityLaunchAnimator.Callback,
+        StatusBarStateController.StateListener,
         LifecycleOwner, BatteryController.BatteryStateChangeCallback {
     public static final boolean MULTIUSER_DEBUG = false;
 
@@ -385,7 +385,6 @@
     private boolean mWakeUpComingFromTouch;
     private PointF mWakeUpTouchLocation;
     private LightRevealScrim mLightRevealScrim;
-    private ChargingRippleView mChargingRipple;
     private WiredChargingRippleController mChargingRippleAnimationController;
     private PowerButtonReveal mPowerButtonReveal;
     private CircleReveal mCircleReveal;
@@ -682,6 +681,9 @@
             new FalsingManager.FalsingBeliefListener() {
                 @Override
                 public void onFalse() {
+                    // Hides quick settings.
+                    mNotificationPanelViewController.resetViews(true);
+                    // Hides bouncer and quick-quick settings.
                     mStatusBarKeyguardViewManager.reset(true);
                 }
             };
@@ -692,6 +694,7 @@
     private boolean mVibrateOnOpening;
     private final VibratorHelper mVibratorHelper;
     private ActivityLaunchAnimator mActivityLaunchAnimator;
+    private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
     protected StatusBarNotificationPresenter mPresenter;
     private NotificationActivityStarter mNotificationActivityStarter;
     private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
@@ -1058,7 +1061,7 @@
                         mMainThreadHandler.post(() -> {
                             mOverlays.remove(plugin);
                             mNotificationShadeWindowController
-                                    .setForcePluginOpen(mOverlays.size() != 0);
+                                    .setForcePluginOpen(mOverlays.size() != 0, this);
                         });
                     }
 
@@ -1081,7 +1084,7 @@
                                         .setStateListener(b -> mOverlays.forEach(
                                                 o -> o.setCollapseDesired(b)));
                                 mNotificationShadeWindowController
-                                        .setForcePluginOpen(mOverlays.size() != 0);
+                                        .setForcePluginOpen(mOverlays.size() != 0, this);
                             });
                         }
                     }
@@ -1215,6 +1218,8 @@
         });
 
         ScrimView scrimBehind = mNotificationShadeWindowView.findViewById(R.id.scrim_behind);
+        ScrimView notificationsScrim = mNotificationShadeWindowView
+                .findViewById(R.id.scrim_notifications);
         ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front);
         ScrimView scrimForBubble = mBubblesManagerOptional.isPresent()
                 ? mBubblesManagerOptional.get().getScrimForBubble() : null;
@@ -1223,10 +1228,9 @@
             mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible);
             mLockscreenLockIconController.onScrimVisibilityChanged(scrimsVisible);
         });
-        mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble);
+        mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront, scrimForBubble);
 
         mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
-        mChargingRippleAnimationController.setViewHost(mNotificationShadeWindowView);
         updateLightRevealScrimVisibility();
 
         mNotificationPanelViewController.initDependencies(
@@ -1370,16 +1374,18 @@
 
     private void setUpPresenter() {
         // Set up the initial notification state.
-        mActivityLaunchAnimator = new ActivityLaunchAnimator(
-                mNotificationShadeWindowViewController, this, mNotificationPanelViewController,
-                mNotificationShadeDepthControllerLazy.get(),
+        mActivityLaunchAnimator = new ActivityLaunchAnimator();
+        mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
+                mNotificationShadeWindowViewController,
+                mNotificationPanelViewController,
                 mStackScrollerController.getNotificationListContainer(),
-                mContext.getMainExecutor());
+                mNotificationShadeDepthControllerLazy.get()
+        );
 
         // TODO: inject this.
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
                 mHeadsUpManager, mNotificationShadeWindowView, mStackScrollerController,
-                mDozeScrimController, mScrimController, mActivityLaunchAnimator,
+                mDozeScrimController, mScrimController, mNotificationShadeWindowController,
                 mDynamicPrivacyController, mKeyguardStateController,
                 mKeyguardIndicationController,
                 this /* statusBar */, mShadeController, mCommandQueue, mInitController,
@@ -1392,6 +1398,7 @@
                 mStatusBarNotificationActivityStarterBuilder
                         .setStatusBar(this)
                         .setActivityLaunchAnimator(mActivityLaunchAnimator)
+                        .setNotificationAnimatorControllerProvider(mNotificationAnimationProvider)
                         .setNotificationPresenter(mPresenter)
                         .setNotificationPanelViewController(mNotificationPanelViewController)
                         .build();
@@ -1519,6 +1526,7 @@
         mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
         mLockscreenLockIconController = statusBarComponent.getLockscreenLockIconController();
         mLockscreenLockIconController.init();
+        statusBarComponent.getAuthRippleController().init();
 
         mNotificationPanelViewController.setLaunchAffordanceListener(
                 mLockscreenLockIconController::onShowingLaunchAffordanceChanged);
@@ -1798,7 +1806,8 @@
     @Override
     public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
         startActivityDismissingKeyguard(intent, false, dismissShade,
-                false /* disallowEnterPictureInPictureWhileLaunching */, callback, 0);
+                false /* disallowEnterPictureInPictureWhileLaunching */, callback, 0,
+                null /* animationController */);
     }
 
     public void setQsExpanded(boolean expanded) {
@@ -1990,16 +1999,16 @@
         return mHeadsUpAppearanceController.shouldBeVisible();
     }
 
+    /** A launch animation was cancelled. */
     //TODO: These can / should probably be moved to NotificationPresenter or ShadeController
-    @Override
     public void onLaunchAnimationCancelled() {
         if (!mPresenter.isCollapsing()) {
             onClosingFinished();
         }
     }
 
-    @Override
-    public void onExpandAnimationFinished(boolean launchIsFullScreen) {
+    /** A launch animation ended. */
+    public void onLaunchAnimationEnd(boolean launchIsFullScreen) {
         if (!mPresenter.isCollapsing()) {
             onClosingFinished();
         }
@@ -2008,20 +2017,20 @@
         }
     }
 
-    @Override
-    public void onExpandAnimationTimedOut() {
+    /** A launch animation timed out. */
+    public void onLaunchAnimationTimedOut(boolean isLaunchForActivity) {
         if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
-                && mActivityLaunchAnimator != null
-                && !mActivityLaunchAnimator.isLaunchForActivity()) {
+                && isLaunchForActivity) {
             onClosingFinished();
         } else {
             mShadeController.collapsePanel(true /* animate */);
         }
     }
 
-    @Override
+    /** Whether we should animate an activity launch. */
     public boolean areLaunchAnimationsEnabled() {
-        return mState == StatusBarState.SHADE;
+        // TODO(b/184121838): Support lock screen launch animations.
+        return mState == StatusBarState.SHADE && !isOccluded();
     }
 
     public boolean isDeviceInVrMode() {
@@ -2725,7 +2734,7 @@
             boolean dismissShade, int flags) {
         startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade,
                 false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
-                flags);
+                flags, null /* animationController */);
     }
 
     public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
@@ -2733,55 +2742,75 @@
         startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, 0);
     }
 
-    public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
+    private void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
             final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching,
-            final Callback callback, int flags) {
+            final Callback callback, int flags,
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
         if (onlyProvisioned && !mDeviceProvisionedController.isDeviceProvisioned()) return;
 
         final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
                 intent, mLockscreenUserManager.getCurrentUserId());
+
+        ActivityLaunchAnimator.Controller animController = null;
+        if (animationController != null && areLaunchAnimationsEnabled()) {
+            animController = dismissShade ? new StatusBarLaunchAnimatorController(
+                    animationController, this, true /* isLaunchForActivity */)
+                    : animationController;
+        }
+        final ActivityLaunchAnimator.Controller animCallbackForLambda = animController;
+
+        // If we animate, we will dismiss the shade only once the animation is done. This is taken
+        // care of by the StatusBarLaunchAnimationController.
+        boolean dismissShadeDirectly = dismissShade && animController == null;
+
         Runnable runnable = () -> {
             mAssistManagerLazy.get().hideAssist();
             intent.setFlags(
                     Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
             intent.addFlags(flags);
-            int result = ActivityManager.START_CANCELED;
-            ActivityOptions options = new ActivityOptions(getActivityOptions(mDisplayId,
-                    null /* remoteAnimation */));
-            options.setDisallowEnterPictureInPictureWhileLaunching(
-                    disallowEnterPictureInPictureWhileLaunching);
-            if (CameraIntents.isInsecureCameraIntent(intent)) {
-                // Normally an activity will set it's requested rotation
-                // animation on its window. However when launching an activity
-                // causes the orientation to change this is too late. In these cases
-                // the default animation is used. This doesn't look good for
-                // the camera (as it rotates the camera contents out of sync
-                // with physical reality). So, we ask the WindowManager to
-                // force the crossfade animation if an orientation change
-                // happens to occur during the launch.
-                options.setRotationAnimationHint(
-                        WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
-            }
-            if (intent.getAction() == Settings.Panel.ACTION_VOLUME) {
-                // Settings Panel is implemented as activity(not a dialog), so
-                // underlying app is paused and may enter picture-in-picture mode
-                // as a result.
-                // So we need to disable picture-in-picture mode here
-                // if it is volume panel.
-                options.setDisallowEnterPictureInPictureWhileLaunching(true);
-            }
-            try {
-                result = ActivityTaskManager.getService().startActivityAsUser(
-                        null, mContext.getBasePackageName(), mContext.getAttributionTag(),
-                        intent,
-                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                        null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
-                        options.toBundle(), UserHandle.CURRENT.getIdentifier());
-            } catch (RemoteException e) {
-                Log.w(TAG, "Unable to start activity", e);
-            }
+            int[] result = new int[] { ActivityManager.START_CANCELED };
+
+            mActivityLaunchAnimator.startIntentWithAnimation(animCallbackForLambda, (adapter) -> {
+                ActivityOptions options = new ActivityOptions(
+                        getActivityOptions(mDisplayId, adapter));
+                options.setDisallowEnterPictureInPictureWhileLaunching(
+                        disallowEnterPictureInPictureWhileLaunching);
+                if (CameraIntents.isInsecureCameraIntent(intent)) {
+                    // Normally an activity will set it's requested rotation
+                    // animation on its window. However when launching an activity
+                    // causes the orientation to change this is too late. In these cases
+                    // the default animation is used. This doesn't look good for
+                    // the camera (as it rotates the camera contents out of sync
+                    // with physical reality). So, we ask the WindowManager to
+                    // force the crossfade animation if an orientation change
+                    // happens to occur during the launch.
+                    options.setRotationAnimationHint(
+                            WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
+                }
+                if (intent.getAction() == Settings.Panel.ACTION_VOLUME) {
+                    // Settings Panel is implemented as activity(not a dialog), so
+                    // underlying app is paused and may enter picture-in-picture mode
+                    // as a result.
+                    // So we need to disable picture-in-picture mode here
+                    // if it is volume panel.
+                    options.setDisallowEnterPictureInPictureWhileLaunching(true);
+                }
+
+                try {
+                    result[0] = ActivityTaskManager.getService().startActivityAsUser(
+                            null, mContext.getBasePackageName(), mContext.getAttributionTag(),
+                            intent,
+                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                            null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
+                            options.toBundle(), UserHandle.CURRENT.getIdentifier());
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Unable to start activity", e);
+                }
+                return result[0];
+            });
+
             if (callback != null) {
-                callback.onActivityStarted(result);
+                callback.onActivityStarted(result[0]);
             }
         };
         Runnable cancelRunnable = () -> {
@@ -2789,7 +2818,7 @@
                 callback.onActivityStarted(ActivityManager.START_CANCELED);
             }
         };
-        executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade,
+        executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly,
                 afterKeyguardGone, true /* deferred */);
     }
 
@@ -3134,18 +3163,34 @@
     }
 
     @Override
-    public void postStartActivityDismissingKeyguard(final PendingIntent intent) {
-        mHandler.post(() -> startPendingIntentDismissingKeyguard(intent));
+    public void postStartActivityDismissingKeyguard(PendingIntent intent) {
+        postStartActivityDismissingKeyguard(intent, null /* animationController */);
+    }
+
+    @Override
+    public void postStartActivityDismissingKeyguard(final PendingIntent intent,
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
+        mHandler.post(() -> startPendingIntentDismissingKeyguard(intent,
+                null /* intentSentUiThreadCallback */, animationController));
     }
 
     @Override
     public void postStartActivityDismissingKeyguard(final Intent intent, int delay) {
-        mHandler.postDelayed(() ->
-                handleStartActivityDismissingKeyguard(intent, true /*onlyProvisioned*/), delay);
+        postStartActivityDismissingKeyguard(intent, delay, null /* animationController */);
     }
 
-    private void handleStartActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
-        startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
+    @Override
+    public void postStartActivityDismissingKeyguard(Intent intent, int delay,
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
+        mHandler.postDelayed(
+                () ->
+                        startActivityDismissingKeyguard(intent, true /* onlyProvisioned */,
+                                true /* dismissShade */,
+                                false /* disallowEnterPictureInPictureWhileLaunching */,
+                                null /* callback */,
+                                0 /* flags */,
+                                animationController),
+                delay);
     }
 
     @Override
@@ -3618,6 +3663,23 @@
         mShadeController.runPostCollapseRunnables();
     }
 
+    /**
+     * Collapse the panel directly if we are on the main thread, post the collapsing on the main
+     * thread if we are not.
+     */
+    void collapsePanelOnMainThread() {
+        if (Looper.getMainLooper().isCurrentThread()) {
+            mShadeController.collapsePanel();
+        } else {
+            mContext.getMainExecutor().execute(mShadeController::collapsePanel);
+        }
+    }
+
+    /** Collapse the panel. The collapsing will be animated for the given {@code duration}. */
+    void collapsePanelWithDuration(int duration) {
+        mNotificationPanelViewController.collapseWithDuration(duration);
+    }
+
     @Override
     public void onStatePreChange(int oldState, int newState) {
         // If we're visible and switched to SHADE_LOCKED (the user dragged
@@ -4043,7 +4105,8 @@
             final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
             startActivityDismissingKeyguard(cameraIntent,
                     false /* onlyProvisioned */, true /* dismissShade */,
-                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0);
+                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
+                    null /* animationController */);
         } else {
             if (!mDeviceInteractive) {
                 // Avoid flickering of the scrim when we instant launch the camera and the bouncer
@@ -4094,7 +4157,8 @@
         if (!mStatusBarKeyguardViewManager.isShowing()) {
             startActivityDismissingKeyguard(emergencyIntent,
                     false /* onlyProvisioned */, true /* dismissShade */,
-                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0);
+                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
+                    null /* animationController */);
             return;
         }
 
@@ -4415,7 +4479,14 @@
         KeyboardShortcuts.dismiss();
     }
 
-    public void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone) {
+    /**
+     * Dismiss the keyguard then execute an action.
+     *
+     * @param action The action to execute after dismissing the keyguard.
+     * @param collapsePanel Whether we should collapse the panel after dismissing the keyguard.
+     */
+    private void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone,
+            boolean collapsePanel) {
         if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
 
         dismissKeyguardThenExecute(() -> {
@@ -4431,7 +4502,8 @@
                 action.run();
             }).start();
 
-            return mShadeController.collapsePanel();
+            boolean deferred = collapsePanel ? mShadeController.collapsePanel() : false;
+            return deferred;
         }, afterKeyguardGone);
     }
 
@@ -4443,27 +4515,54 @@
     @Override
     public void startPendingIntentDismissingKeyguard(
             final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) {
-        startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback, null /* row */);
+        startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback,
+                (ActivityLaunchAnimator.Controller) null);
+    }
+
+    @Override
+    public void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentUiThreadCallback, View associatedView) {
+        ActivityLaunchAnimator.Controller animationController = null;
+        if (associatedView instanceof ExpandableNotificationRow) {
+            animationController = mNotificationAnimationProvider.getAnimatorController(
+                    ((ExpandableNotificationRow) associatedView));
+        }
+
+        startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback,
+                animationController);
     }
 
     @Override
     public void startPendingIntentDismissingKeyguard(
             final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback,
-            View associatedView) {
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
         final boolean afterKeyguardGone = intent.isActivity()
                 && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
 
+        boolean animate = animationController != null && areLaunchAnimationsEnabled();
+        boolean collapse = !animate;
         executeActionDismissingKeyguard(() -> {
             try {
-                intent.send(null, 0, null, null, null, null, getActivityOptions(
-                        mDisplayId,
-                        mActivityLaunchAnimator.getLaunchAnimation(associatedView, isOccluded())));
+                // We wrap animationCallback with a StatusBarLaunchAnimatorController so that the
+                // shade is collapsed after the animation (or when it is cancelled, aborted, etc).
+                ActivityLaunchAnimator.Controller controller =
+                        animate ? new StatusBarLaunchAnimatorController(animationController, this,
+                                intent.isActivity())
+                                : null;
+
+                mActivityLaunchAnimator.startPendingIntentWithAnimation(
+                        controller,
+                        (animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null,
+                                null, getActivityOptions(mDisplayId, animationAdapter)));
             } catch (PendingIntent.CanceledException e) {
                 // the stack trace isn't very helpful here.
                 // Just log the exception message.
                 Log.w(TAG, "Sending intent failed: " + e);
-
+                if (!collapse) {
+                    // executeActionDismissingKeyguard did not collapse for us already.
+                    collapsePanelOnMainThread();
+                }
                 // TODO: Dismiss Keyguard.
             }
             if (intent.isActivity()) {
@@ -4472,7 +4571,7 @@
             if (intentSentUiThreadCallback != null) {
                 postOnUiThread(intentSentUiThreadCallback);
             }
-        }, afterKeyguardGone);
+        }, afterKeyguardGone, collapse);
     }
 
     private void postOnUiThread(Runnable runnable) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ef2444e..2815ce7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -187,6 +187,7 @@
     private int mLastBiometricMode;
     private boolean mLastLockVisible;
     private boolean mLastLockOrientationIsPortrait;
+    private boolean mQsExpanded;
 
     private OnDismissAction mAfterKeyguardGoneAction;
     private Runnable mKeyguardGoneCancelAction;
@@ -1128,9 +1129,16 @@
     }
 
     /**
+     * Whether qs is currently expanded.
+     */
+    public boolean isQsExpanded() {
+        return mQsExpanded;
+    }
+    /**
      * Set whether qs is currently expanded
      */
     public void setQsExpanded(boolean expanded) {
+        mQsExpanded = expanded;
         if (mAlternateAuthInterceptor != null) {
             mAlternateAuthInterceptor.setQsExpanded(expanded);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
new file mode 100644
index 0000000..d45f64f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -0,0 +1,47 @@
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator
+
+/**
+ * A [ActivityLaunchAnimator.Controller] that takes care of collapsing the status bar at the right
+ * time.
+ */
+class StatusBarLaunchAnimatorController(
+    private val delegate: ActivityLaunchAnimator.Controller,
+    private val statusBar: StatusBar,
+    private val isLaunchForActivity: Boolean = true
+) : ActivityLaunchAnimator.Controller by delegate {
+    override fun onIntentStarted(willAnimate: Boolean) {
+        delegate.onIntentStarted(willAnimate)
+        if (!willAnimate) {
+            statusBar.collapsePanelOnMainThread()
+        }
+    }
+
+    override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+        delegate.onLaunchAnimationStart(isExpandingFullyAbove)
+        if (!isExpandingFullyAbove) {
+            statusBar.collapsePanelWithDuration(ActivityLaunchAnimator.ANIMATION_DURATION.toInt())
+        }
+    }
+
+    override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+        delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
+        statusBar.onLaunchAnimationEnd(isExpandingFullyAbove)
+    }
+
+    override fun onLaunchAnimationCancelled() {
+        delegate.onLaunchAnimationCancelled()
+        statusBar.onLaunchAnimationCancelled()
+    }
+
+    override fun onLaunchAnimationTimedOut() {
+        delegate.onLaunchAnimationTimedOut()
+        statusBar.onLaunchAnimationTimedOut(isLaunchForActivity)
+    }
+
+    override fun onLaunchAnimationAborted() {
+        delegate.onLaunchAnimationAborted()
+        statusBar.collapsePanelOnMainThread()
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 801ac96..2e918da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -21,7 +21,6 @@
 import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
 
 import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -40,7 +39,6 @@
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.EventLog;
-import android.view.RemoteAnimationAdapter;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -52,6 +50,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FeatureFlags;
@@ -60,11 +59,10 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -124,6 +122,7 @@
     private final NotificationPresenter mPresenter;
     private final NotificationPanelViewController mNotificationPanel;
     private final ActivityLaunchAnimator mActivityLaunchAnimator;
+    private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
     private final OnUserInteractionCallback mOnUserInteractionCallback;
 
     private boolean mIsCollapsingToShowActivityOverLockscreen;
@@ -162,7 +161,8 @@
             StatusBar statusBar,
             NotificationPresenter presenter,
             NotificationPanelViewController panel,
-            ActivityLaunchAnimator activityLaunchAnimator) {
+            ActivityLaunchAnimator activityLaunchAnimator,
+            NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
         mContext = context;
         mCommandQueue = commandQueue;
         mMainThreadHandler = mainThreadHandler;
@@ -198,6 +198,7 @@
         mPresenter = presenter;
         mNotificationPanel = panel;
         mActivityLaunchAnimator = activityLaunchAnimator;
+        mNotificationAnimationProvider = notificationAnimationProvider;
 
         if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
             mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@@ -400,17 +401,15 @@
         }
 
         if (Looper.getMainLooper().isCurrentThread()) {
-            mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
+            expandBubbleStack(entry);
         } else {
-            mMainThreadHandler.post(
-                    () -> mBubblesManagerOptional.get().expandStackAndSelectBubble(entry));
+            mMainThreadHandler.post(() -> expandBubbleStack(entry));
         }
+    }
 
-        // expandStackAndSelectBubble won't affect shouldCollapse, so we can collapse directly even
-        // if we are not on the main thread.
-        if (shouldCollapse()) {
-            collapseOnMainThread();
-        }
+    private void expandBubbleStack(NotificationEntry entry) {
+        mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
+        mShadeController.collapsePanel();
     }
 
     private void startNotificationIntent(
@@ -420,32 +419,36 @@
             ExpandableNotificationRow row,
             boolean wasOccluded,
             boolean isActivityIntent) {
-        RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row,
-                wasOccluded);
         mLogger.logStartNotificationIntent(entry.getKey(), intent);
         try {
-            if (adapter != null) {
-                ActivityTaskManager.getService()
-                        .registerRemoteAnimationForNextActivityStart(
-                                intent.getCreatorPackage(), adapter);
+            ActivityLaunchAnimator.Controller animationController = null;
+            if (!wasOccluded && mStatusBar.areLaunchAnimationsEnabled()) {
+                animationController = new StatusBarLaunchAnimatorController(
+                        mNotificationAnimationProvider.getAnimatorController(row), mStatusBar,
+                        isActivityIntent);
             }
-            long eventTime = row.getAndResetLastActionUpTime();
-            Bundle options = eventTime > 0
-                    ? getActivityOptions(
-                            mStatusBar.getDisplayId(),
-                            adapter,
-                            mKeyguardStateController.isShowing(),
-                            eventTime)
-                    : getActivityOptions(mStatusBar.getDisplayId(), adapter);
-            int launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
-                    null, null, options);
-            mMainThreadHandler.post(() -> {
-                mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
-                if (shouldCollapse()) {
-                    collapseOnMainThread();
-                }
-            });
-        } catch (RemoteException | PendingIntent.CanceledException e) {
+
+            mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController,
+                    (adapter) -> {
+                        long eventTime = row.getAndResetLastActionUpTime();
+                        Bundle options = eventTime > 0
+                                ? getActivityOptions(
+                                mStatusBar.getDisplayId(),
+                                adapter,
+                                mKeyguardStateController.isShowing(),
+                                eventTime)
+                                : getActivityOptions(mStatusBar.getDisplayId(), adapter);
+                        return intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
+                                null, null, options);
+                    });
+
+            // Note that other cases when we should still collapse (like activity already on top) is
+            // handled by the StatusBarLaunchAnimatorController.
+            boolean shouldCollapse = animationController == null;
+            if (shouldCollapse) {
+                collapseOnMainThread();
+            }
+        } catch (PendingIntent.CanceledException e) {
             // the stack trace isn't very helpful here.
             // Just log the exception message.
             mLogger.logSendingIntentFailed(e);
@@ -458,20 +461,30 @@
             ExpandableNotificationRow row) {
         mActivityStarter.dismissKeyguardThenExecute(() -> {
             AsyncTask.execute(() -> {
-                int launchResult = TaskStackBuilder.create(mContext)
-                        .addNextIntentWithParentStack(intent)
-                        .startActivities(getActivityOptions(
-                                mStatusBar.getDisplayId(),
-                                mActivityLaunchAnimator.getLaunchAnimation(
-                                        row, mStatusBar.isOccluded())),
-                                new UserHandle(UserHandle.getUserId(appUid)));
+                ActivityLaunchAnimator.Controller animationController = null;
+                if (!mStatusBar.isOccluded() && mStatusBar.areLaunchAnimationsEnabled()) {
+                    animationController = new StatusBarLaunchAnimatorController(
+                            mNotificationAnimationProvider.getAnimatorController(row), mStatusBar,
+                            true /* isActivityIntent */);
+                }
+
+                mActivityLaunchAnimator.startIntentWithAnimation(
+                        animationController,
+                        (adapter) -> TaskStackBuilder.create(mContext)
+                                .addNextIntentWithParentStack(intent)
+                                .startActivities(getActivityOptions(
+                                        mStatusBar.getDisplayId(),
+                                        adapter),
+                                        new UserHandle(UserHandle.getUserId(appUid))));
+
+                // Note that other cases when we should still collapse (like activity already on
+                // top) is handled by the StatusBarLaunchAnimatorController.
+                boolean shouldCollapse = animationController == null;
 
                 // Putting it back on the main thread, since we're touching views
                 mMainThreadHandler.post(() -> {
-                    mActivityLaunchAnimator.setLaunchResult(launchResult,
-                            true /* isActivityIntent */);
                     removeHUN(row);
-                    if (shouldCollapse()) {
+                    if (shouldCollapse) {
                         mCommandQueue.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
                                 true /* force */);
                     }
@@ -494,11 +507,10 @@
                     tsb.addNextIntent(intent);
                 }
                 tsb.startActivities(null, UserHandle.CURRENT);
-                if (shouldCollapse()) {
-                    // Putting it back on the main thread, since we're touching views
-                    mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
-                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
-                }
+
+                // Putting it back on the main thread, since we're touching views
+                mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
+                        CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
             });
             return true;
         }, null, false /* afterKeyguardGone */);
@@ -576,11 +588,6 @@
         }
     }
 
-    private boolean shouldCollapse() {
-        return mStatusBarStateController.getState() != StatusBarState.SHADE
-                || !mActivityLaunchAnimator.isAnimationPending();
-    }
-
     private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) {
         if (mPresenter.isDeviceInVrMode()) {
             return true;
@@ -639,6 +646,7 @@
         private NotificationPresenter mNotificationPresenter;
         private NotificationPanelViewController mNotificationPanelViewController;
         private ActivityLaunchAnimator mActivityLaunchAnimator;
+        private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
 
         @Inject
         public Builder(
@@ -714,12 +722,20 @@
             return this;
         }
 
+        /** Set the ActivityLaunchAnimator. */
         public Builder setActivityLaunchAnimator(ActivityLaunchAnimator activityLaunchAnimator) {
             mActivityLaunchAnimator = activityLaunchAnimator;
             return this;
         }
 
-        /** Set the NotificationPanelViewController */
+        /** Set the NotificationLaunchAnimatorControllerProvider. */
+        public Builder setNotificationAnimatorControllerProvider(
+                NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
+            mNotificationAnimationProvider = notificationAnimationProvider;
+            return this;
+        }
+
+        /** Set the NotificationPanelViewController. */
         public Builder setNotificationPanelViewController(
                 NotificationPanelViewController notificationPanelViewController) {
             mNotificationPanelViewController = notificationPanelViewController;
@@ -759,7 +775,8 @@
                     mStatusBar,
                     mNotificationPresenter,
                     mNotificationPanelViewController,
-                    mActivityLaunchAnimator);
+                    mActivityLaunchAnimator,
+                    mNotificationAnimationProvider);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 94edd1e..088f947 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -57,7 +57,6 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -117,7 +116,7 @@
 
     private final AccessibilityManager mAccessibilityManager;
     private final KeyguardManager mKeyguardManager;
-    private final ActivityLaunchAnimator mActivityLaunchAnimator;
+    private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final IStatusBarService mBarService;
     private final DynamicPrivacyController mDynamicPrivacyController;
     private boolean mReinflateNotificationsOnUserSwitched;
@@ -133,7 +132,7 @@
             NotificationStackScrollLayoutController stackScrollerController,
             DozeScrimController dozeScrimController,
             ScrimController scrimController,
-            ActivityLaunchAnimator activityLaunchAnimator,
+            NotificationShadeWindowController notificationShadeWindowController,
             DynamicPrivacyController dynamicPrivacyController,
             KeyguardStateController keyguardStateController,
             KeyguardIndicationController keyguardIndicationController,
@@ -152,7 +151,7 @@
         mShadeController = shadeController;
         mCommandQueue = commandQueue;
         mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
-        mActivityLaunchAnimator = activityLaunchAnimator;
+        mNotificationShadeWindowController = notificationShadeWindowController;
         mAboveShelfObserver.setListener(statusBarWindow.findViewById(
                 R.id.notification_container_parent));
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
@@ -272,8 +271,7 @@
     @Override
     public boolean isCollapsing() {
         return mNotificationPanel.isCollapsing()
-                || mActivityLaunchAnimator.isAnimationPending()
-                || mActivityLaunchAnimator.isAnimationRunning();
+                || mNotificationShadeWindowController.isLaunchingActivity();
     }
 
     private void maybeEndAmbientPulse() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index ecd9613..e0cbbf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -18,6 +18,7 @@
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import com.android.systemui.biometrics.AuthRippleController;
 import com.android.systemui.statusbar.phone.LockscreenLockIconController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
@@ -79,4 +80,10 @@
      */
     @StatusBarScope
     LockscreenLockIconController getLockscreenLockIconController();
+
+    /**
+     * Creates an AuthRippleController
+     */
+    @StatusBarScope
+    AuthRippleController getAuthRippleController();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 781abe6..0ce7538 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 
 import com.android.systemui.R;
+import com.android.systemui.biometrics.AuthRippleView;
 import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
@@ -44,4 +45,13 @@
             NotificationShadeWindowView notificationShadeWindowView) {
         return notificationShadeWindowView.findViewById(R.id.lock_icon);
     }
+
+    /** */
+    @Provides
+    @StatusBarComponent.StatusBarScope
+    @Nullable
+    public static AuthRippleView getAuthRippleView(
+            NotificationShadeWindowView notificationShadeWindowView) {
+        return notificationShadeWindowView.findViewById(R.id.auth_ripple);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index fc9a35d..c9011f4 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -81,7 +81,7 @@
 @SysUISingleton
 public class ThemeOverlayController extends SystemUI implements Dumpable {
     protected static final String TAG = "ThemeOverlayController";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG = true;
 
     protected static final int NEUTRAL = 0;
     protected static final int ACCENT = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
index 603d423..46611e0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
@@ -50,6 +50,8 @@
 
     private var desiredMeasureWidth = 0
     private var desiredMeasureHeight = 0
+    private var transitionVisibility = View.VISIBLE
+
     /**
      * The measured state of this view which is the one we will lay ourselves out with. This
      * may differ from the currentState if there is an external animation or transition running.
@@ -81,6 +83,13 @@
         }
     }
 
+    override fun setTransitionVisibility(visibility: Int) {
+        // We store the last transition visibility assigned to this view to restore it later if
+        // necessary.
+        super.setTransitionVisibility(visibility)
+        transitionVisibility = visibility
+    }
+
     override fun onFinishInflate() {
         super.onFinishInflate()
         val childCount = childCount
@@ -162,7 +171,16 @@
         updateBounds()
         translationX = currentState.translation.x
         translationY = currentState.translation.y
+
         CrossFadeHelper.fadeIn(this, currentState.alpha)
+
+        // CrossFadeHelper#fadeIn will change this view visibility, which overrides the transition
+        // visibility. We set the transition visibility again to make sure that this view plays well
+        // with GhostView, which sets the transition visibility and is used for activity launch
+        // animations.
+        if (transitionVisibility != View.VISIBLE) {
+            setTransitionVisibility(transitionVisibility)
+        }
     }
 
     private fun applyCurrentStateOnPredraw() {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index ba063a8..c1835db 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -28,8 +28,10 @@
 
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.LifecycleActivity;
 
@@ -44,6 +46,7 @@
 
     private final QuickAccessWalletClient mQuickAccessWalletClient;
     private final KeyguardStateController mKeyguardStateController;
+    private final KeyguardDismissUtil mKeyguardDismissUtil;
     private final ActivityStarter mActivityStarter;
     private final Executor mExecutor;
     private final Handler mHandler;
@@ -54,12 +57,14 @@
     public WalletActivity(
             QuickAccessWalletClient quickAccessWalletClient,
             KeyguardStateController keyguardStateController,
+            KeyguardDismissUtil keyguardDismissUtil,
             ActivityStarter activityStarter,
             @Background Executor executor,
-            @Background Handler handler,
+            @Main Handler handler,
             UserTracker userTracker) {
         mQuickAccessWalletClient = quickAccessWalletClient;
         mKeyguardStateController = keyguardStateController;
+        mKeyguardDismissUtil = keyguardDismissUtil;
         mActivityStarter = activityStarter;
         mExecutor = executor;
         mHandler = handler;
@@ -88,20 +93,30 @@
                 mExecutor,
                 mHandler,
                 mUserTracker,
-                !mKeyguardStateController.isUnlocked());
+                mKeyguardStateController);
+        // Clicking the wallet button will open the wallet app if the device is unlocked; bring up
+        // the security bouncer otherwise.
         walletView.getWalletButton().setOnClickListener(
-                v -> mActivityStarter.startActivity(
-                        mQuickAccessWalletClient.createWalletIntent(), true));
+                v -> {
+                    if (mKeyguardStateController.isUnlocked()) {
+                        mActivityStarter.startActivity(
+                                mQuickAccessWalletClient.createWalletIntent(), true);
+                    } else {
+                        mKeyguardDismissUtil.executeWhenUnlocked(() -> false, false);
+                    }
+                });
     }
 
     @Override
     protected void onStart() {
         super.onStart();
+        mKeyguardStateController.addCallback(mWalletScreenController);
     }
 
     @Override
     protected void onResume() {
         super.onResume();
+        mWalletScreenController.queryWalletCards();
     }
 
     @Override
@@ -116,6 +131,7 @@
 
     @Override
     protected void onDestroy() {
+        mKeyguardStateController.removeCallback(mWalletScreenController);
         mWalletScreenController.onDismissed();
         super.onDestroy();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index a93f0f0..d195062 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -42,6 +42,7 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -52,7 +53,8 @@
 public class WalletScreenController implements
         WalletCardCarousel.OnSelectionListener,
         QuickAccessWalletClient.OnWalletCardsRetrievedCallback,
-        QuickAccessWalletClient.WalletServiceEventListener {
+        QuickAccessWalletClient.WalletServiceEventListener,
+        KeyguardStateController.Callback {
 
     private static final String TAG = "WalletScreenCtrl";
     private static final String PREFS_HAS_CARDS = "has_cards";
@@ -65,6 +67,7 @@
     private final ActivityStarter mActivityStarter;
     private final Executor mExecutor;
     private final Handler mHandler;
+    private final KeyguardStateController mKeyguardStateController;
     private final Runnable mSelectionRunnable = this::selectCard;
     private final SharedPreferences mPrefs;
     private final WalletView mWalletView;
@@ -72,7 +75,6 @@
 
     @VisibleForTesting String mSelectedCardId;
     @VisibleForTesting boolean mIsDismissed;
-    private boolean mIsDeviceLocked;
     private boolean mHasRegisteredListener;
 
     public WalletScreenController(
@@ -83,12 +85,13 @@
             Executor executor,
             Handler handler,
             UserTracker userTracker,
-            boolean isDeviceLocked) {
+            KeyguardStateController keyguardStateController) {
         mContext = context;
         mWalletClient = walletClient;
         mActivityStarter = activityStarter;
         mExecutor = executor;
         mHandler = handler;
+        mKeyguardStateController = keyguardStateController;
         mPrefs = userTracker.getUserContext().getSharedPreferences(TAG, Context.MODE_PRIVATE);
         mWalletView = walletView;
         mWalletView.setMinimumHeight(getExpectedMinHeight());
@@ -105,7 +108,6 @@
             // to decrease perceived latency.
             showEmptyStateView();
         }
-        mIsDeviceLocked = isDeviceLocked;
     }
 
     /**
@@ -122,11 +124,17 @@
         for (WalletCard card : walletCards) {
             data.add(new QAWalletCardViewInfo(mContext, card));
         }
+
+        // Get on main thread for UI updates.
         mHandler.post(() -> {
+            if (mIsDismissed) {
+                return;
+            }
             if (data.isEmpty()) {
                 showEmptyStateView();
             } else {
-                mWalletView.showCardCarousel(data, response.getSelectedIndex(), mIsDeviceLocked);
+                mWalletView.showCardCarousel(
+                        data, response.getSelectedIndex(), !mKeyguardStateController.isUnlocked());
             }
             // The empty state view will not be shown preemptively next time if cards were returned
             mPrefs.edit().putBoolean(PREFS_HAS_CARDS, !data.isEmpty()).apply();
@@ -140,10 +148,10 @@
      */
     @Override
     public void onWalletCardRetrievalError(@NonNull GetWalletCardsError error) {
-        if (mIsDismissed) {
-            return;
-        }
         mHandler.post(() -> {
+            if (mIsDismissed) {
+                return;
+            }
             mWalletView.showErrorMessage(error.getMessage());
         });
     }
@@ -170,6 +178,11 @@
     }
 
     @Override
+    public void onKeyguardFadingAwayChanged() {
+        queryWalletCards();
+    }
+
+    @Override
     public void onCardSelected(@NonNull WalletCardViewInfo card) {
         if (mIsDismissed) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index d2f0720..a379394 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -103,7 +103,9 @@
         CharSequence centerCardText = centerCard.getLabel();
         Drawable icon = centerCard.getIcon();
         if (icon != null) {
-            mIcon.setImageDrawable(resizeDrawable(getResources(), icon));
+            Drawable drawable = resizeDrawable(getResources(), icon);
+            drawable.setTint(mContext.getColor(R.color.GM2_blue_600));
+            mIcon.setImageDrawable(drawable);
             mIcon.setVisibility(VISIBLE);
         } else {
             mIcon.setVisibility(INVISIBLE);
@@ -126,7 +128,6 @@
         mCardCarouselContainer.setVisibility(VISIBLE);
         mErrorView.setVisibility(GONE);
         if (isDeviceLocked) {
-            // TODO(b/182964813): Add click action to prompt device unlock.
             mWalletButton.setText(R.string.wallet_button_label_device_locked);
         } else {
             mWalletButton.setText(R.string.wallet_button_label_device_unlocked);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 33b2d67..743dd46 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -157,11 +157,12 @@
     @WMSingleton
     @Provides
     static PhonePipMenuController providesPipPhoneMenuController(Context context,
-            PipMediaController pipMediaController, SystemWindows systemWindows,
+            PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+            SystemWindows systemWindows,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler) {
-        return new PhonePipMenuController(context, pipMediaController, systemWindows,
-                mainExecutor, mainHandler);
+        return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
+                systemWindows, mainExecutor, mainHandler);
     }
 
     @WMSingleton
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 70a7b7a..0fcd79b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -18,26 +18,34 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.res.Resources;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.clock.ClockManager;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
@@ -50,6 +58,8 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.verification.VerificationMode;
 
+import java.util.concurrent.Executor;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@@ -78,6 +88,12 @@
     ContentResolver mContentResolver;
     @Mock
     BroadcastDispatcher mBroadcastDispatcher;
+    @Mock
+    private PluginManager mPluginManager;
+    @Mock
+    private FeatureFlags mFeatureFlags;
+    @Mock
+    private Executor mExecutor;
 
     private KeyguardClockSwitchController mController;
 
@@ -87,6 +103,8 @@
 
         when(mView.findViewById(com.android.systemui.R.id.left_aligned_notification_icon_container))
                 .thenReturn(mNotificationIcons);
+        when(mView.getContext()).thenReturn(getContext());
+        when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
         when(mView.isAttachedToWindow()).thenReturn(true);
         when(mResources.getString(anyInt())).thenReturn("h:mm");
         mController = new KeyguardClockSwitchController(
@@ -98,7 +116,10 @@
                 mKeyguardSliceViewController,
                 mNotificationIconAreaController,
                 mContentResolver,
-                mBroadcastDispatcher);
+                mBroadcastDispatcher,
+                mPluginManager,
+                mFeatureFlags,
+                mExecutor);
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
         when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
@@ -182,6 +203,45 @@
         verify(mView).setClockPlugin(mClockPlugin, StatusBarState.SHADE);
     }
 
+    @Test
+    public void testSmartspacePluginConnectedRemovesKeyguardStatusArea() {
+        mController.init();
+
+        View statusArea = mock(View.class);
+        when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(statusArea);
+
+        View nic = mock(View.class);
+        when(mView.findViewById(R.id.left_aligned_notification_icon_container)).thenReturn(nic);
+        when(nic.getLayoutParams()).thenReturn(mock(RelativeLayout.LayoutParams.class));
+
+        BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class);
+        TestView view = mock(TestView.class);
+        when(plugin.getView(any())).thenReturn(view);
+
+        mController.mPluginListener.onPluginConnected(plugin, mContext);
+        verify(statusArea).setVisibility(View.GONE);
+    }
+
+    @Test
+    public void testSmartspacePluginDisconnectedShowsKeyguardStatusArea() {
+        mController.init();
+
+        View statusArea = mock(View.class);
+        when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(statusArea);
+
+        View nic = mock(View.class);
+        when(mView.findViewById(R.id.left_aligned_notification_icon_container)).thenReturn(nic);
+        when(nic.getLayoutParams()).thenReturn(mock(RelativeLayout.LayoutParams.class));
+
+        BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class);
+        TestView view = mock(TestView.class);
+        when(plugin.getView(any())).thenReturn(view);
+
+        mController.mPluginListener.onPluginConnected(plugin, mContext);
+        mController.mPluginListener.onPluginDisconnected(plugin);
+        verify(statusArea).setVisibility(View.VISIBLE);
+    }
+
     private void verifyAttachment(VerificationMode times) {
         verify(mClockManager, times).addOnClockChangedListener(
                 any(ClockManager.ClockChangedListener.class));
@@ -191,4 +251,12 @@
                 any(ColorExtractor.OnColorsChangedListener.class));
         verify(mView, times).updateColors(mGradientColors);
     }
+
+    private static class TestView extends View implements BcSmartspaceDataPlugin.SmartspaceView {
+        TestView(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        public void registerDataProvider(BcSmartspaceDataPlugin plugin) { }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 9659610e..d3a2d2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -303,6 +303,19 @@
     }
 
     @Test
+    public void performA11yActions_visible_notifyAccessibilityActionPerformed() {
+        final int displayId = mContext.getDisplayId();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
+                    Float.NaN);
+        });
+
+        mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null);
+
+        verify(mWindowMagnifierCallback).onAccessibilityActionPerformed(eq(displayId));
+    }
+
+    @Test
     public void onNavigationModeChanged_updateMirrorViewLayout() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index ad1ce76..7833114 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -118,6 +118,16 @@
     }
 
     @Test
+    public void onAccessibilityActionPerformed_enabled_notifyCallback() throws RemoteException {
+        mCommandQueue.requestWindowMagnificationConnection(true);
+        waitForIdleSync();
+
+        mWindowMagnification.onAccessibilityActionPerformed(Display.DEFAULT_DISPLAY);
+
+        verify(mConnectionCallback).onAccessibilityActionPerformed(eq(Display.DEFAULT_DISPLAY));
+    }
+
+    @Test
     public void onConfigurationChanged_updateModeSwitches() {
         final Configuration config = new Configuration();
         config.densityDpi = Configuration.DENSITY_DPI_ANY;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 8252ff4..3f0831c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -49,6 +50,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
@@ -71,6 +73,7 @@
 public class AppOpsControllerTest extends SysuiTestCase {
     private static final String TEST_PACKAGE_NAME = "test";
     private static final String TEST_ATTRIBUTION_NAME = "attribution";
+    private static final String SYSTEM_PKG = "android";
     private static final int TEST_UID = UserHandle.getUid(0, 0);
     private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
     private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
@@ -84,8 +87,6 @@
     @Mock
     private DumpManager mDumpManager;
     @Mock
-    private PermissionFlagsCache mFlagsCache;
-    @Mock
     private PackageManager mPackageManager;
     @Mock
     private IndividualSensorPrivacyController mSensorPrivacyController;
@@ -101,22 +102,19 @@
     private AppOpsControllerImpl mController;
     private TestableLooper mTestableLooper;
 
+    private String mExemptedRolePkgName;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mTestableLooper = TestableLooper.get(this);
+        mExemptedRolePkgName = getContext().getString(R.string.config_systemUiIntelligence);
 
         getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
 
         // All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of
         // TEST_UID_NON_USER_SENSITIVE are user sensitive.
         getContext().setMockPackageManager(mPackageManager);
-        when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID))).thenReturn(
-                PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
-        when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID_OTHER)))
-                .thenReturn(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
-        when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
-                eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0);
 
         doAnswer((invocation) -> mRecordingCallback = invocation.getArgument(0))
                 .when(mAudioManager).registerAudioRecordingCallback(any(), any());
@@ -135,7 +133,6 @@
                 mContext,
                 mTestableLooper.getLooper(),
                 mDumpManager,
-                mFlagsCache,
                 mAudioManager,
                 mSensorPrivacyController,
                 mDispatcher,
@@ -242,18 +239,26 @@
     }
 
     @Test
-    public void nonUserSensitiveOpsAreIgnored() {
+    public void systemAndExemptedRolesAreIgnored() {
+        assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals(""));
+
         mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
-                TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+                TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
+        assertEquals(0, mController.getActiveAppOpsForUser(
+                UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
+        mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+                TEST_UID_NON_USER_SENSITIVE, SYSTEM_PKG, true);
         assertEquals(0, mController.getActiveAppOpsForUser(
                 UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
     }
 
     @Test
-    public void nonUserSensitiveOpsNotNotified() {
+    public void exemptedRoleNotNotified() {
+        assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals(""));
+
         mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
         mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
-                TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+                TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
 
         mTestableLooper.processAllMessages();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
deleted file mode 100644
index 0fb0ce0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.appops
-
-import android.content.pm.PackageManager
-import android.os.UserHandle
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNotEquals
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class PermissionFlagsCacheTest : SysuiTestCase() {
-
-    companion object {
-        const val TEST_PERMISSION = "test_permission"
-        const val TEST_PACKAGE = "test_package"
-        const val TEST_UID1 = 1000
-        const val TEST_UID2 = UserHandle.PER_USER_RANGE + 1000
-    }
-
-    @Mock
-    private lateinit var packageManager: PackageManager
-
-    private lateinit var executor: FakeExecutor
-    private lateinit var flagsCache: PermissionFlagsCache
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        executor = FakeExecutor(FakeSystemClock())
-
-        flagsCache = PermissionFlagsCache(packageManager, executor)
-        executor.runAllReady()
-    }
-
-    @Test
-    fun testNotListeningByDefault() {
-        verify(packageManager, never()).addOnPermissionsChangeListener(any())
-    }
-
-    @Test
-    fun testGetCorrectFlags() {
-        `when`(packageManager.getPermissionFlags(anyString(), anyString(), any())).thenReturn(0)
-        `when`(packageManager.getPermissionFlags(
-                TEST_PERMISSION,
-                TEST_PACKAGE,
-                UserHandle.getUserHandleForUid(TEST_UID1))
-        ).thenReturn(1)
-
-        assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
-        assertNotEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2))
-    }
-
-    @Test
-    fun testFlagIsCached() {
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
-
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
-
-        verify(packageManager, times(1)).getPermissionFlags(
-                TEST_PERMISSION,
-                TEST_PACKAGE,
-                UserHandle.getUserHandleForUid(TEST_UID1)
-        )
-    }
-
-    @Test
-    fun testListeningAfterFirstRequest() {
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
-
-        verify(packageManager).addOnPermissionsChangeListener(any())
-    }
-
-    @Test
-    fun testListeningOnlyOnce() {
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
-
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2)
-
-        verify(packageManager, times(1)).addOnPermissionsChangeListener(any())
-    }
-
-    @Test
-    fun testUpdateFlag() {
-        assertEquals(0, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
-
-        `when`(packageManager.getPermissionFlags(
-                TEST_PERMISSION,
-                TEST_PACKAGE,
-                UserHandle.getUserHandleForUid(TEST_UID1))
-        ).thenReturn(1)
-
-        flagsCache.onPermissionsChanged(TEST_UID1)
-
-        executor.runAllReady()
-
-        assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
-    }
-
-    @Test
-    fun testUpdateFlag_notUpdatedIfUidHasNotBeenRequestedBefore() {
-        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
-
-        flagsCache.onPermissionsChanged(TEST_UID2)
-
-        executor.runAllReady()
-
-        verify(packageManager, never()).getPermissionFlags(
-                TEST_PERMISSION,
-                TEST_PACKAGE,
-                UserHandle.getUserHandleForUid(TEST_UID2)
-        )
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 02ba304..d395075 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -16,14 +16,14 @@
 
 package com.android.systemui.biometrics
 
+import android.graphics.PointF
 import android.hardware.biometrics.BiometricSourceType
 import android.testing.AndroidTestingRunner
-import android.view.View
-import android.view.ViewGroup
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.ConfigurationController
 import org.junit.Before
@@ -32,6 +32,8 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
@@ -41,38 +43,50 @@
 @RunWith(AndroidTestingRunner::class)
 class AuthRippleControllerTest : SysuiTestCase() {
     private lateinit var controller: AuthRippleController
+    @Mock private lateinit var rippleView: AuthRippleView
     @Mock private lateinit var commandRegistry: CommandRegistry
     @Mock private lateinit var configurationController: ConfigurationController
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
-    @Mock private lateinit var rippleView: AuthRippleView
-    @Mock private lateinit var viewHost: ViewGroup
+    @Mock private lateinit var authController: AuthController
+    @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
 
     @Before
-
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         controller = AuthRippleController(
-            commandRegistry, configurationController, context, keyguardUpdateMonitor)
-        controller.rippleView = rippleView // Replace the real ripple view with a mock instance
-        controller.setViewHost(viewHost)
+            context,
+            authController,
+            configurationController,
+            keyguardUpdateMonitor,
+            commandRegistry,
+            notificationShadeWindowController,
+            rippleView
+        )
+        controller.init()
     }
 
     @Test
-    fun testAddRippleView() {
-        val listenerCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
-        verify(viewHost).addOnAttachStateChangeListener(listenerCaptor.capture())
+    fun testFingerprintTriggerRipple() {
+        val fpsLocation = PointF(5f, 5f)
+        `when`(authController.udfpsSensorLocation).thenReturn(fpsLocation)
+        controller.onViewAttached()
 
-        // Fake attach to window
-        listenerCaptor.value.onViewAttachedToWindow(viewHost)
-        verify(viewHost).addView(rippleView)
+        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+
+        captor.value.onBiometricAuthenticated(
+            0 /* userId */,
+            BiometricSourceType.FINGERPRINT /* type */,
+            false /* isStrongBiometric */)
+        verify(rippleView).setSensorLocation(fpsLocation)
+        verify(rippleView).startRipple(any())
     }
 
     @Test
-    fun testTriggerRipple() {
-        // Fake attach to window
-        val listenerCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
-        verify(viewHost).addOnAttachStateChangeListener(listenerCaptor.capture())
-        listenerCaptor.value.onViewAttachedToWindow(viewHost)
+    fun testFaceTriggerRipple() {
+        val faceLocation = PointF(5f, 5f)
+        `when`(authController.faceAuthSensorLocation).thenReturn(faceLocation)
+        controller.onViewAttached()
 
         val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
         verify(keyguardUpdateMonitor).registerCallback(captor.capture())
@@ -81,17 +95,43 @@
             0 /* userId */,
             BiometricSourceType.FACE /* type */,
             false /* isStrongBiometric */)
-        verify(rippleView, never()).startRipple()
+        verify(rippleView).setSensorLocation(faceLocation)
+        verify(rippleView).startRipple(any())
+    }
+
+    @Test
+    fun testNullFaceSensorLocationDoesNothing() {
+        `when`(authController.faceAuthSensorLocation).thenReturn(null)
+        controller.onViewAttached()
+
+        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+
+        captor.value.onBiometricAuthenticated(
+            0 /* userId */,
+            BiometricSourceType.FACE /* type */,
+            false /* isStrongBiometric */)
+        verify(rippleView, never()).startRipple(any())
+    }
+
+    @Test
+    fun testNullFingerprintSensorLocationDoesNothing() {
+        `when`(authController.udfpsSensorLocation).thenReturn(null)
+        controller.onViewAttached()
+
+        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
 
         captor.value.onBiometricAuthenticated(
             0 /* userId */,
             BiometricSourceType.FINGERPRINT /* type */,
             false /* isStrongBiometric */)
-        verify(rippleView).startRipple()
+        verify(rippleView, never()).startRipple(any())
     }
 
     @Test
     fun testUpdateRippleColor() {
+        controller.onViewAttached()
         val captor = ArgumentCaptor
             .forClass(ConfigurationController.ConfigurationListener::class.java)
         verify(configurationController).addCallback(captor.capture())
@@ -104,10 +144,4 @@
         captor.value.onUiModeChanged()
         verify(rippleView).setColor(ArgumentMatchers.anyInt())
     }
-
-    @Test
-    fun testForwardsSensorLocation() {
-        controller.setSensorLocation(5f, 5f)
-        verify(rippleView).setSensorLocation(5f, 5f)
-    }
 }
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 9504970..bbd3ce8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -48,6 +48,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -100,13 +101,13 @@
     @Mock
     private DumpManager mDumpManager;
     @Mock
-    private AuthRippleController mAuthRippleController;
-    @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
     private KeyguardViewMediator mKeyguardViewMediator;
     @Mock
     private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback;
+    @Mock
+    private FalsingManager mFalsingManager;
 
     private FakeExecutor mFgExecutor;
 
@@ -157,9 +158,9 @@
                 mStatusBar,
                 mStatusBarKeyguardViewManager,
                 mDumpManager,
-                mAuthRippleController,
                 mKeyguardUpdateMonitor,
-                mKeyguardViewMediator);
+                mKeyguardViewMediator,
+                mFalsingManager);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 923cae8..a7f9fe4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.classifier;
 
+import static com.android.systemui.plugins.FalsingManager.HIGH_PENALTY;
+import static com.android.systemui.plugins.FalsingManager.NO_PENALTY;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -55,8 +58,6 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class BrightLineClassifierTest extends SysuiTestCase {
-    private static final long DOUBLE_TAP_TIMEOUT_MS = 1000;
-
     private BrightLineFalsingManager mBrightLineFalsingManager;
     @Mock
     private FalsingDataProvider mFalsingDataProvider;
@@ -91,7 +92,7 @@
                 .thenReturn(mPassedResult);
         when(mClassifierB.classifyGesture(anyInt(), anyDouble(), anyDouble()))
                 .thenReturn(mPassedResult);
-        when(mSingleTapClassfier.isTap(any(List.class))).thenReturn(mPassedResult);
+        when(mSingleTapClassfier.isTap(any(List.class), anyDouble())).thenReturn(mPassedResult);
         when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
                 .thenReturn(mPassedResult);
         mClassifiers.add(mClassifierA);
@@ -170,13 +171,13 @@
 
     @Test
     public void testIsFalseTap_BasicCheck() {
-        when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult);
+        when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mFalsedResult);
 
-        assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isTrue();
+        assertThat(mBrightLineFalsingManager.isSimpleTap()).isFalse();
 
-        when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
+        when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
 
-        assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse();
+        assertThat(mBrightLineFalsingManager.isSimpleTap()).isTrue();
     }
 
     @Test
@@ -185,26 +186,26 @@
         when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(new ArrayList<>());
         when(mFalsingDataProvider.getPriorMotionEvents()).thenReturn(mMotionEventList);
 
-        mBrightLineFalsingManager.isFalseTap(false, 0);
-        verify(mSingleTapClassfier).isTap(mMotionEventList);
+        mBrightLineFalsingManager.isFalseTap(0);
+        verify(mSingleTapClassfier).isTap(mMotionEventList, 0);
     }
 
 
     @Test
     public void testIsFalseTap_RobustCheck_NoFaceAuth() {
-        when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
+        when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
         when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
                 .thenReturn(mFalsedResult);
         when(mHistoryTracker.falseBelief()).thenReturn(1.0);
         mFalsingDataProvider.setJustUnlockedWithFace(false);
-        assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isTrue();
+        assertThat(mBrightLineFalsingManager.isFalseTap(NO_PENALTY)).isTrue();
     }
 
     @Test
     public void testIsFalseTap_RobustCheck_FaceAuth() {
-        when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
+        when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
         when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isFalse();
+        assertThat(mBrightLineFalsingManager.isFalseTap(NO_PENALTY)).isFalse();
     }
 
     @Test
@@ -230,7 +231,7 @@
     @Test
     public void testHistory_singleTap() {
         // When trying to classify single taps, we don't immediately add results to history.
-        mBrightLineFalsingManager.isFalseTap(false, 0);
+        mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
         mGestureFinalizedListener.onGestureFinalized(1000);
         verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
     }
@@ -238,9 +239,9 @@
     @Test
     public void testHistory_multipleSingleTaps() {
         // When trying to classify single taps, we don't immediately add results to history.
-        mBrightLineFalsingManager.isFalseTap(false, 0);
+        mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
         mGestureFinalizedListener.onGestureFinalized(1000);
-        mBrightLineFalsingManager.isFalseTap(false, 0);
+        mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
         mGestureFinalizedListener.onGestureFinalized(2000);
         verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
         verify(mHistoryTracker).addResults(anyCollection(), eq(2000L));
@@ -249,10 +250,10 @@
     @Test
     public void testHistory_doubleTap() {
         // When trying to classify single taps, we don't immediately add results to history.
-        mBrightLineFalsingManager.isFalseTap(false, 0);
+        mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
         mGestureFinalizedListener.onGestureFinalized(1000);
         // Before checking for double tap, we may check for single-tap on the second gesture.
-        mBrightLineFalsingManager.isFalseTap(false, 0);
+        mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
         mBrightLineFalsingManager.isFalseDoubleTap();
         mGestureFinalizedListener.onGestureFinalized(2000);
 
@@ -270,8 +271,8 @@
                 .thenReturn(mFalsedResult);
         assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse();
 
-        when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult);
-        assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse();
+        when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mFalsedResult);
+        assertThat(mBrightLineFalsingManager.isSimpleTap()).isFalse();
 
         when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
                 .thenReturn(mFalsedResult);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
index f726cbe..2ceee6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyDouble;
 import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.Mockito.when;
 
@@ -78,7 +79,7 @@
 
     @Test
     public void testSingleTap() {
-        when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult);
+        when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mFalsedResult);
         addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
         addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP, 1);
 
@@ -88,7 +89,7 @@
 
     @Test
     public void testDoubleTap() {
-        when(mSingleTapClassifier.isTap(anyList())).thenReturn(mPassedResult);
+        when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mPassedResult);
 
         addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
         addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -104,7 +105,8 @@
 
     @Test
     public void testBadFirstTap() {
-        when(mSingleTapClassifier.isTap(anyList())).thenReturn(mPassedResult, mFalsedResult);
+        when(mSingleTapClassifier.isTap(anyList(), anyDouble()))
+                .thenReturn(mPassedResult, mFalsedResult);
 
         addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
         addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -120,7 +122,8 @@
 
     @Test
     public void testBadSecondTap() {
-        when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult, mPassedResult);
+        when(mSingleTapClassifier.isTap(anyList(), anyDouble()))
+                .thenReturn(mFalsedResult, mPassedResult);
 
         addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
         addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -136,7 +139,7 @@
 
     @Test
     public void testBadTouchSlop() {
-        when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult);
+        when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mFalsedResult);
 
         addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
         addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -152,7 +155,7 @@
 
     @Test
     public void testBadTouchSlow() {
-        when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult);
+        when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mFalsedResult);
 
         addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
         addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
index 1524107..e3c800e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
@@ -142,12 +142,12 @@
         addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
         addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
 
-        assertThat(mClassifier.isTap(mMotionEvents).isFalse()).isFalse();
+        assertThat(mClassifier.isTap(mMotionEvents, 0.5).isFalse()).isFalse();
 
         addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
         addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP + 1);
 
-        assertThat(mClassifier.isTap(mMotionEvents).isFalse()).isTrue();
+        assertThat(mClassifier.isTap(mMotionEvents, 0.5).isFalse()).isTrue();
 
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index 8db0f33..107ac83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -162,6 +162,9 @@
         assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
         // Shows person icon.
         assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
+        // No messages count.
+        assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
+
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
@@ -220,6 +223,8 @@
         // Has person icon.
         assertEquals(View.VISIBLE,
                 smallResult.findViewById(R.id.person_icon).getVisibility());
+        // No messages count.
+        assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
@@ -281,6 +286,8 @@
         // Has person icon.
         assertEquals(View.VISIBLE,
                 smallResult.findViewById(R.id.person_icon).getVisibility());
+        // No messages count.
+        assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
@@ -342,6 +349,8 @@
         // Has person icon.
         assertEquals(View.VISIBLE,
                 smallResult.findViewById(R.id.person_icon).getVisibility());
+        // No messages count.
+        assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
@@ -404,6 +413,8 @@
                 smallResult.findViewById(R.id.predefined_icon).getVisibility());
         // Has person icon.
         assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
+        // No messages count.
+        assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
@@ -471,7 +482,7 @@
                 smallResult.findViewById(R.id.person_icon).getVisibility());
 
         // Has a single message, no count shown.
-        assertEquals(View.GONE, result.findViewById(R.id.messages_count).getVisibility());
+        assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
@@ -497,7 +508,7 @@
         assertThat(statusContent.getMaxLines()).isEqualTo(3);
 
         // Has a single message, no count shown.
-        assertEquals(View.GONE, result.findViewById(R.id.messages_count).getVisibility());
+        assertEquals(View.GONE, largeResult.findViewById(R.id.messages_count).getVisibility());
 
     }
 
@@ -544,7 +555,7 @@
                 smallResult.findViewById(R.id.person_icon).getVisibility());
 
         // Has two messages, show count.
-        assertEquals(View.VISIBLE, result.findViewById(R.id.messages_count).getVisibility());
+        assertEquals(View.VISIBLE, smallResult.findViewById(R.id.messages_count).getVisibility());
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
@@ -570,7 +581,7 @@
         assertThat(statusContent.getMaxLines()).isEqualTo(3);
 
         // Has two messages, show count.
-        assertEquals(View.VISIBLE, result.findViewById(R.id.messages_count).getVisibility());
+        assertEquals(View.VISIBLE, largeResult.findViewById(R.id.messages_count).getVisibility());
     }
 
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/plugins/animation/ActivityLaunchAnimatorTest.kt
new file mode 100644
index 0000000..722b0b1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/animation/ActivityLaunchAnimatorTest.kt
@@ -0,0 +1,188 @@
+package com.android.systemui.plugins.animation
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import android.graphics.Point
+import android.graphics.Rect
+import android.os.Looper
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.IRemoteAnimationFinishedCallback
+import android.view.RemoteAnimationAdapter
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertNotNull
+import junit.framework.Assert.assertNull
+import junit.framework.Assert.assertTrue
+import junit.framework.AssertionFailedError
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import kotlin.concurrent.thread
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class ActivityLaunchAnimatorTest : SysuiTestCase() {
+    private val activityLaunchAnimator = ActivityLaunchAnimator()
+    private val rootView = View(mContext)
+    @Spy private val controller = TestLaunchAnimatorController(rootView)
+    @Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
+
+    @get:Rule val rule = MockitoJUnit.rule()
+
+    private fun startIntentWithAnimation(
+        controller: ActivityLaunchAnimator.Controller? = this.controller,
+        intentStarter: (RemoteAnimationAdapter?) -> Int
+    ) {
+        // We start in a new thread so that we can ensure that the callbacks are called in the main
+        // thread.
+        thread {
+            activityLaunchAnimator.startIntentWithAnimation(controller, intentStarter)
+        }.join()
+    }
+
+    @Test
+    fun animationAdapterIsNullIfControllerIsNull() {
+        var startedIntent = false
+        var animationAdapter: RemoteAnimationAdapter? = null
+
+        startIntentWithAnimation(controller = null) { adapter ->
+            startedIntent = true
+            animationAdapter = adapter
+
+            ActivityManager.START_SUCCESS
+        }
+
+        assertTrue(startedIntent)
+        assertNull(animationAdapter)
+    }
+
+    @Test
+    fun animatesIfActivityOpens() {
+        val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+        var animationAdapter: RemoteAnimationAdapter? = null
+        startIntentWithAnimation { adapter ->
+            animationAdapter = adapter
+            ActivityManager.START_SUCCESS
+        }
+
+        assertNotNull(animationAdapter)
+        waitForIdleSync()
+        verify(controller).onIntentStarted(willAnimateCaptor.capture())
+        assertTrue(willAnimateCaptor.value)
+    }
+
+    @Test
+    fun doesNotAnimateIfActivityIsAlreadyOpen() {
+        val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+        startIntentWithAnimation { ActivityManager.START_DELIVERED_TO_TOP }
+
+        waitForIdleSync()
+        verify(controller).onIntentStarted(willAnimateCaptor.capture())
+        assertFalse(willAnimateCaptor.value)
+    }
+
+    @Test
+    fun doesNotStartIfAnimationIsCancelled() {
+        val runner = ActivityLaunchAnimator.Runner(controller)
+        runner.onAnimationCancelled()
+        runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
+
+        waitForIdleSync()
+        verify(controller).onLaunchAnimationCancelled()
+        verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+    }
+
+    @Test
+    fun abortsIfNoOpeningWindowIsFound() {
+        val runner = ActivityLaunchAnimator.Runner(controller)
+        runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
+
+        waitForIdleSync()
+        verify(controller).onLaunchAnimationAborted()
+        verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+    }
+
+    @Test
+    fun startsAnimationIfWindowIsOpening() {
+        val runner = ActivityLaunchAnimator.Runner(controller)
+        runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback)
+        waitForIdleSync()
+        verify(controller).onLaunchAnimationStart(anyBoolean())
+    }
+
+    private fun fakeWindow() = RemoteAnimationTarget(
+            0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
+            Point(), Rect(), Rect(), WindowConfiguration(), false, SurfaceControl(), Rect(),
+            ActivityManager.RunningTaskInfo()
+    )
+}
+
+/**
+ * A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
+ * outside of the main thread.
+ */
+private class TestLaunchAnimatorController(
+    private val rootView: View
+) : ActivityLaunchAnimator.Controller {
+    override fun getRootView(): View = rootView
+
+    override fun createAnimatorState() = ActivityLaunchAnimator.State(
+            top = 100,
+            bottom = 200,
+            left = 300,
+            right = 400,
+            topCornerRadius = 10f,
+            bottomCornerRadius = 20f
+    )
+
+    private fun assertOnMainThread() {
+        if (Looper.myLooper() != Looper.getMainLooper()) {
+            throw AssertionFailedError("Called outside of main thread.")
+        }
+    }
+
+    override fun onIntentStarted(willAnimate: Boolean) {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationProgress(
+        state: ActivityLaunchAnimator.State,
+        progress: Float,
+        linearProgress: Float
+    ) {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationCancelled() {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationTimedOut() {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationAborted() {
+        assertOnMainThread()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index 650ee50..21fec91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -38,6 +38,7 @@
 import com.android.internal.logging.testing.FakeMetricsLogger;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.globalactions.GlobalActionsDialogLite;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
@@ -121,7 +122,7 @@
         mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
                 mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
                 new QSDetailDisplayer(), mQuickQSPanelController, mFakeTunerService,
-                mMetricsLogger, false, mGlobalActionsDialog);
+                mMetricsLogger, new FalsingManagerFake(), false, mGlobalActionsDialog);
 
         mController.init();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index f2f9656b..c35f8b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -34,6 +34,7 @@
 import static org.mockito.Mockito.when;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -46,6 +47,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.FeatureFlagUtils;
+import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
@@ -418,7 +420,7 @@
         public void secondaryClick() {}
 
         @Override
-        public void longClick() {}
+        public void longClick(@Nullable View view) {}
 
         @Override
         public void userSwitch(int currentUser) {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 937ab1c..2d6ed7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -150,12 +150,12 @@
 
     @Test
     public void testClick_falsing() {
-        mFalsingManager.setFalseRobustTap(true);
+        mFalsingManager.setFalseTap(true);
         mTile.click();
         mTestableLooper.processAllMessages();
         assertThat(mTile.mClicked).isFalse();
 
-        mFalsingManager.setFalseRobustTap(false);
+        mFalsingManager.setFalseTap(false);
         mTile.click();
         mTestableLooper.processAllMessages();
         assertThat(mTile.mClicked).isTrue();
@@ -189,7 +189,7 @@
 
     @Test
     public void testLongClick_Metrics() {
-        mTile.longClick();
+        mTile.longClick(null /* view */);
         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
         assertEquals(1, mUiEventLoggerFake.numLogs());
         UiEventLoggerFake.FakeUiEvent event = mUiEventLoggerFake.get(0);
@@ -201,7 +201,7 @@
     public void testLongClick_log() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
 
-        mTile.longClick();
+        mTile.longClick(null /* view */);
         verify(mQsLogger).logTileLongClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index f57283f..613f879 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -30,6 +30,8 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
@@ -117,10 +119,13 @@
     @Mock
     private FeatureFlags mFeatureFlags;
     @Captor
+    ArgumentCaptor<Intent> mIntentCaptor;
+    @Captor
     ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor;
     @Captor
     ArgumentCaptor<QuickAccessWalletClient.OnWalletCardsRetrievedCallback> mCallbackCaptor;
 
+    private Context mSpiedContext;
     private TestableLooper mTestableLooper;
     private QuickAccessWalletTile mTile;
 
@@ -129,8 +134,10 @@
         MockitoAnnotations.initMocks(this);
 
         mTestableLooper = TestableLooper.get(this);
+        mSpiedContext = spy(mContext);
 
-        when(mHost.getContext()).thenReturn(mContext);
+        doNothing().when(mSpiedContext).startActivity(any(Intent.class));
+        when(mHost.getContext()).thenReturn(mSpiedContext);
         when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger);
         when(mFeatureFlags.isQuickAccessWalletEnabled()).thenReturn(true);
         when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
@@ -179,16 +186,46 @@
     }
 
     @Test
-    public void testHandleClick_openGPay() {
+    public void testHandleClick_noCards_hasIntent_openWalletApp() {
         Intent intent = new Intent("WalletIntent");
         when(mQuickAccessWalletClient.createWalletIntent()).thenReturn(intent);
+        setUpWalletCard(/* hasCard= */ false);
+
         mTile.handleClick();
+        mTestableLooper.processAllMessages();
 
         verify(mActivityStarter, times(1))
                 .postStartActivityDismissingKeyguard(eq(intent), anyInt());
     }
 
     @Test
+    public void testHandleClick_noCards_noIntent_doNothing() {
+        when(mQuickAccessWalletClient.createWalletIntent()).thenReturn(null);
+        setUpWalletCard(/* hasCard= */ false);
+
+        mTile.handleClick();
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mActivityStarter);
+    }
+
+    @Test
+    public void testHandleClick_hasCards_startWalletActivity() {
+        setUpWalletCard(/* hasCard= */ true);
+
+        mTile.handleClick();
+        mTestableLooper.processAllMessages();
+
+        verify(mSpiedContext).startActivity(mIntentCaptor.capture());
+
+        Intent nextStartedIntent = mIntentCaptor.getValue();
+        String walletClassName = "com.android.systemui.wallet.ui.WalletActivity";
+
+        assertNotNull(nextStartedIntent);
+        assertThat(nextStartedIntent.getComponent().getClassName()).isEqualTo(walletClassName);
+    }
+
+    @Test
     public void testHandleUpdateState_updateLabelAndIcon() {
         QSTile.State state = new QSTile.State();
         QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_wallet);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index eaef43d..35c92b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -108,6 +108,7 @@
         when(mController.isRemoteInputActive(mEntry)).thenReturn(true);
         mRemoteInputManager.onPerformRemoveNotification(mEntry, mEntry.getKey());
 
+        assertFalse(mEntry.mRemoteEditImeVisible);
         verify(mController).removeRemoteInput(mEntry, null);
     }
 
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 e65db5e..45a7d0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -174,6 +174,13 @@
     }
 
     @Test
+    fun setQsPanelExpansion_appliesBlur() {
+        notificationShadeDepthController.qsPanelExpansion = 1f
+        notificationShadeDepthController.updateBlurCallback.doFrame(0)
+        verify(blurUtils).applyBlur(any(), eq(maxBlur))
+    }
+
+    @Test
     fun updateGlobalDialogVisibility_animatesBlur() {
         notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
         verify(globalActionsSpring).animateTo(eq(maxBlur / 2), eq(root))
@@ -215,7 +222,7 @@
     fun updateBlurCallback_appLaunchAnimation_overridesZoom() {
         `when`(shadeSpring.radius).thenReturn(maxBlur)
         `when`(shadeAnimation.radius).thenReturn(maxBlur)
-        val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
+        val animProgress = ExpandAnimationParameters()
         animProgress.linearProgress = 1f
         notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
@@ -264,7 +271,7 @@
 
     @Test
     fun setNotificationLaunchAnimationParams_schedulesFrame() {
-        val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
+        val animProgress = ExpandAnimationParameters()
         animProgress.linearProgress = 0.5f
         notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
         verify(choreographer).postFrameCallback(
@@ -273,7 +280,7 @@
 
     @Test
     fun setNotificationLaunchAnimationParams_whennNull_ignoresIfShadeHasNoBlur() {
-        val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
+        val animProgress = ExpandAnimationParameters()
         animProgress.linearProgress = 0.5f
         `when`(shadeSpring.radius).thenReturn(0)
         `when`(shadeAnimation.radius).thenReturn(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
index 9ce7241..5e783a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
@@ -16,17 +16,17 @@
 
 package com.android.systemui.statusbar.charging
 
+import android.content.Context
 import android.testing.AndroidTestingRunner
 import android.view.View
-import android.view.ViewGroupOverlay
-import android.view.ViewRootImpl
+import android.view.WindowManager
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.capture
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -34,7 +34,8 @@
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
-import org.mockito.Mockito.never
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -47,55 +48,45 @@
     @Mock private lateinit var batteryController: BatteryController
     @Mock private lateinit var featureFlags: FeatureFlags
     @Mock private lateinit var configurationController: ConfigurationController
-    @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var rippleView: ChargingRippleView
-    @Mock private lateinit var viewHost: View
-    @Mock private lateinit var viewHostRootImpl: ViewRootImpl
-    @Mock private lateinit var viewGroupOverlay: ViewGroupOverlay
+    @Mock private lateinit var windowManager: WindowManager
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        `when`(viewHost.viewRootImpl).thenReturn(viewHostRootImpl)
-        `when`(viewHostRootImpl.view).thenReturn(viewHost)
-        `when`(viewHost.overlay).thenReturn(viewGroupOverlay)
         `when`(featureFlags.isChargingRippleEnabled).thenReturn(true)
-        `when`(keyguardStateController.isShowing).thenReturn(true)
         controller = WiredChargingRippleController(
                 commandRegistry, batteryController, configurationController,
-                featureFlags, context, keyguardStateController)
+                featureFlags, context)
         controller.rippleView = rippleView // Replace the real ripple view with a mock instance
-        controller.setViewHost(viewHost)
+        context.addMockSystemService(Context.WINDOW_SERVICE, windowManager)
     }
 
     @Test
-    fun testSetRippleViewAsOverlay() {
-        val listenerCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
-        verify(viewHost).addOnAttachStateChangeListener(listenerCaptor.capture())
-
-        // Fake attach to window
-        listenerCaptor.value.onViewAttachedToWindow(viewHost)
-        verify(viewGroupOverlay).add(rippleView)
-    }
-
-    @Test
-    fun testTriggerRipple() {
+    fun testTriggerRipple_UnlockedState() {
         val captor = ArgumentCaptor
                 .forClass(BatteryController.BatteryStateChangeCallback::class.java)
         verify(batteryController).addCallback(captor.capture())
 
-        val unusedBatteryLevel = 0
+        // Verify ripple added to window manager.
         captor.value.onBatteryLevelChanged(
-                unusedBatteryLevel,
-                false /* plugged in */,
-                false /* charging */)
-        verify(rippleView, never()).startRipple()
-
-        captor.value.onBatteryLevelChanged(
-                unusedBatteryLevel,
+                0 /* unusedBatteryLevel */,
                 false /* plugged in */,
                 true /* charging */)
-        verify(rippleView).startRipple()
+        val attachListenerCaptor =
+                ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+        verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture())
+        verify(windowManager).addView(eq(rippleView), any<WindowManager.LayoutParams>())
+
+        // Verify ripple started
+        val runnableCaptor =
+                ArgumentCaptor.forClass(Runnable::class.java)
+        attachListenerCaptor.value.onViewAttachedToWindow(rippleView)
+        verify(rippleView).startRipple(runnableCaptor.capture())
+
+        // Verify ripple removed
+        runnableCaptor.value.run()
+        verify(windowManager).removeView(rippleView)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
deleted file mode 100644
index 2fa6cf0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.RemoteAnimationAdapter;
-import android.view.View;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class ActivityLaunchAnimatorTest extends SysuiTestCase {
-
-    private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
-    private ActivityLaunchAnimator mLaunchAnimator;
-    @Mock
-    private ActivityLaunchAnimator.Callback mCallback;
-    @Mock
-    private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
-    @Mock
-    private NotificationShadeWindowView mNotificationShadeWindowView;
-    @Mock
-    private NotificationListContainer mNotificationContainer;
-    @Mock
-    private ExpandableNotificationRow mRow;
-    @Mock
-    private NotificationShadeDepthController mNotificationShadeDepthController;
-    @Mock
-    private NotificationPanelViewController mNotificationPanelViewController;
-    @Rule
-    public MockitoRule rule = MockitoJUnit.rule();
-
-    @Before
-    public void setUp() throws Exception {
-        when(mNotificationShadeWindowViewController.getView())
-                .thenReturn(mNotificationShadeWindowView);
-        when(mNotificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
-        when(mCallback.areLaunchAnimationsEnabled()).thenReturn(true);
-        mLaunchAnimator = new ActivityLaunchAnimator(
-                mNotificationShadeWindowViewController,
-                mCallback,
-                mNotificationPanelViewController,
-                mNotificationShadeDepthController,
-                mNotificationContainer,
-                mExecutor);
-    }
-
-    @Test
-    public void testReturnsNullIfNotEnabled() {
-        when(mCallback.areLaunchAnimationsEnabled()).thenReturn(false);
-        RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
-                false /* occluded */);
-        Assert.assertTrue("The LaunchAnimator generated an animation even though animations are "
-                        + "disabled", launchAnimation == null);
-    }
-
-    @Test
-    public void testNotWorkingWhenOccluded() {
-        when(mCallback.areLaunchAnimationsEnabled()).thenReturn(false);
-        RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
-                true /* occluded */);
-        Assert.assertTrue("The LaunchAnimator generated an animation even though we're occluded",
-                launchAnimation == null);
-    }
-
-    @Test
-    public void testTimeoutCalled() {
-        RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
-                false /* occluded */);
-        Assert.assertTrue("No animation generated", launchAnimation != null);
-        executePostsImmediately(mNotificationShadeWindowView);
-        mLaunchAnimator.setLaunchResult(ActivityManager.START_SUCCESS,
-                true /* wasIntentActivity */);
-        verify(mCallback).onExpandAnimationTimedOut();
-    }
-
-    private void executePostsImmediately(View view) {
-        doAnswer((i) -> {
-            Runnable run = i.getArgument(0);
-            run.run();
-            return null;
-        }).when(view).post(any());
-        doAnswer((i) -> {
-            Runnable run = i.getArgument(0);
-            run.run();
-            return null;
-        }).when(view).postDelayed(any(), anyLong());
-    }
-}
-
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 6a5e6e8..04ac154 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
@@ -82,6 +82,7 @@
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
@@ -219,6 +220,8 @@
     @Mock
     private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
     @Mock
+    private NotificationShadeDepthController mNotificationShadeDepthController;
+    @Mock
     private AuthController mAuthController;
     @Mock
     private ScrimController mScrimController;
@@ -333,6 +336,7 @@
                 mScrimController,
                 mUserManager,
                 mMediaDataManager,
+                mNotificationShadeDepthController,
                 mAmbientState,
                 mFeatureFlags);
         mNotificationPanelViewController.initDependencies(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
index fcea17c..4b8eec4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
@@ -119,7 +119,7 @@
 
     @Test
     public void testSetForcePluginOpen_beforeStatusBarInitialization() {
-        mNotificationShadeWindowController.setForcePluginOpen(true);
+        mNotificationShadeWindowController.setForcePluginOpen(true, this);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
index 4ed2746..b70c6dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
@@ -63,7 +63,8 @@
         when(mResources.getDimension(R.dimen.double_tap_slop))
                 .thenReturn((float) ViewConfiguration.get(mContext).getScaledTouchSlop() - 1);
 
-        mFalsingManager.setFalseRobustTap(true);  // Test double tapping most of the time.
+        mFalsingManager.setSimpleTap(true);
+        mFalsingManager.setFalseTap(true);  // Test double tapping most of the time.
 
         mNotificationTapHelper = new NotificationTapHelper.Factory(mFalsingManager, mFakeExecutor)
                 .create(mActivationListener, mDoubleTapListener, mSlideBackListener);
@@ -158,7 +159,7 @@
                                                1,
                                                0);
 
-        mFalsingManager.setFalseTap(true);
+        mFalsingManager.setSimpleTap(false);
         mNotificationTapHelper.onTouchEvent(evDownA);
         mNotificationTapHelper.onTouchEvent(evUpA);
         verify(mActivationListener, never()).onActiveChanged(true);
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 116e1b9..123e4ef 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
@@ -49,7 +49,6 @@
 import com.android.systemui.DejankUtils;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dock.DockManager;
-import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -79,6 +78,7 @@
 
     private ScrimController mScrimController;
     private ScrimView mScrimBehind;
+    private ScrimView mNotificationsScrim;
     private ScrimView mScrimInFront;
     private ScrimView mScrimForBubble;
     private ScrimState mScrimState;
@@ -104,8 +104,6 @@
     @Mock
     private DockManager mDockManager;
     @Mock
-    private BlurUtils mBlurUtils;
-    @Mock
     private ConfigurationController mConfigurationController;
     @Mock
     private FeatureFlags mFeatureFlags;
@@ -163,6 +161,7 @@
         mScrimController.onPreDraw();
         // Force finish all animations.
         mLooper.processAllMessages();
+        endAnimation(mNotificationsScrim);
         endAnimation(mScrimBehind);
         endAnimation(mScrimInFront);
         endAnimation(mScrimForBubble);
@@ -189,6 +188,7 @@
         mScrimBehind = spy(new ScrimView(getContext()));
         mScrimInFront = new ScrimView(getContext());
         mScrimForBubble = new ScrimView(getContext());
+        mNotificationsScrim = new ScrimView(getContext());
         mAlwaysOnEnabled = true;
         mLooper = TestableLooper.get(this);
         DejankUtils.setImmediate(true);
@@ -201,7 +201,6 @@
 
         when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
         when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
-        when(mBlurUtils.supportsBlursOnWindows()).thenReturn(true);
 
         doAnswer((Answer<Void>) invocation -> {
             mScrimState = invocation.getArgument(0);
@@ -222,10 +221,11 @@
         mScrimController = new ScrimController(mLightBarController,
                 mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
                 new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
-                mDockManager, mBlurUtils, mConfigurationController, mFeatureFlags,
+                mDockManager, mConfigurationController, mFeatureFlags,
                 new FakeExecutor(new FakeSystemClock()));
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
-        mScrimController.attachViews(mScrimBehind, mScrimInFront, mScrimForBubble);
+        mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront,
+                mScrimForBubble);
         mScrimController.setAnimatorListener(mAnimatorListener);
 
         mScrimController.setHasBackdrop(false);
@@ -264,7 +264,7 @@
                 TRANSPARENT /* bubble */);
 
         assertScrimTint(false /* front */,
-                false /* behind */,
+                true /* behind */,
                 false /* bubble */);
     }
 
@@ -516,7 +516,7 @@
                 TRANSPARENT /* bubble */);
 
         assertScrimTint(false /* front */,
-                false /* behind */,
+                true /* behind */,
                 false /* bubble */);
 
         // Back scrim should be visible after start dragging
@@ -586,7 +586,7 @@
     @Test
     public void qsExpansion() {
         reset(mScrimBehind);
-        mScrimController.setQsExpansion(1f);
+        mScrimController.setQsPosition(1f, 999 /* value doesn't matter */);
         finishAnimationsImmediately();
 
         assertScrimAlpha(TRANSPARENT, OPAQUE, TRANSPARENT);
@@ -636,7 +636,7 @@
 
         // Make sure at the very end of the animation, we're reset to transparent
         assertScrimTint(false /* front */,
-                false /* behind */,
+                true /* behind */,
                 false  /* bubble */);
     }
 
@@ -947,7 +947,9 @@
         if (scrim == mScrimInFront) {
             return "front";
         } else if (scrim == mScrimBehind) {
-            return "back";
+            return "behind";
+        } else if (scrim == mNotificationsScrim) {
+            return "notifications";
         } else if (scrim == mScrimForBubble) {
             return "bubble";
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 68464ce..e34bc0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -35,7 +35,6 @@
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -54,6 +53,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FeatureFlags;
@@ -63,9 +63,9 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
@@ -136,6 +136,8 @@
     private OnUserInteractionCallback mOnUserInteractionCallback;
     @Mock
     private NotificationActivityStarter mNotificationActivityStarter;
+    @Mock
+    private ActivityLaunchAnimator mActivityLaunchAnimator;
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
 
     private NotificationTestHelper mNotificationTestHelper;
@@ -213,11 +215,14 @@
                         mock(MetricsLogger.class),
                         mock(StatusBarNotificationActivityStarterLogger.class),
                         mOnUserInteractionCallback)
-                .setStatusBar(mStatusBar)
-                .setNotificationPresenter(mock(NotificationPresenter.class))
-                .setNotificationPanelViewController(mock(NotificationPanelViewController.class))
-                .setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class))
-                .build();
+                        .setStatusBar(mStatusBar)
+                        .setNotificationPresenter(mock(NotificationPresenter.class))
+                        .setNotificationPanelViewController(
+                                mock(NotificationPanelViewController.class))
+                        .setActivityLaunchAnimator(mActivityLaunchAnimator)
+                        .setNotificationAnimatorControllerProvider(
+                                mock(NotificationLaunchAnimatorControllerProvider.class))
+                        .build();
 
         // set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
         doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute(
@@ -254,14 +259,7 @@
         // Then
         verify(mShadeController, atLeastOnce()).collapsePanel();
 
-        verify(mContentIntent).sendAndReturnResult(
-                any(Context.class),
-                anyInt() /* code */,
-                any() /* fillInIntent */,
-                any() /* PendingIntent.OnFinished */,
-                any() /* Handler */,
-                any() /* requiredPermission */,
-                any() /* Bundle options */);
+        verify(mActivityLaunchAnimator).startPendingIntentWithAnimation(eq(null), any());
 
         verify(mAssistManager).hideAssist();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index c0ebfad..8601de5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -47,7 +47,6 @@
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -125,7 +124,7 @@
                 mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
                 notificationShadeWindowView, stackScrollLayoutController,
                 mock(DozeScrimController.class), mock(ScrimController.class),
-                mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
+                mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class),
                 mock(KeyguardStateController.class),
                 mock(KeyguardIndicationController.class), mStatusBar,
                 mock(ShadeControllerImpl.class), mCommandQueue, mInitController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 5de62b9..3d07eb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -703,6 +703,10 @@
     // ============================= Smart Action tests ============================================
     // =============================================================================================
 
+    private View anyView() {
+        return any();
+    }
+
     @Test
     public void testTapSmartAction_waitsForKeyguard() throws InterruptedException {
         setSmartActions(TEST_ACTION_TITLES);
@@ -710,7 +714,7 @@
         mView.getChildAt(2).performClick();
 
         verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
-                any());
+                anyView());
     }
 
     @Test
@@ -721,7 +725,8 @@
 
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any(), any());
+        verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any(),
+                anyView());
     }
 
     @Test
@@ -734,7 +739,7 @@
         mView.getChildAt(2).performClick();
 
         verify(mActivityStarter, times(1))
-                .startPendingIntentDismissingKeyguard(any(), any(), any());
+                .startPendingIntentDismissingKeyguard(any(), any(), anyView());
     }
 
     @Test
@@ -746,7 +751,7 @@
         mView.getChildAt(2).performClick();
 
         verify(mActivityStarter, times(1))
-                .startPendingIntentDismissingKeyguard(any(), any(), any());
+                .startPendingIntentDismissingKeyguard(any(), any(), anyView());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index f85962b..653946e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -51,6 +51,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import com.google.common.util.concurrent.MoreExecutors;
 
@@ -85,6 +86,8 @@
     ActivityStarter mActivityStarter;
     @Mock
     UserTracker mUserTracker;
+    @Mock
+    KeyguardStateController mKeyguardStateController;
     @Captor
     ArgumentCaptor<Intent> mIntentCaptor;
     @Captor
@@ -106,6 +109,7 @@
         when(mWalletClient.getShortcutShortLabel()).thenReturn(SHORTCUT_SHORT_LABEL);
         when(mWalletClient.getServiceLabel()).thenReturn(SERVICE_LABEL);
         when(mWalletClient.createWalletIntent()).thenReturn(mWalletIntent);
+        when(mKeyguardStateController.isUnlocked()).thenReturn(true);
         mController = new WalletScreenController(
                 mContext,
                 mWalletView,
@@ -114,7 +118,7 @@
                 MoreExecutors.directExecutor(),
                 new Handler(mTestableLooper.getLooper()),
                 mUserTracker,
-                /* isDeviceLocked= */false);
+                mKeyguardStateController);
     }
 
     @Test
@@ -206,6 +210,14 @@
     }
 
     @Test
+    public void onKeyguardFadingAwayChanged_queryCards() {
+        mController.onKeyguardFadingAwayChanged();
+
+        verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+        assertEquals(mController, mCallbackCaptor.getValue());
+    }
+
+    @Test
     public void onCardSelected() {
         mController.onCardSelected(createCardViewInfo());
 
diff --git a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
index ea3e650..5f9aaae 100644
--- a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
+++ b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
@@ -158,24 +158,29 @@
         final Calendar cal = Calendar.getInstance();
         cal.add(Calendar.HOUR, SEND_NOTIFICATION_DELAY_HOURS);
         mAlarmManager.set(RTC_WAKEUP, cal.getTimeInMillis(),
-                createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION,
-                        service.flattenToShortString()));
+                createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION, service));
     }
 
     private void cancelAlarm(int userId, ComponentName service) {
         mAlarmManager.cancel(
-                createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION,
-                        service.flattenToShortString()));
+                createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION, service));
     }
 
     protected static PendingIntent createPendingIntent(Context context, int userId, String action,
-            String serviceComponentName) {
+            ComponentName serviceComponentName) {
+        return PendingIntent.getBroadcast(context, 0,
+                createIntent(context, userId, action, serviceComponentName),
+                PendingIntent.FLAG_IMMUTABLE);
+    }
+
+    protected static Intent createIntent(Context context, int userId, String action,
+            ComponentName serviceComponentName) {
         final Intent intent = new Intent(action);
         intent.setPackage(context.getPackageName())
-                .setIdentifier(serviceComponentName)
+                .setIdentifier(serviceComponentName.flattenToShortString())
+                .putExtra(Intent.EXTRA_COMPONENT_NAME, serviceComponentName)
                 .putExtra(Intent.EXTRA_USER_ID, userId);
-        return PendingIntent.getBroadcast(context, 0, intent,
-                PendingIntent.FLAG_IMMUTABLE);
+        return intent;
     }
 
     /** A sub class to handle notifications and settings on the main thread. */
@@ -204,10 +209,9 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
-            final String service = intent.getIdentifier();
-            final ComponentName componentName = ComponentName.unflattenFromString(service);
-            if (TextUtils.isEmpty(action) || TextUtils.isEmpty(service)
-                    || componentName == null) {
+            final ComponentName componentName = intent.getParcelableExtra(
+                    Intent.EXTRA_COMPONENT_NAME);
+            if (TextUtils.isEmpty(action) || componentName == null) {
                 return;
             }
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_SYSTEM);
@@ -215,7 +219,8 @@
                 trySendNotification(userId, componentName);
             } else if (ACTION_A11Y_SETTINGS.equals(action)) {
                 launchSettings(userId, componentName);
-                mNotificationManager.cancel(service, NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
+                mNotificationManager.cancel(componentName.flattenToShortString(),
+                        NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
                 onNotificationCanceled(userId, componentName);
             } else if (ACTION_DISMISS_NOTIFICATION.equals(action)) {
                 onNotificationCanceled(userId, componentName);
@@ -257,8 +262,7 @@
                                 mContext.getPackageManager());
                         final int size = mContext.getResources().getDimensionPixelSize(
                                 android.R.dimen.app_icon_size);
-                        sendNotification(userId, componentName.flattenToShortString(),
-                                displayName,
+                        sendNotification(userId, componentName, displayName,
                                 ImageUtils.buildScaledBitmap(drawable, size, size));
                     }
                     break;
@@ -289,7 +293,8 @@
             }
         }
 
-        private void sendNotification(int userId, String serviceComponentName, CharSequence name,
+        private void sendNotification(int userId, ComponentName serviceComponentName,
+                CharSequence name,
                 Bitmap bitmap) {
             final Notification.Builder notificationBuilder = new Notification.Builder(mContext,
                     SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY);
@@ -315,7 +320,8 @@
             if (bitmap != null) {
                 notificationBuilder.setLargeIcon(bitmap);
             }
-            mNotificationManager.notify(serviceComponentName, NOTE_A11Y_VIEW_AND_CONTROL_ACCESS,
+            mNotificationManager.notify(serviceComponentName.flattenToShortString(),
+                    NOTE_A11Y_VIEW_AND_CONTROL_ACCESS,
                     notificationBuilder.build());
         }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 8040851..f9aecd7 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -119,6 +119,11 @@
     }
 
     @Override
+    public void onAccessibilityActionPerformed(int displayId) {
+        updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+    }
+
+    @Override
     public void onTouchInteractionStart(int displayId, int mode) {
         handleUserInteractionChanged(displayId, mode);
     }
@@ -148,8 +153,13 @@
     }
 
     private void updateMagnificationButton(int displayId, int mode) {
-        if (isActivated(displayId, mode) && mMagnificationCapabilities
-                == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
+        final boolean isActivated = isActivated(displayId, mode);
+        final boolean showButton;
+        synchronized (mLock) {
+            showButton = isActivated && mMagnificationCapabilities
+                    == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+        }
+        if (showButton) {
             getWindowMagnificationMgr().showMagnificationButton(displayId, mode);
         } else {
             getWindowMagnificationMgr().removeMagnificationButton(displayId);
@@ -430,13 +440,22 @@
 
     private boolean isActivated(int displayId, int mode) {
         boolean isActivated = false;
-        if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
-                && mFullScreenMagnificationController != null) {
-            isActivated = mFullScreenMagnificationController.isMagnifying(displayId)
-                    || mFullScreenMagnificationController.isForceShowMagnifiableBounds(displayId);
-        } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
-                && mWindowMagnificationMgr != null) {
-            isActivated = mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+        if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
+            synchronized (mLock) {
+                if (mFullScreenMagnificationController == null) {
+                    return false;
+                }
+                isActivated = mFullScreenMagnificationController.isMagnifying(displayId)
+                        || mFullScreenMagnificationController.isForceShowMagnifiableBounds(
+                        displayId);
+            }
+        } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+            synchronized (mLock) {
+                if (mWindowMagnificationMgr == null) {
+                    return false;
+                }
+                isActivated = mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+            }
         }
         return isActivated;
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 86f61ee..938cb73 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -95,6 +95,13 @@
         void onPerformScaleAction(int displayId, float scale);
 
         /**
+         * Called when the accessibility action is performed.
+         *
+         * @param displayId The logical display id.
+         */
+        void onAccessibilityActionPerformed(int displayId);
+
+        /**
          * Called when the state of the magnification activation is changed.
          *
          * @param displayId The logical display id.
@@ -536,9 +543,12 @@
 
         @Override
         public void onPerformScaleAction(int displayId, float scale) {
-            synchronized (mLock) {
-                mCallback.onPerformScaleAction(displayId, scale);
-            }
+            mCallback.onPerformScaleAction(displayId, scale);
+        }
+
+        @Override
+        public void onAccessibilityActionPerformed(int displayId) {
+            mCallback.onAccessibilityActionPerformed(displayId);
         }
 
         @Override
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index e9b2ed3..55490ce 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -154,6 +154,8 @@
     private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000;
     private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000;
 
+    private static final long DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS = 10 * 1000;
+
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "CompanionDeviceManagerService";
 
@@ -1131,6 +1133,14 @@
                 // Service binding is managed manually based on corresponding device being nearby
                 return Long.MAX_VALUE;
             }
+
+            @Override
+            public void binderDied() {
+                super.binderDied();
+
+                // Re-connect to the service if process gets killed
+                mMainHandler.postDelayed(this::connect, DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS);
+            }
         };
     }
 
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 7fc7933..5cbcacf 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -158,7 +158,6 @@
         "android.hardware.power.stats-V1-java",
         "android.hidl.manager-V1.2-java",
         "capture_state_listener-aidl-java",
-        "dnsresolver_aidl_interface-V8-java",
         "icu4j_calendar_astronomer",
         "netd-client",
         "overlayable_policy_aidl-java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 185cdfc..922b21a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3789,6 +3789,10 @@
     private void destroyNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
         try {
             mNetd.networkDestroy(networkAgent.network.getNetId());
+        } catch (RemoteException | ServiceSpecificException e) {
+            loge("Exception destroying network(networkDestroy): " + e);
+        }
+        try {
             mDnsResolver.destroyNetworkCache(networkAgent.network.getNetId());
         } catch (RemoteException | ServiceSpecificException e) {
             loge("Exception destroying network: " + e);
@@ -5683,7 +5687,7 @@
                     + mNetworkRequestForCallback.requestId
                     + " " + mRequests
                     + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent)
-                    + "callback flags: " + mCallbackFlags;
+                    + " callback flags: " + mCallbackFlags;
         }
     }
 
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index c983600..7057b840 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -19,6 +19,7 @@
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
 per-file *Battery* = file:/BATTERY_STATS_OWNERS
+per-file *Binder* = file:/core/java/com/android/internal/os/BINDER_OWNERS
 per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
 per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
 per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index fa3771a..6e7771d 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
-import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -46,6 +45,7 @@
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.security.MessageDigest;
@@ -132,13 +132,13 @@
     private static final String FLASH_LOCK_UNLOCKED = "0";
 
     private final Context mContext;
+    private final String mDataBlockFile;
     private final boolean mIsRunningDSU;
     private final Object mLock = new Object();
     private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
 
     private int mAllowedUid = -1;
     private long mBlockDeviceSize;
-    private String mDataBlockFile;
 
     @GuardedBy("mLock")
     private boolean mIsWritable = true;
@@ -146,8 +146,12 @@
     public PersistentDataBlockService(Context context) {
         super(context);
         mContext = context;
-        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
         mIsRunningDSU = SystemProperties.getBoolean(GSI_RUNNING_PROP, false);
+        if (mIsRunningDSU) {
+            mDataBlockFile = GSI_SANDBOX;
+        } else {
+            mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+        }
         mBlockDeviceSize = -1; // Load lazily
     }
 
@@ -262,7 +266,11 @@
     private long getBlockDeviceSize() {
         synchronized (mLock) {
             if (mBlockDeviceSize == -1) {
-                mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
+                if (mIsRunningDSU) {
+                    mBlockDeviceSize = MAX_DATA_BLOCK_SIZE;
+                } else {
+                    mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
+                }
             }
         }
 
@@ -292,40 +300,30 @@
         return true;
     }
 
-    private FileOutputStream getBlockOutputStream() throws IOException {
-        if (!mIsRunningDSU) {
-            return new FileOutputStream(new File(mDataBlockFile));
-        } else {
-            File sandbox = new File(GSI_SANDBOX);
-            File realpdb = new File(SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP));
-            if (!sandbox.exists()) {
-                FileUtils.copy(realpdb, sandbox);
-                mDataBlockFile = GSI_SANDBOX;
-            }
-            Slog.i(TAG, "PersistentDataBlock copy-on-write");
-            return new FileOutputStream(sandbox);
-        }
+    private FileChannel getBlockOutputChannel() throws IOException {
+        return new RandomAccessFile(mDataBlockFile, "rw").getChannel();
     }
 
     private boolean computeAndWriteDigestLocked() {
         byte[] digest = computeDigestLocked(null);
         if (digest != null) {
-            DataOutputStream outputStream;
+            FileChannel channel;
             try {
-                outputStream = new DataOutputStream(getBlockOutputStream());
+                channel = getBlockOutputChannel();
             } catch (IOException e) {
                 Slog.e(TAG, "partition not available?", e);
                 return false;
             }
 
             try {
-                outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
-                outputStream.flush();
+                ByteBuffer buf = ByteBuffer.allocate(DIGEST_SIZE_BYTES);
+                buf.put(digest);
+                buf.flip();
+                channel.write(buf);
+                channel.force(true);
             } catch (IOException e) {
                 Slog.e(TAG, "failed to write block checksum", e);
                 return false;
-            } finally {
-                IoUtils.closeQuietly(outputStream);
             }
             return true;
         } else {
@@ -376,25 +374,18 @@
     }
 
     private void formatPartitionLocked(boolean setOemUnlockEnabled) {
-        DataOutputStream outputStream;
-        try {
-            outputStream = new DataOutputStream(getBlockOutputStream());
-        } catch (IOException e) {
-            Slog.e(TAG, "partition not available?", e);
-            return;
-        }
 
-        byte[] data = new byte[DIGEST_SIZE_BYTES];
         try {
-            outputStream.write(data, 0, DIGEST_SIZE_BYTES);
-            outputStream.writeInt(PARTITION_TYPE_MARKER);
-            outputStream.writeInt(0); // data size
-            outputStream.flush();
+            FileChannel channel = getBlockOutputChannel();
+            ByteBuffer buf = ByteBuffer.allocate(DIGEST_SIZE_BYTES + HEADER_SIZE);
+            buf.put(new byte[DIGEST_SIZE_BYTES]);
+            buf.putInt(PARTITION_TYPE_MARKER);
+            buf.putInt(0);
+            channel.write(buf);
+            channel.force(true);
         } catch (IOException e) {
             Slog.e(TAG, "failed to format block", e);
             return;
-        } finally {
-            IoUtils.closeQuietly(outputStream);
         }
 
         doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
@@ -402,16 +393,9 @@
     }
 
     private void doSetOemUnlockEnabledLocked(boolean enabled) {
-        FileOutputStream outputStream;
-        try {
-            outputStream = getBlockOutputStream();
-        } catch (IOException e) {
-            Slog.e(TAG, "partition not available", e);
-            return;
-        }
 
         try {
-            FileChannel channel = outputStream.getChannel();
+            FileChannel channel = getBlockOutputChannel();
 
             channel.position(getBlockDeviceSize() - 1);
 
@@ -419,13 +403,12 @@
             data.put(enabled ? (byte) 1 : (byte) 0);
             data.flip();
             channel.write(data);
-            outputStream.flush();
+            channel.force(true);
         } catch (IOException e) {
             Slog.e(TAG, "unable to access persistent partition", e);
             return;
         } finally {
             SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
-            IoUtils.closeQuietly(outputStream);
         }
     }
 
@@ -479,35 +462,32 @@
                 return (int) -maxBlockSize;
             }
 
-            DataOutputStream outputStream;
+            FileChannel channel;
             try {
-                outputStream = new DataOutputStream(getBlockOutputStream());
+                channel = getBlockOutputChannel();
             } catch (IOException e) {
                 Slog.e(TAG, "partition not available?", e);
-                return -1;
+               return -1;
             }
 
-            ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
+            ByteBuffer headerAndData = ByteBuffer.allocate(
+                                           data.length + HEADER_SIZE + DIGEST_SIZE_BYTES);
+            headerAndData.put(new byte[DIGEST_SIZE_BYTES]);
             headerAndData.putInt(PARTITION_TYPE_MARKER);
             headerAndData.putInt(data.length);
             headerAndData.put(data);
-
+            headerAndData.flip();
             synchronized (mLock) {
                 if (!mIsWritable) {
-                    IoUtils.closeQuietly(outputStream);
                     return -1;
                 }
 
                 try {
-                    byte[] checksum = new byte[DIGEST_SIZE_BYTES];
-                    outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
-                    outputStream.write(headerAndData.array());
-                    outputStream.flush();
+                    channel.write(headerAndData);
+                    channel.force(true);
                 } catch (IOException e) {
                     Slog.e(TAG, "failed writing to the persistent data block", e);
                     return -1;
-                } finally {
-                    IoUtils.closeQuietly(outputStream);
                 }
 
                 if (computeAndWriteDigestLocked()) {
@@ -567,17 +547,6 @@
         public void wipe() {
             enforceOemUnlockWritePermission();
 
-            if (mIsRunningDSU) {
-                File sandbox = new File(GSI_SANDBOX);
-                if (sandbox.exists()) {
-                    if (sandbox.delete()) {
-                        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
-                    } else {
-                        Slog.e(TAG, "Failed to wipe sandbox persistent data block");
-                    }
-                }
-                return;
-            }
             synchronized (mLock) {
                 int ret = nativeWipe(mDataBlockFile);
 
@@ -735,28 +704,18 @@
         }
 
         private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
-            FileOutputStream outputStream;
-            try {
-                outputStream = getBlockOutputStream();
-            } catch (IOException e) {
-                Slog.e(TAG, "partition not available", e);
-                return;
-            }
             synchronized (mLock) {
                 if (!mIsWritable) {
-                    IoUtils.closeQuietly(outputStream);
                     return;
                 }
                 try {
-                    FileChannel channel = outputStream.getChannel();
+                    FileChannel channel = getBlockOutputChannel();
                     channel.position(offset);
                     channel.write(dataBuffer);
-                    outputStream.flush();
+                    channel.force(true);
                 } catch (IOException e) {
                     Slog.e(TAG, "unable to access persistent partition", e);
                     return;
-                } finally {
-                    IoUtils.closeQuietly(outputStream);
                 }
 
                 computeAndWriteDigestLocked();
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index b485fe8..611fe7a 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -29,6 +29,8 @@
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -155,7 +157,6 @@
         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
         int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-        int targetSdk;
 
         boolean matchTelephonyCallbackEvent(int event) {
             return (callback != null) && (this.eventList.contains(event));
@@ -228,6 +229,54 @@
                     TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION, packageName,
                     userHandle));
         }
+
+        /**
+         * To check the SDK version for
+         * {@link android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener} should add
+         * {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+         * @noinspection ConstantConditions
+         */
+        public boolean isActiveDataSubIdReadPhoneStateEnforcedInPlatformCompat(String packageName,
+                UserHandle userHandle) {
+            return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
+                    REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_ACTIVE_DATA_SUB_ID, packageName,
+                    userHandle));
+        }
+
+        /**
+         * To check the SDK version for
+         * {@link android.telephony.TelephonyCallback.CellInfoListener} should add
+         * {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+         * @noinspection ConstantConditions
+         */
+        public boolean isCellInfoReadPhoneStateEnforcedInPlatformCompat(String packageName,
+                UserHandle userHandle) {
+            return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
+                    REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_CELL_INFO, packageName, userHandle));
+        }
+
+        /**
+         * To check the SDK version for
+         * {@link android.telephony.TelephonyCallback.DisplayInfoListener} should remove
+         * {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+         * @noinspection ConstantConditions
+         */
+        public boolean isDisplayInfoReadPhoneStateEnforcedInPlatformCompat(String packageName,
+                UserHandle userHandle) {
+            return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
+                    REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_DISPLAY_INFO, packageName, userHandle));
+        }
+
+        /**
+         * Support backward compatibility for {@link android.telephony.TelephonyDisplayInfo}.
+         *
+         * @noinspection ConstantConditions
+         */
+        public boolean isDisplayInfoNrAdvancedSupported(String packageName,
+                UserHandle userHandle) {
+            return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
+                    DISPLAY_INFO_NR_ADVANCED_SUPPORTED, packageName, userHandle));
+        }
     }
 
     private final Context mContext;
@@ -346,6 +395,39 @@
      */
     private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>>
             mPreciseDataConnectionStates;
+    /**
+     * Support backward compatibility for {@link android.telephony.TelephonyDisplayInfo}.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long DISPLAY_INFO_NR_ADVANCED_SUPPORTED = 181658987L;
+
+    /**
+     * To check the SDK version for
+     * {@link android.telephony.TelephonyCallback.DisplayInfoListener} should remove
+     * {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_DISPLAY_INFO = 183164979L;
+
+    /**
+     * To check the SDK version for
+     * {@link android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener} should add
+     * {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_ACTIVE_DATA_SUB_ID
+            = 182478738L;
+
+    /**
+     * To check the SDK version for {@link android.telephony.TelephonyCallback.CellInfoListener}
+     * should add {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_CELL_INFO = 184323934L;
 
     private static final Set<Integer> REQUIRE_PRECISE_PHONE_STATE_PERMISSION;
     static {
@@ -379,13 +461,46 @@
                 || events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
     }
 
-    private boolean isPhoneStatePermissionRequired(Set<Integer> events, int targetSdk) {
-        return events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
+    private boolean isPhoneStatePermissionRequired(Set<Integer> events, String callingPackage,
+            UserHandle userHandle) {
+        if (events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
                 || events.contains(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
-                || events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
-                || events.contains(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)
-                || (targetSdk <= android.os.Build.VERSION_CODES.R ? events.contains(
-                TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED) : false);
+                || events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) {
+            return true;
+        }
+
+        // Only check READ_PHONE_STATE for CALL_STATE_CHANGED for Android 12 or above.
+        if ((events.contains(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED))
+                && mConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(
+                        callingPackage, userHandle)) {
+            return true;
+        }
+
+        // Only check READ_PHONE_STATE for ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED for Android 12
+        // or above.
+        if (events.contains(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)
+                && mConfigurationProvider.isActiveDataSubIdReadPhoneStateEnforcedInPlatformCompat(
+                        callingPackage, userHandle)) {
+            return true;
+        }
+
+        // Only check READ_PHONE_STATE for CELL_INFO_CHANGED for Android 12 or above.
+        if (events.contains(TelephonyCallback.EVENT_CELL_INFO_CHANGED)
+                && mConfigurationProvider.isCellInfoReadPhoneStateEnforcedInPlatformCompat(
+                        callingPackage, userHandle)) {
+            return true;
+        }
+
+        // Only check READ_PHONE_STATE for DISPLAY_INFO_CHANGED for Android 11 or older.
+        // READ_PHONE_STATE is not required anymore after Android 12.
+        if (events.contains(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)
+                && !mConfigurationProvider.isDisplayInfoReadPhoneStateEnforcedInPlatformCompat(
+                        callingPackage, userHandle)) {
+            return true;
+        }
+
+        return false;
     }
 
     private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) {
@@ -902,12 +1017,11 @@
             remove(callback.asBinder());
             return;
         }
-        int callerTargetSdk = TelephonyPermissions.getTargetSdk(mContext, callingPackage);
+
         // Checks permission and throws SecurityException for disallowed operations. For pre-M
         // apps whose runtime permission has been revoked, we return immediately to skip sending
         // events to the app without crashing it.
-        if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId,
-                "listen", callerTargetSdk)) {
+        if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId, "listen")) {
             return;
         }
 
@@ -940,7 +1054,6 @@
             }
             r.phoneId = phoneId;
             r.eventList = events;
-            r.targetSdk = callerTargetSdk;
 
             if (DBG) {
                 log("listen:  Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
@@ -1773,7 +1886,8 @@
                             TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)
                             && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) {
                         try {
-                            if (r.targetSdk <= android.os.Build.VERSION_CODES.R) {
+                            if (!mConfigurationProvider.isDisplayInfoNrAdvancedSupported(
+                                    r.callingPackage, Binder.getCallingUserHandle())) {
                                 telephonyDisplayInfo =
                                         getBackwardCompatibleTelephonyDisplayInfo(
                                                 telephonyDisplayInfo);
@@ -2922,7 +3036,7 @@
     }
 
     private boolean checkListenerPermission(Set<Integer> events, int subId, String callingPackage,
-            @Nullable String callingFeatureId, String message, int targetSdk) {
+            @Nullable String callingFeatureId, String message) {
         LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
                 new LocationAccessPolicy.LocationPermissionQuery.Builder()
                         .setCallingPackage(callingPackage)
@@ -2958,26 +3072,13 @@
             }
         }
 
-        if (isPhoneStatePermissionRequired(events, targetSdk)) {
+        if (isPhoneStatePermissionRequired(events, callingPackage, Binder.getCallingUserHandle())) {
             if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
                     mContext, subId, callingPackage, callingFeatureId, message)) {
                 isPermissionCheckSuccessful = false;
             }
         }
 
-        // Only check READ_PHONE_STATE for CALL_STATE_CHANGED for API 31+.
-        if (mConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(callingPackage,
-                Binder.getCallingUserHandle())) {
-            if (events.contains(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)
-                    || events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) {
-                if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                        mContext, subId, callingPackage, callingFeatureId, message)) {
-                    throw new SecurityException("CALL_STATE_CHANGED event requires "
-                            + "READ_PHONE_STATE");
-                }
-            }
-        }
-
         if (isPrecisePhoneStatePermissionRequired(events)) {
             // check if calling app has either permission READ_PRECISE_PHONE_STATE
             // or with carrier privileges
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c1ab6cc..c7f2f43 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -78,6 +78,7 @@
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.ForegroundServiceDidNotStartInTimeException;
 import android.app.ForegroundServiceStartNotAllowedException;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
@@ -4988,9 +4989,10 @@
     }
 
     void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) {
-        mAm.crashApplication(app.uid, app.getPid(), app.info.packageName, app.userId,
+        mAm.crashApplicationWithType(app.uid, app.getPid(), app.info.packageName, app.userId,
                 "Context.startForegroundService() did not then call Service.startForeground(): "
-                    + serviceRecord, false /*force*/);
+                    + serviceRecord, false /*force*/,
+                ForegroundServiceDidNotStartInTimeException.TYPE_ID);
     }
 
     void scheduleServiceTimeoutLocked(ProcessRecord proc) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7dc39b3..23dda78 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -179,6 +179,7 @@
 import android.app.ProcessMemoryState;
 import android.app.ProfilerInfo;
 import android.app.PropertyInvalidatedCache;
+import android.app.RemoteServiceException;
 import android.app.SyncNotedAppOp;
 import android.app.WaitResult;
 import android.app.backup.BackupManager.OperationType;
@@ -233,6 +234,7 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerInternal;
 import android.media.audiofx.AudioEffect;
+import android.net.ConnectivityManager;
 import android.net.Proxy;
 import android.net.Uri;
 import android.os.AppZygote;
@@ -2935,6 +2937,13 @@
     @Override
     public void crashApplication(int uid, int initialPid, String packageName, int userId,
             String message, boolean force) {
+        crashApplicationWithType(uid, initialPid, packageName, userId, message, force,
+                RemoteServiceException.TYPE_ID);
+    }
+
+    @Override
+    public void crashApplicationWithType(int uid, int initialPid, String packageName, int userId,
+            String message, boolean force, int exceptionTypeId) {
         if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: crashApplication() from pid="
@@ -2947,7 +2956,7 @@
 
         synchronized(this) {
             mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, userId,
-                    message, force);
+                    message, force, exceptionTypeId);
         }
     }
 
@@ -3481,9 +3490,9 @@
 
                     // Clear its scheduled jobs
                     JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
-                    // Clearing data is akin to uninstalling. The app is force stopped before we
-                    // get to this point, so the reason won't be checked by the app.
-                    js.cancelJobsForUid(appInfo.uid, JobParameters.STOP_REASON_USER, "clear data");
+                    // Clearing data is a user-initiated action.
+                    js.cancelJobsForUid(appInfo.uid, JobParameters.STOP_REASON_USER,
+                            JobParameters.DEBUG_REASON_DATA_CLEARED, "clear data");
 
                     // Clear its pending alarms
                     AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class);
@@ -12936,7 +12945,7 @@
                     }
                     mBatteryStatsService.noteCurrentTimeChanged();
                     break;
-                case Intent.ACTION_CLEAR_DNS_CACHE:
+                case ConnectivityManager.ACTION_CLEAR_DNS_CACHE:
                     mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
                     break;
                 case Proxy.PROXY_CHANGE_ACTION:
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 3602f44..5a59eabd 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -486,7 +486,7 @@
      * @param message
      */
     void scheduleAppCrashLocked(int uid, int initialPid, String packageName, int userId,
-            String message, boolean force) {
+            String message, boolean force, int exceptionTypeId) {
         ProcessRecord proc = null;
 
         // Figure out which process to kill.  We don't trust that initialPid
@@ -518,7 +518,7 @@
             return;
         }
 
-        proc.scheduleCrashLocked(message);
+        proc.scheduleCrashLocked(message, exceptionTypeId);
         if (force) {
             // If the app is responsive, the scheduled crash will happen as expected
             // and then the delayed summary kill will be a no-op.
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index c8721cc..960cc42 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1328,6 +1328,9 @@
                         || uidRec.getSetProcState() == PROCESS_STATE_NONEXISTENT) {
                     uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
                 }
+                if (uidRec.getSetCapability() != uidRec.getCurCapability()) {
+                    uidChange |= UidRecord.CHANGE_CAPABILITY;
+                }
                 uidRec.setSetProcState(uidRec.getCurProcState());
                 uidRec.setSetCapability(uidRec.getCurCapability());
                 uidRec.setSetAllowListed(uidRec.isCurAllowListed());
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index ed136af..9e94d4a 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -26,6 +26,7 @@
 import android.app.ApplicationExitInfo.Reason;
 import android.app.ApplicationExitInfo.SubReason;
 import android.app.IApplicationThread;
+import android.app.RemoteServiceException;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProcessInfo;
 import android.content.pm.VersionedPackage;
@@ -949,6 +950,21 @@
 
     @GuardedBy("mService")
     void scheduleCrashLocked(String message) {
+        scheduleCrashLocked(message, RemoteServiceException.TYPE_ID);
+    }
+
+    /**
+     * Let an app process throw an exception on a binder thread, which typically crashes the
+     * process, unless it has an unhandled exception handler.
+     *
+     * See {@link ActivityThread#throwRemoteServiceException}.
+     *
+     * @param message exception message
+     * @param exceptionTypeId ID defined in {@link android.app.RemoteServiceException} or one
+     *                        of its subclasses.
+     */
+    @GuardedBy("mService")
+    void scheduleCrashLocked(String message, int exceptionTypeId) {
         // Checking killedbyAm should keep it from showing the crash dialog if the process
         // was already dead for a good / normal reason.
         if (!mKilledByAm) {
@@ -959,7 +975,7 @@
                 }
                 final long ident = Binder.clearCallingIdentity();
                 try {
-                    mThread.scheduleCrash(message);
+                    mThread.scheduleCrash(message, exceptionTypeId);
                 } catch (RemoteException e) {
                     // If it's already dead our work is done. If it's wedged just kill it.
                     // We won't get the crash dialog or the error reporting.
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index df65ee6..c1bfe25 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -147,6 +147,9 @@
         if ((currentChange & UidRecord.CHANGE_GONE) != 0) {
             currentChange &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
         }
+        if ((pendingChange & UidRecord.CHANGE_CAPABILITY) != 0) {
+            currentChange |= UidRecord.CHANGE_CAPABILITY;
+        }
         return currentChange;
     }
 
@@ -285,12 +288,9 @@
                         reg.mLastProcStates.delete(item.uid);
                     }
                 } else {
+                    boolean doReport = false;
                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
-                        if (DEBUG_UID_OBSERVERS) {
-                            Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
-                                    + ": " + item.procState + ": " + item.capability);
-                        }
-                        boolean doReport = true;
+                        doReport = true;
                         if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) {
                             final int lastState = reg.mLastProcStates.get(item.uid,
                                     ActivityManager.PROCESS_STATE_UNKNOWN);
@@ -302,13 +302,20 @@
                                 doReport = item.procState != PROCESS_STATE_NONEXISTENT;
                             }
                         }
-                        if (doReport) {
-                            if (reg.mLastProcStates != null) {
-                                reg.mLastProcStates.put(item.uid, item.procState);
-                            }
-                            observer.onUidStateChanged(item.uid, item.procState,
-                                    item.procStateSeq, item.capability);
+                    }
+                    if ((reg.mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
+                        doReport |= (change & UidRecord.CHANGE_CAPABILITY) != 0;
+                    }
+                    if (doReport) {
+                        if (DEBUG_UID_OBSERVERS) {
+                            Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
+                                    + ": " + item.procState + ": " + item.capability);
                         }
+                        if (reg.mLastProcStates != null) {
+                            reg.mLastProcStates.put(item.uid, item.procState);
+                        }
+                        observer.onUidStateChanged(item.uid, item.procState,
+                                item.procStateSeq, item.capability);
                     }
                 }
                 final int duration = (int) (SystemClock.uptimeMillis() - start);
@@ -428,12 +435,14 @@
                 ActivityManager.UID_OBSERVER_ACTIVE,
                 ActivityManager.UID_OBSERVER_GONE,
                 ActivityManager.UID_OBSERVER_PROCSTATE,
+                ActivityManager.UID_OBSERVER_CAPABILITY,
         };
         private static final int[] PROTO_ENUMS = new int[]{
                 ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
                 ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE,
                 ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
                 ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
+                ActivityManagerProto.UID_OBSERVER_FLAG_CAPABILITY,
         };
 
         UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint) {
@@ -462,6 +471,9 @@
             if ((mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
                 pw.print(" GONE");
             }
+            if ((mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
+                pw.print(" CAP");
+            }
             if ((mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
                 pw.print(" STATE");
                 pw.print(" (cut=");
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 2fb662f..4ba59fa 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -122,6 +122,7 @@
     static final int CHANGE_ACTIVE = 1<<2;
     static final int CHANGE_CACHED = 1<<3;
     static final int CHANGE_UNCACHED = 1<<4;
+    static final int CHANGE_CAPABILITY = 1<<5;
 
     // Keep the enum lists in sync
     private static int[] ORIG_ENUMS = new int[] {
@@ -130,6 +131,7 @@
             CHANGE_ACTIVE,
             CHANGE_CACHED,
             CHANGE_UNCACHED,
+            CHANGE_CAPABILITY,
     };
     private static int[] PROTO_ENUMS = new int[] {
             UidRecordProto.CHANGE_GONE,
@@ -137,6 +139,7 @@
             UidRecordProto.CHANGE_ACTIVE,
             UidRecordProto.CHANGE_CACHED,
             UidRecordProto.CHANGE_UNCACHED,
+            UidRecordProto.CHANGE_CAPABILITY,
     };
 
     // UidObserverController is the only thing that should modify this.
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 143a1cf..a5d0e72 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -93,7 +93,6 @@
 import android.util.EventLog;
 import android.util.IntArray;
 import android.util.Pair;
-import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
@@ -110,6 +109,7 @@
 import com.android.server.am.UserState.KeyEvictedCallback;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.UserManagerService;
+import com.android.server.utils.Slogf;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerService;
@@ -510,7 +510,7 @@
                         elapsedTimeMs);
                 final long maxElapsedTimeMs = 120_000;
                 if (elapsedTimeMs > maxElapsedTimeMs) {
-                    Slog.wtf("SystemServerTiming",
+                    Slogf.wtf("SystemServerTiming",
                             "finishUserBoot took too long. elapsedTimeMs=" + elapsedTimeMs);
                 }
             }
@@ -533,12 +533,12 @@
             final UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
             if (parent != null
                     && isUserRunning(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) {
-                Slog.d(TAG, "User " + userId + " (parent " + parent.id
+                Slogf.d(TAG, "User " + userId + " (parent " + parent.id
                         + "): attempting unlock because parent is unlocked");
                 maybeUnlockUser(userId);
             } else {
                 String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id);
-                Slog.d(TAG, "User " + userId + " (parent " + parentId
+                Slogf.d(TAG, "User " + userId + " (parent " + parentId
                         + "): delaying unlock because parent is locked");
             }
         } else {
@@ -587,7 +587,7 @@
         // Call onBeforeUnlockUser on a worker thread that allows disk I/O
         FgThread.getHandler().post(() -> {
             if (!StorageManager.isUserKeyUnlocked(userId)) {
-                Slog.w(TAG, "User key got locked unexpectedly, leaving user locked.");
+                Slogf.w(TAG, "User key got locked unexpectedly, leaving user locked.");
                 return;
             }
             mInjector.getUserManager().onBeforeUnlockUser(userId);
@@ -716,7 +716,7 @@
 
         Runnable initializeUser = () -> mInjector.getUserManager().makeInitialized(userInfo.id);
         if (!userInfo.isInitialized()) {
-            Slog.d(TAG, "Initializing user #" + userId);
+            Slogf.d(TAG, "Initializing user #" + userId);
             if (userInfo.preCreated) {
                 initializeUser.run();
             } else if (userId != UserHandle.USER_SYSTEM) {
@@ -739,7 +739,7 @@
         }
 
         if (userInfo.preCreated) {
-            Slog.i(TAG, "Stopping pre-created user " + userInfo.toFullString());
+            Slogf.i(TAG, "Stopping pre-created user " + userInfo.toFullString());
             // Pre-created user was started right after creation so services could properly
             // intialize it; it should be stopped right away as it's not really a "real" user.
             stopUser(userInfo.id, /* force= */ true, /* allowDelayedLocking= */ false,
@@ -752,7 +752,7 @@
 
         mHandler.obtainMessage(USER_UNLOCKED_MSG, userId, 0).sendToTarget();
 
-        Slog.i(TAG, "Posting BOOT_COMPLETED user #" + userId);
+        Slogf.i(TAG, "Posting BOOT_COMPLETED user #" + userId);
         // Do not report secondary users, runtime restarts or first boot/upgrade
         if (userId == UserHandle.USER_SYSTEM
                 && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
@@ -777,7 +777,7 @@
                         public void performReceive(Intent intent, int resultCode, String data,
                                 Bundle extras, boolean ordered, boolean sticky, int sendingUser)
                                         throws RemoteException {
-                            Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u"
+                            Slogf.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u"
                                     + userId);
                             mBootCompleted = true;
                         }
@@ -863,11 +863,12 @@
         for (int i = 0; i < usersToStop.length; i++) {
             int relatedUserId = usersToStop[i];
             if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLU(relatedUserId)) {
-                if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked cannot stop related user "
-                        + relatedUserId);
+                if (DEBUG_MU) {
+                    Slogf.i(TAG, "stopUsersLocked cannot stop related user " + relatedUserId);
+                }
                 // We still need to stop the requested user if it's a force stop.
                 if (force) {
-                    Slog.i(TAG,
+                    Slogf.i(TAG,
                             "Force stop user " + userId + ". Related users will not be stopped");
                     stopSingleUserLU(userId, allowDelayedLocking, stopUserCallback,
                             keyEvictedCallback);
@@ -876,7 +877,7 @@
                 return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
             }
         }
-        if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
+        if (DEBUG_MU) Slogf.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
         for (int userIdToStop : usersToStop) {
             stopSingleUserLU(userIdToStop, allowDelayedLocking,
                     userIdToStop == userId ? stopUserCallback : null,
@@ -906,7 +907,7 @@
     private void stopSingleUserLU(final int userId, boolean allowDelayedLocking,
             final IStopUserCallback stopUserCallback,
             KeyEvictedCallback keyEvictedCallback) {
-        if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId);
+        if (DEBUG_MU) Slogf.i(TAG, "stopSingleUserLocked userId=" + userId);
         final UserState uss = mStartedUsers.get(userId);
         if (uss == null) {  // User is not started
             // If mDelayUserDataLocking is set and allowDelayedLocking is not set, we need to lock
@@ -917,7 +918,7 @@
             // and no further action is necessary.
             if (mDelayUserDataLocking) {
                 if (allowDelayedLocking && keyEvictedCallback != null) {
-                    Slog.wtf(TAG, "allowDelayedLocking set with KeyEvictedCallback, ignore it"
+                    Slogf.wtf(TAG, "allowDelayedLocking set with KeyEvictedCallback, ignore it"
                             + " and lock user:" + userId, new RuntimeException());
                     allowDelayedLocking = false;
                 }
@@ -1043,7 +1044,7 @@
     void finishUserStopped(UserState uss, boolean allowDelayedLocking) {
         final int userId = uss.mHandle.getIdentifier();
         if (DEBUG_MU) {
-            Slog.i(TAG, "finishUserStopped(%d): allowDelayedLocking=%b", userId,
+            Slogf.i(TAG, "finishUserStopped(%d): allowDelayedLocking=%b", userId,
                     allowDelayedLocking);
         }
 
@@ -1067,7 +1068,7 @@
                 mUserLru.remove(Integer.valueOf(userId));
                 updateStartedUserArrayLU();
                 if (allowDelayedLocking && !keyEvictedCallbacks.isEmpty()) {
-                    Slog.wtf(TAG,
+                    Slogf.wtf(TAG,
                             "Delayed locking enabled while KeyEvictedCallbacks not empty, userId:"
                                     + userId + " callbacks:" + keyEvictedCallbacks);
                     allowDelayedLocking = false;
@@ -1118,7 +1119,7 @@
         FgThread.getHandler().post(() -> {
             synchronized (mLock) {
                 if (mStartedUsers.get(userId) != null) {
-                    Slog.w(TAG, "User was restarted, skipping key eviction");
+                    Slogf.w(TAG, "User was restarted, skipping key eviction");
                     return;
                 }
             }
@@ -1156,10 +1157,10 @@
             if (totalUnlockedUsers > mMaxRunningUsers) { // should lock a user
                 userIdToLock = mLastActiveUsers.get(mLastActiveUsers.size() - 1);
                 mLastActiveUsers.remove(mLastActiveUsers.size() - 1);
-                Slog.i(TAG, "finishUserStopped, stopping user:" + userId
+                Slogf.i(TAG, "finishUserStopped, stopping user:" + userId
                         + " lock user:" + userIdToLock);
             } else {
-                Slog.i(TAG, "finishUserStopped, user:" + userId + ", skip locking");
+                Slogf.i(TAG, "finishUserStopped, user:" + userId + ", skip locking");
                 // do not lock
                 userIdToLock = UserHandle.USER_NULL;
             }
@@ -1196,7 +1197,7 @@
     }
 
     private void forceStopUser(@UserIdInt int userId, String reason) {
-        if (DEBUG_MU) Slog.i(TAG, "forceStopUser(%d): %s", userId, reason);
+        if (DEBUG_MU) Slogf.i(TAG, "forceStopUser(%d): %s", userId, reason);
         mInjector.activityManagerForceStopPackage(userId, reason);
         if (mInjector.getUserManager().isPreCreated(userId)) {
             // Don't fire intent for precreated.
@@ -1227,7 +1228,7 @@
      * Stops the guest or ephemeral user if it has gone to the background.
      */
     private void stopGuestOrEphemeralUserIfBackground(int oldUserId) {
-        if (DEBUG_MU) Slog.i(TAG, "Stop guest or ephemeral user if background: " + oldUserId);
+        if (DEBUG_MU) Slogf.i(TAG, "Stop guest or ephemeral user if background: " + oldUserId);
         synchronized(mLock) {
             UserState oldUss = mStartedUsers.get(oldUserId);
             if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId || oldUss == null
@@ -1265,7 +1266,7 @@
 
     void startProfiles() {
         int currentUserId = getCurrentUserId();
-        if (DEBUG_MU) Slog.i(TAG, "startProfilesLocked");
+        if (DEBUG_MU) Slogf.i(TAG, "startProfilesLocked");
         List<UserInfo> profiles = mInjector.getUserManager().getProfiles(
                 currentUserId, false /* enabledOnly */);
         List<UserInfo> profilesToStart = new ArrayList<>(profiles.size());
@@ -1281,7 +1282,7 @@
             startUser(profilesToStart.get(i).id, /* foreground= */ false);
         }
         if (i < profilesToStartSize) {
-            Slog.w(TAG, "More profiles than MAX_RUNNING_USERS");
+            Slogf.w(TAG, "More profiles than MAX_RUNNING_USERS");
         }
     }
 
@@ -1309,7 +1310,7 @@
         }
 
         if (!userInfo.isEnabled()) {
-            Slog.w(TAG, "Cannot start disabled profile #" + userId);
+            Slogf.w(TAG, "Cannot start disabled profile #" + userId);
             return false;
         }
 
@@ -1375,7 +1376,9 @@
 
     private boolean startUserInternal(@UserIdInt int userId, boolean foreground,
             @Nullable IProgressListener unlockListener, @NonNull TimingsTraceAndSlog t) {
-        if (DEBUG_MU) Slog.i(TAG, "Starting user %d%s", userId, foreground ? " in foreground" : "");
+        if (DEBUG_MU) {
+            Slogf.i(TAG, "Starting user %d%s", userId, foreground ? " in foreground" : "");
+        }
         EventLog.writeEvent(EventLogTags.UC_START_USER_INTERNAL, userId);
 
         final int callingUid = Binder.getCallingUid();
@@ -1387,7 +1390,7 @@
             if (oldUserId == userId) {
                 final UserState state = getStartedUserState(userId);
                 if (state == null) {
-                    Slog.wtf(TAG, "Current user has no UserState");
+                    Slogf.wtf(TAG, "Current user has no UserState");
                     // continue starting.
                 } else {
                     if (userId == UserHandle.USER_SYSTEM && state.state == STATE_BOOTING) {
@@ -1417,16 +1420,16 @@
             t.traceEnd();
 
             if (userInfo == null) {
-                Slog.w(TAG, "No user info for user #" + userId);
+                Slogf.w(TAG, "No user info for user #" + userId);
                 return false;
             }
             if (foreground && userInfo.isProfile()) {
-                Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
+                Slogf.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
                 return false;
             }
 
             if (foreground && userInfo.preCreated) {
-                Slog.w(TAG, "Cannot start pre-created user #" + userId + " as foreground");
+                Slogf.w(TAG, "Cannot start pre-created user #" + userId + " as foreground");
                 return false;
             }
 
@@ -1454,7 +1457,7 @@
                     needStart = true;
                     updateUmState = true;
                 } else if (uss.state == UserState.STATE_SHUTDOWN && !isCallingOnHandlerThread()) {
-                    Slog.i(TAG, "User #" + userId
+                    Slogf.i(TAG, "User #" + userId
                             + " is shutting down - will start after full stop");
                     mHandler.post(() -> startUser(userId, foreground, unlockListener));
                     t.traceEnd(); // updateStartedUserArrayStarting
@@ -1663,7 +1666,7 @@
                 // We always want to unlock user storage, even user is not started yet
                 storageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret);
             } catch (RemoteException | RuntimeException e) {
-                Slog.w(TAG, "Failed to unlock: " + e.getMessage());
+                Slogf.w(TAG, "Failed to unlock: " + e.getMessage());
             }
         }
         synchronized (mLock) {
@@ -1699,7 +1702,7 @@
         for (int testUserId : userIds) {
             final UserInfo parent = mInjector.getUserManager().getProfileParent(testUserId);
             if (parent != null && parent.id == userId && testUserId != userId) {
-                Slog.d(TAG, "User " + testUserId + " (parent " + parent.id
+                Slogf.d(TAG, "User " + testUserId + " (parent " + parent.id
                         + "): attempting unlock because parent was just unlocked");
                 maybeUnlockUser(testUserId);
             }
@@ -1714,25 +1717,25 @@
         int currentUserId = getCurrentUserId();
         UserInfo targetUserInfo = getUserInfo(targetUserId);
         if (targetUserId == currentUserId) {
-            Slog.i(TAG, "user #" + targetUserId + " is already the current user");
+            Slogf.i(TAG, "user #" + targetUserId + " is already the current user");
             return true;
         }
         if (targetUserInfo == null) {
-            Slog.w(TAG, "No user info for user #" + targetUserId);
+            Slogf.w(TAG, "No user info for user #" + targetUserId);
             return false;
         }
         if (!targetUserInfo.supportsSwitchTo()) {
-            Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
+            Slogf.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
             return false;
         }
         if (targetUserInfo.isProfile()) {
-            Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
+            Slogf.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
             return false;
         }
         boolean userSwitchUiEnabled;
         synchronized (mLock) {
             if (!mInitialized) {
-                Slog.e(TAG, "Cannot switch to User #" + targetUserId
+                Slogf.e(TAG, "Cannot switch to User #" + targetUserId
                         + ": UserController not ready yet");
                 return false;
             }
@@ -1809,13 +1812,13 @@
             boolean disallowRunInBg = hasRestriction || shouldStopBackgroundUsersOnSwitch();
             if (!disallowRunInBg) {
                 if (DEBUG_MU) {
-                    Slog.i(TAG, "stopBackgroundUsersIfEnforced() NOT stopping %d and related users",
-                            oldUserId);
+                    Slogf.i(TAG, "stopBackgroundUsersIfEnforced() NOT stopping %d and related "
+                            + "users", oldUserId);
                 }
                 return;
             }
             if (DEBUG_MU) {
-                Slog.i(TAG, "stopBackgroundUsersIfEnforced() stopping %d and related users",
+                Slogf.i(TAG, "stopBackgroundUsersIfEnforced() stopping %d and related users",
                         oldUserId);
             }
             stopUsersLU(oldUserId, /* force= */ false, /* allowDelayedLocking= */ true,
@@ -1825,7 +1828,7 @@
 
     private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
         synchronized (mLock) {
-            Slog.e(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
+            Slogf.e(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
             mTimeoutUserSwitchCallbacks = mCurWaitingUserSwitchCallbacks;
             mHandler.removeMessages(USER_SWITCH_CALLBACKS_TIMEOUT_MSG);
             sendContinueUserSwitchLU(uss, oldUserId, newUserId);
@@ -1838,7 +1841,7 @@
     private void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) {
         synchronized (mLock) {
             if (mTimeoutUserSwitchCallbacks != null && !mTimeoutUserSwitchCallbacks.isEmpty()) {
-                Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId
+                Slogf.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId
                         + ". Observers that didn't respond: " + mTimeoutUserSwitchCallbacks);
                 mTimeoutUserSwitchCallbacks = null;
             }
@@ -1870,10 +1873,10 @@
                             synchronized (mLock) {
                                 long delay = SystemClock.elapsedRealtime() - dispatchStartedTime;
                                 if (delay > USER_SWITCH_TIMEOUT_MS) {
-                                    Slog.e(TAG, "User switch timeout: observer " + name
+                                    Slogf.e(TAG, "User switch timeout: observer " + name
                                             + " sent result after " + delay + " ms");
                                 } else if (delay > USER_SWITCH_WARNING_TIMEOUT_MS) {
-                                    Slog.w(TAG, "User switch slowed down by observer " + name
+                                    Slogf.w(TAG, "User switch slowed down by observer " + name
                                             + ": result sent after " + delay + " ms");
                                 }
 
@@ -2091,7 +2094,7 @@
                         }
                     }
                     String msg = builder.toString();
-                    Slog.w(TAG, msg);
+                    Slogf.w(TAG, msg);
                     throw new SecurityException(msg);
                 }
             }
@@ -2318,7 +2321,7 @@
                     + Binder.getCallingPid()
                     + ", uid=" + Binder.getCallingUid()
                     + " requires " + INTERACT_ACROSS_USERS;
-            Slog.w(TAG, msg);
+            Slogf.w(TAG, msg);
             throw new SecurityException(msg);
         }
     }
@@ -2421,7 +2424,7 @@
                     + "() from pid=" + Binder.getCallingPid()
                     + ", uid=" + Binder.getCallingUid()
                     + " requires " + permission;
-            Slog.w(TAG, msg);
+            Slogf.w(TAG, msg);
             throw new SecurityException(msg);
         }
     }
@@ -2732,7 +2735,7 @@
                      * journey so no need to create a new journey for user start.
                      */
                     if (DEBUG_MU) {
-                        Slog.d(TAG, journey + " not logged as it is expected to be part of "
+                        Slogf.d(TAG, journey + " not logged as it is expected to be part of "
                                 + userJourneySession.mJourney);
                     }
                     return;
@@ -2755,7 +2758,7 @@
             }
 
             if (DEBUG_MU) {
-                Slog.d(TAG,
+                Slogf.d(TAG,
                         "Starting a new journey: " + journey + " with session id: " + newSessionId);
             }
 
@@ -2787,7 +2790,7 @@
         synchronized (mUserIdToUserJourneyMap) {
             final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId);
             if (userJourneySession == null || userJourneySession.mSessionId == INVALID_SESSION_ID) {
-                Slog.w(TAG, "UserLifecycleEvent " + event
+                Slogf.w(TAG, "UserLifecycleEvent " + event
                         + " received without an active userJourneySession.");
                 return;
             }
@@ -2870,13 +2873,13 @@
         private volatile long mUnlockStarted;
         @Override
         public void onStarted(int id, Bundle extras) throws RemoteException {
-            Slog.d(TAG, "Started unlocking user " + id);
+            Slogf.d(TAG, "Started unlocking user " + id);
             mUnlockStarted = SystemClock.uptimeMillis();
         }
 
         @Override
         public void onProgress(int id, int progress, Bundle extras) throws RemoteException {
-            Slog.d(TAG, "Unlocking user " + id + " progress " + progress);
+            Slogf.d(TAG, "Unlocking user " + id + " progress " + progress);
         }
 
         @Override
@@ -3061,7 +3064,7 @@
                 // config_customUserSwitchUi is set to true on Automotive as CarSystemUI is
                 // responsible to show the UI; OEMs should not change that, but if they do, we
                 // should at least warn the user...
-                Slog.w(TAG, "Showing user switch dialog on UserController, it could cause a race "
+                Slogf.w(TAG, "Showing user switch dialog on UserController, it could cause a race "
                         + "condition if it's shown by CarSystemUI as well");
             }
             final Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser,
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 2bc81cb..d25896c6 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -31,6 +31,7 @@
 import android.app.UriGrantsManager;
 import android.content.ClipData;
 import android.content.ClipDescription;
+import android.content.ClipboardManager;
 import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -220,8 +221,6 @@
             SystemProperties.getBoolean("ro.boot.qemu", false);
 
     // DeviceConfig properties
-    private static final String PROPERTY_SHOW_ACCESS_NOTIFICATIONS = "show_access_notifications";
-    private static final boolean DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
     private static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";
     private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;
 
@@ -242,7 +241,8 @@
     private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
 
     @GuardedBy("mLock")
-    private boolean mShowAccessNotifications = DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
+    private boolean mShowAccessNotifications =
+            ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
 
     @GuardedBy("mLock")
     private int mMaxClassificationLength = DEFAULT_MAX_CLASSIFICATION_LENGTH;
@@ -310,8 +310,10 @@
 
     private void updateConfig() {
         synchronized (mLock) {
-            mShowAccessNotifications = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
-                    PROPERTY_SHOW_ACCESS_NOTIFICATIONS, DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
+            mShowAccessNotifications = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_CLIPBOARD,
+                    ClipboardManager.DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS,
+                    ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
             mMaxClassificationLength = DeviceConfig.getInt(DeviceConfig.NAMESPACE_CLIPBOARD,
                     PROPERTY_MAX_CLASSIFICATION_LENGTH, DEFAULT_MAX_CLASSIFICATION_LENGTH);
         }
@@ -1056,11 +1058,9 @@
         if (clipboard.primaryClip == null) {
             return;
         }
-        if (!mShowAccessNotifications) {
-            return;
-        }
         if (Settings.Secure.getInt(getContext().getContentResolver(),
-                Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, 1) == 0) {
+                Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS,
+                (mShowAccessNotifications ? 1 : 0)) == 0) {
             return;
         }
         // Don't notify if the app accessing the clipboard is the same as the current owner.
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index ffeb77d..cf4fe1e 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -420,7 +420,7 @@
         /*
          * Tell the VMs to toss their DNS caches
          */
-        final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
+        final Intent intent = new Intent(ConnectivityManager.ACTION_CLEAR_DNS_CACHE);
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         /*
          * Connectivity events can happen before boot has completed ...
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 1786a51..614e488 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -268,10 +268,6 @@
 
     private final SyncLogger mLogger;
 
-    // NOTE: this is a temporary allow-list for testing purposes; it will be removed before release.
-    private final String[] mEjSyncAllowedPackages = new String[]{
-            "com.google.android.google", "com.android.frameworks.servicestests"};
-
     private boolean isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs) {
         for (JobInfo job: pendingJobs) {
             if (job.getId() == jobId) {
@@ -991,14 +987,6 @@
             }
         }
 
-        final boolean scheduleAsEj =
-                extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false);
-        // NOTE: this is a temporary check for internal testing - to be removed before release.
-        if (scheduleAsEj && !ArrayUtils.contains(mEjSyncAllowedPackages, callingPackage)) {
-            throw new IllegalArgumentException(
-                    callingPackage + " is not allowed to schedule a sync as an EJ yet.");
-        }
-
         for (AccountAndUser account : accounts) {
             // If userId is specified, do not sync accounts of other users
             if (userId >= UserHandle.USER_SYSTEM && account.userId >= UserHandle.USER_SYSTEM
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 10f6948f..919d25c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -258,18 +258,6 @@
         return super.handleUserControlPressed(message);
     }
 
-    @Override
-    protected void wakeUpIfActiveSource() {
-        if (!isActiveSource()) {
-            return;
-        }
-        // Wake up the device if the power is in standby mode, or its screen is off -
-        // which can happen if the device is holding a partial lock.
-        if (mService.isPowerStandbyOrTransient() || !mService.getPowerManager().isScreenOn()) {
-            mService.wakeUp();
-        }
-    }
-
     @ServiceThreadOnly
     @Constants.HandleMessageResult
     protected int handleSetMenuLanguage(HdmiCecMessage message) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 979a1d4..c001c40 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -292,7 +292,6 @@
 
     // Only source devices that react to routing control messages should implement
     // this method (e.g. a TV with built in switch).
-    // TODO(): decide which type will handle the routing when multi device type is supported
     protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
         // do nothing
     }
@@ -372,7 +371,7 @@
         if (!isActiveSource()) {
             return;
         }
-        // Wake up the device
+        // Wake up the device. This will also exit dream mode.
         mService.wakeUp();
         return;
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 03fb3a4..acfeb6c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -713,7 +713,9 @@
         assertRunOnServiceThread();
         if (!mService.isPowerStandbyOrTransient()) {
             addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
-            if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
+            if (!isDirectConnectAddress(avr.getPhysicalAddress())) {
+                startArcAction(false);
+            } else if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
                     && !hasAction(SetArcTransmissionStateAction.class)) {
                 startArcAction(true);
             }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 71a5d1c..6b28fbc 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
 import static android.Manifest.permission.MANAGE_BIOMETRIC;
 import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS;
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.UserHandle.USER_ALL;
@@ -160,6 +161,7 @@
 import java.util.Objects;
 import java.util.Random;
 import java.util.Set;
+import java.util.StringJoiner;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -1074,6 +1076,10 @@
         mContext.enforceCallingOrSelfPermission(BIOMETRIC_PERMISSION, "LockSettingsBiometric");
     }
 
+    private boolean hasPermission(String permission) {
+        return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
+    }
+
     @Override
     public boolean hasSecureLockScreen() {
         return mHasSecureLockScreen;
@@ -1567,42 +1573,52 @@
             throw new UnsupportedOperationException(
                     "This operation requires secure lock screen feature");
         }
-        checkWritePermission(userId);
-        enforceFrpResolved();
+        if (!hasPermission(PERMISSION) && !hasPermission(SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS)) {
+            throw new SecurityException(
+                    "setLockCredential requires SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS or "
+                            + PERMISSION);
+        }
 
-        // When changing credential for profiles with unified challenge, some callers
-        // will pass in empty credential while others will pass in the credential of
-        // the parent user. setLockCredentialInternal() handles the formal case (empty
-        // credential) correctly but not the latter. As a stopgap fix, convert the latter
-        // case to the formal. The long-term fix would be fixing LSS such that it should
-        // accept only the parent user credential on its public API interfaces, swap it
-        // with the profile's random credential at that API boundary (i.e. here) and make
-        // sure LSS internally does not special case profile with unififed challenge: b/80170828.
-        if (!savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) {
-            // Verify the parent credential again, to make sure we have a fresh enough
-            // auth token such that getDecryptedPasswordForTiedProfile() inside
-            // setLockCredentialInternal() can function correctly.
-            verifyCredential(savedCredential, mUserManager.getProfileParent(userId).id,
-                    0 /* flags */);
-            savedCredential.zeroize();
-            savedCredential = LockscreenCredential.createNone();
-        }
-        synchronized (mSeparateChallengeLock) {
-            if (!setLockCredentialInternal(credential, savedCredential,
-                    userId, /* isLockTiedToParent= */ false)) {
-                scheduleGc();
-                return false;
+        long identity = Binder.clearCallingIdentity();
+        try {
+            enforceFrpResolved();
+            // When changing credential for profiles with unified challenge, some callers
+            // will pass in empty credential while others will pass in the credential of
+            // the parent user. setLockCredentialInternal() handles the formal case (empty
+            // credential) correctly but not the latter. As a stopgap fix, convert the latter
+            // case to the formal. The long-term fix would be fixing LSS such that it should
+            // accept only the parent user credential on its public API interfaces, swap it
+            // with the profile's random credential at that API boundary (i.e. here) and make
+            // sure LSS internally does not special case profile with unififed challenge: b/80170828
+            if (!savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) {
+                // Verify the parent credential again, to make sure we have a fresh enough
+                // auth token such that getDecryptedPasswordForTiedProfile() inside
+                // setLockCredentialInternal() can function correctly.
+                verifyCredential(savedCredential, mUserManager.getProfileParent(userId).id,
+                        0 /* flags */);
+                savedCredential.zeroize();
+                savedCredential = LockscreenCredential.createNone();
             }
-            setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
-            notifyPasswordChanged(userId);
+            synchronized (mSeparateChallengeLock) {
+                if (!setLockCredentialInternal(credential, savedCredential,
+                        userId, /* isLockTiedToParent= */ false)) {
+                    scheduleGc();
+                    return false;
+                }
+                setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
+                notifyPasswordChanged(userId);
+            }
+            if (mUserManager.getUserInfo(userId).isManagedProfile()) {
+                // Make sure the profile doesn't get locked straight after setting work challenge.
+                setDeviceUnlockedForUser(userId);
+            }
+            notifySeparateProfileChallengeChanged(userId);
+            onPostPasswordChanged(credential, userId);
+            scheduleGc();
+            return true;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        if (mUserManager.getUserInfo(userId).isManagedProfile()) {
-            // Make sure the profile doesn't get locked straight after setting work challenge.
-            setDeviceUnlockedForUser(userId);
-        }
-        notifySeparateProfileChallengeChanged(userId);
-        scheduleGc();
-        return true;
     }
 
     /**
@@ -1703,10 +1719,157 @@
         return true;
     }
 
+    private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
+        updateEncryptionPasswordIfNeeded(newCredential, userHandle);
+        if (newCredential.isPattern()) {
+            setBoolean(LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, true, userHandle);
+        }
+        updatePasswordHistory(newCredential, userHandle);
+        mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userHandle);
+    }
+
+    /**
+     * Update device encryption password if calling user is USER_SYSTEM and device supports
+     * encryption.
+     */
+    private void updateEncryptionPasswordIfNeeded(LockscreenCredential credential, int userHandle) {
+        // Update the device encryption password.
+        if (userHandle != UserHandle.USER_SYSTEM || !isDeviceEncryptionEnabled()) {
+            return;
+        }
+        if (!shouldEncryptWithCredentials()) {
+            updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
+            return;
+        }
+        if (credential.isNone()) {
+            // Set the encryption password to default.
+            setCredentialRequiredToDecrypt(false);
+        }
+        updateEncryptionPassword(credential.getStorageCryptType(), credential.getCredential());
+    }
+
+    /**
+     * Store the hash of the *current* password in the password history list, if device policy
+     * enforces password history requirement.
+     */
+    private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
+        if (password.isNone()) {
+            return;
+        }
+        if (password.isPattern()) {
+            // Do not keep track of historical patterns
+            return;
+        }
+        // Add the password to the password history. We assume all
+        // password hashes have the same length for simplicity of implementation.
+        String passwordHistory = getString(
+                LockPatternUtils.PASSWORD_HISTORY_KEY, /* defaultValue= */ null, userHandle);
+        if (passwordHistory == null) {
+            passwordHistory = "";
+        }
+        int passwordHistoryLength = getRequestedPasswordHistoryLength(userHandle);
+        if (passwordHistoryLength == 0) {
+            passwordHistory = "";
+        } else {
+            final byte[] hashFactor = getHashFactor(password, userHandle);
+            final byte[] salt = getSalt(userHandle).getBytes();
+            String hash = password.passwordToHistoryHash(hashFactor, salt);
+            if (hash == null) {
+                Slog.e(TAG, "Compute new style password hash failed, fallback to legacy style");
+                hash = password.legacyPasswordToHash(salt);
+            }
+            if (TextUtils.isEmpty(passwordHistory)) {
+                passwordHistory = hash;
+            } else {
+                String[] history = passwordHistory.split(
+                        LockPatternUtils.PASSWORD_HISTORY_DELIMITER);
+                StringJoiner joiner = new StringJoiner(LockPatternUtils.PASSWORD_HISTORY_DELIMITER);
+                joiner.add(hash);
+                for (int i = 0; i < passwordHistoryLength - 1 && i < history.length; i++) {
+                    joiner.add(history[i]);
+                }
+                passwordHistory = joiner.toString();
+            }
+        }
+        setString(LockPatternUtils.PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
+    }
+
+    private String getSalt(int userId) {
+        long salt = getLong(LockPatternUtils.LOCK_PASSWORD_SALT_KEY, 0, userId);
+        if (salt == 0) {
+            try {
+                salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
+                setLong(LockPatternUtils.LOCK_PASSWORD_SALT_KEY, salt, userId);
+                Slog.v(TAG, "Initialized lock password salt for user: " + userId);
+            } catch (NoSuchAlgorithmException e) {
+                // Throw an exception rather than storing a password we'll never be able to recover
+                throw new IllegalStateException("Couldn't get SecureRandom number", e);
+            }
+        }
+        return Long.toHexString(salt);
+    }
+
+    private int getRequestedPasswordHistoryLength(int userId) {
+        return mInjector.getDevicePolicyManager().getPasswordHistoryLength(null, userId);
+    }
+
+    private static boolean isDeviceEncryptionEnabled() {
+        return StorageManager.isEncrypted();
+    }
+
+    private boolean shouldEncryptWithCredentials() {
+        return isCredentialRequiredToDecrypt() && !isDoNotAskCredentialsOnBootSet();
+    }
+
+    private boolean isDoNotAskCredentialsOnBootSet() {
+        return mInjector.getDevicePolicyManager().getDoNotAskCredentialsOnBoot();
+    }
+
+    private boolean isCredentialRequiredToDecrypt() {
+        final int value = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, -1);
+        return value != 0;
+    }
+
     private VerifyCredentialResponse convertResponse(GateKeeperResponse gateKeeperResponse) {
         return VerifyCredentialResponse.fromGateKeeperResponse(gateKeeperResponse);
     }
 
+    private void setCredentialRequiredToDecrypt(boolean required) {
+        if (isDeviceEncryptionEnabled()) {
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0);
+        }
+    }
+
+    /** Update the encryption password if it is enabled **/
+    @Override
+    public void updateEncryptionPassword(final int type, final byte[] password) {
+        if (!hasSecureLockScreen() && password != null && password.length != 0) {
+            throw new UnsupportedOperationException(
+                    "This operation requires the lock screen feature.");
+        }
+        if (!isDeviceEncryptionEnabled()) {
+            return;
+        }
+        final IBinder service = ServiceManager.getService("mount");
+        if (service == null) {
+            Slog.e(TAG, "Could not find the mount service to update the encryption password");
+            return;
+        }
+
+        // TODO(b/120484642): This is a location where we still use a String for vold
+        String passwordString = password != null ? new String(password) : null;
+        mHandler.post(() -> {
+            IStorageManager storageManager = mInjector.getStorageManager();
+            try {
+                storageManager.changeEncryptionPassword(type, passwordString);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Error changing encryption password", e);
+            }
+        });
+    }
+
     @VisibleForTesting /** Note: this method is overridden in unit tests */
     protected void tieProfileLockToParent(int userId, LockscreenCredential password) {
         if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
@@ -1952,10 +2115,16 @@
     @Nullable
     public VerifyCredentialResponse verifyCredential(LockscreenCredential credential,
             int userId, int flags) {
-        checkPasswordReadPermission();
+        if (!hasPermission(PERMISSION) && !hasPermission(SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS)) {
+            throw new SecurityException(
+                    "verifyCredential requires SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS or "
+                            + PERMISSION);
+        }
+        final long identity = Binder.clearCallingIdentity();
         try {
             return doVerifyCredential(credential, userId, null /* progressCallback */, flags);
         } finally {
+            Binder.restoreCallingIdentity(identity);
             scheduleGc();
         }
     }
@@ -3436,8 +3605,12 @@
                 throw new UnsupportedOperationException(
                         "This operation requires secure lock screen feature.");
             }
-            return LockSettingsService.this.setLockCredentialWithToken(
-                    credential, tokenHandle, token, userId);
+            if (!LockSettingsService.this.setLockCredentialWithToken(
+                    credential, tokenHandle, token, userId)) {
+                return false;
+            }
+            onPostPasswordChanged(credential, userId);
+            return true;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 5286bce..ba1e23c 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -16,7 +16,9 @@
 
 package com.android.server.media;
 
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+import static android.content.Intent.ACTION_SCREEN_OFF;
+import static android.content.Intent.ACTION_SCREEN_ON;
 import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
 import static android.media.MediaRouter2Utils.getOriginalId;
 import static android.media.MediaRouter2Utils.getProviderId;
@@ -26,7 +28,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.media.IMediaRouter2;
 import android.media.IMediaRouter2Manager;
@@ -41,6 +46,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -62,6 +68,7 @@
 import java.util.Objects;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 
 /**
  * Implements features related to {@link android.media.MediaRouter2} and
@@ -74,12 +81,13 @@
     // TODO: (In Android S or later) if we add callback methods for generic failures
     //       in MediaRouter2, remove this constant and replace the usages with the real request IDs.
     private static final long DUMMY_REQUEST_ID = -1;
-    private static final int PACKAGE_IMPORTANCE_FOR_DISCOVERY = IMPORTANCE_FOREGROUND;
+    private static final int PACKAGE_IMPORTANCE_FOR_DISCOVERY = IMPORTANCE_FOREGROUND_SERVICE;
 
     private final Context mContext;
     private final Object mLock = new Object();
     final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1);
     final ActivityManager mActivityManager;
+    final PowerManager mPowerManager;
 
     @GuardedBy("mLock")
     private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
@@ -100,11 +108,32 @@
                 }
             };
 
+    private final BroadcastReceiver mScreenOnOffReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                final int count = mUserRecords.size();
+                for (int i = 0; i < count; i++) {
+                    UserHandler userHandler = mUserRecords.valueAt(i).mHandler;
+                    userHandler.sendMessage(PooledLambda.obtainMessage(
+                            UserHandler::updateDiscoveryPreferenceOnHandler, userHandler));
+                }
+            }
+        }
+    };
+
     MediaRouter2ServiceImpl(Context context) {
         mContext = context;
         mActivityManager = mContext.getSystemService(ActivityManager.class);
         mActivityManager.addOnUidImportanceListener(mOnUidImportanceListener,
                 PACKAGE_IMPORTANCE_FOR_DISCOVERY);
+        mPowerManager = mContext.getSystemService(PowerManager.class);
+
+        IntentFilter screenOnOffIntentFilter = new IntentFilter();
+        screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON);
+        screenOnOffIntentFilter.addAction(ACTION_SCREEN_OFF);
+
+        mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter);
     }
 
     ////////////////////////////////////////////////////////////////
@@ -2121,19 +2150,26 @@
             if (service == null) {
                 return;
             }
-            List<RouteDiscoveryPreference> discoveryPreferences = new ArrayList<>();
+            List<RouteDiscoveryPreference> discoveryPreferences = Collections.emptyList();
             List<RouterRecord> routerRecords = getRouterRecords();
             List<ManagerRecord> managerRecords = getManagerRecords();
-            boolean isAnyManagerScanning =
-                    managerRecords.stream().anyMatch(manager -> manager.mIsScanning
-                    && service.mActivityManager.getPackageImportance(manager.mPackageName)
-                    <= PACKAGE_IMPORTANCE_FOR_DISCOVERY);
 
-            for (RouterRecord routerRecord : routerRecords) {
-                if (isAnyManagerScanning
-                        || service.mActivityManager.getPackageImportance(routerRecord.mPackageName)
-                        <= PACKAGE_IMPORTANCE_FOR_DISCOVERY) {
-                    discoveryPreferences.add(routerRecord.mDiscoveryPreference);
+            if (service.mPowerManager.isInteractive()) {
+                boolean isManagerScanning = managerRecords.stream().anyMatch(manager ->
+                        manager.mIsScanning && service.mActivityManager
+                                .getPackageImportance(manager.mPackageName)
+                                <= PACKAGE_IMPORTANCE_FOR_DISCOVERY);
+
+                if (isManagerScanning) {
+                    discoveryPreferences = routerRecords.stream()
+                            .map(record -> record.mDiscoveryPreference)
+                            .collect(Collectors.toList());
+                } else {
+                    discoveryPreferences = routerRecords.stream().filter(record ->
+                            service.mActivityManager.getPackageImportance(record.mPackageName)
+                                    <= PACKAGE_IMPORTANCE_FOR_DISCOVERY)
+                            .map(record -> record.mDiscoveryPreference)
+                            .collect(Collectors.toList());
                 }
             }
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 352eac3..05922b3 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -926,10 +926,11 @@
 
             mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
             try {
-                // TODO: There shouldn't be a need to receive callback for all changes.
-                mActivityManager.registerUidObserver(mUidObserver,
-                        ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE,
-                        ActivityManager.PROCESS_STATE_UNKNOWN, "android");
+                final int changes = ActivityManager.UID_OBSERVER_PROCSTATE
+                        | ActivityManager.UID_OBSERVER_GONE
+                        | ActivityManager.UID_OBSERVER_CAPABILITY;
+                mActivityManager.registerUidObserver(mUidObserver, changes,
+                        NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android");
                 mNetworkManager.registerObserver(mAlertObserver);
             } catch (RemoteException e) {
                 // ignored; both services live in system_server
@@ -5889,7 +5890,8 @@
         return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
     }
 
-    private class UidBlockedState {
+    @VisibleForTesting
+    static final class UidBlockedState {
         public int blockedReasons;
         public int allowedReasons;
         public int effectiveBlockedReasons;
@@ -5901,19 +5903,29 @@
         }
 
         void updateEffectiveBlockedReasons() {
-            effectiveBlockedReasons = blockedReasons;
+            if (LOGV && blockedReasons == BLOCKED_REASON_NONE) {
+                Log.v(TAG, "updateEffectiveBlockedReasons(): no blocked reasons");
+            }
+            effectiveBlockedReasons = getEffectiveBlockedReasons(blockedReasons, allowedReasons);
+            if (LOGV) {
+                Log.v(TAG, "updateEffectiveBlockedReasons()"
+                        + ": blockedReasons=" + Integer.toBinaryString(blockedReasons)
+                        + ", effectiveReasons=" + Integer.toBinaryString(effectiveBlockedReasons));
+            }
+        }
+
+        @VisibleForTesting
+        static int getEffectiveBlockedReasons(int blockedReasons, int allowedReasons) {
+            int effectiveBlockedReasons = blockedReasons;
             // If the uid is not subject to any blocked reasons, then return early
             if (blockedReasons == BLOCKED_REASON_NONE) {
-                if (LOGV) {
-                    Log.v(TAG, "updateEffectiveBlockedReasons(): no blocked reasons");
-                }
-                return;
+                return effectiveBlockedReasons;
             }
             if ((allowedReasons & ALLOWED_REASON_SYSTEM) != 0) {
-                effectiveBlockedReasons = (blockedReasons & ALLOWED_METERED_REASON_MASK);
+                effectiveBlockedReasons &= ALLOWED_METERED_REASON_MASK;
             }
             if ((allowedReasons & ALLOWED_METERED_REASON_SYSTEM) != 0) {
-                effectiveBlockedReasons = (blockedReasons & ~ALLOWED_METERED_REASON_MASK);
+                effectiveBlockedReasons &= ~ALLOWED_METERED_REASON_MASK;
             }
             if ((allowedReasons & ALLOWED_REASON_FOREGROUND) != 0) {
                 effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER;
@@ -5939,11 +5951,7 @@
             if ((allowedReasons & ALLOWED_METERED_REASON_USER_EXEMPTED) != 0) {
                 effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER;
             }
-            if (LOGV) {
-                Log.v(TAG, "updateEffectiveBlockedReasons()"
-                        + ": blockedReasons=" + Integer.toBinaryString(blockedReasons)
-                        + ", effectiveReasons=" + Integer.toBinaryString(effectiveBlockedReasons));
-            }
+            return effectiveBlockedReasons;
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6dcf39b..2f4cbd5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -685,6 +685,21 @@
                 }
             }
         }
+
+        // Remove notifications with the specified user & channel ID.
+        public void removeChannelNotifications(String pkg, @UserIdInt int userId,
+                String channelId) {
+            for (int i = 0; i < mBuffer.size(); i++) {
+                final Pair<StatusBarNotification, Integer> pair = mBuffer.get(i);
+                if (pair.first != null
+                        && userId == pair.first.getNormalizedUserId()
+                        && pkg != null && pkg.equals(pair.first.getPackageName())
+                        && pair.first.getNotification() != null
+                        && Objects.equals(channelId, pair.first.getNotification().getChannelId())) {
+                    mBuffer.remove(i);
+                }
+            }
+        }
     }
 
     void loadDefaultApprovedServices(int userId) {
@@ -3623,6 +3638,8 @@
             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),
@@ -8179,8 +8196,10 @@
             summaries.remove(r.getSbn().getPackageName());
         }
 
-        // Save it for users of getHistoricalNotifications()
-        mArchive.record(r.getSbn(), reason);
+        // Save it for users of getHistoricalNotifications(), unless the whole channel was deleted
+        if (reason != REASON_CHANNEL_REMOVED) {
+            mArchive.record(r.getSbn(), reason);
+        }
 
         final long now = System.currentTimeMillis();
         final LogMaker logMaker = r.getItemLogMaker()
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index f5d6489..1050835 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -27,6 +27,7 @@
 import android.service.notification.IConditionProvider;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.ZenModeConfig;
+import android.util.Log;
 import android.util.Slog;
 
 import java.io.PrintWriter;
@@ -122,8 +123,11 @@
 
     public static void traceSetNotificationPolicy(String pkg, int targetSdk,
             NotificationManager.Policy policy) {
-        append(TYPE_SET_NOTIFICATION_POLICY, "pkg=" + pkg + " targetSdk=" + targetSdk
-                + " NotificationPolicy=" + policy.toString());
+        String policyLog = "pkg=" + pkg + " targetSdk=" + targetSdk
+                + " NotificationPolicy=" + policy.toString();
+        append(TYPE_SET_NOTIFICATION_POLICY, policyLog);
+        // TODO(b/180205791): remove when we can better surface apps that are changing policy
+        Log.d(TAG, "Zen Policy Changed: " + policyLog);
     }
 
     public static void traceSubscribe(Uri uri, IConditionProvider provider, RemoteException e) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 18c689f..fcee63c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -586,19 +586,6 @@
     }
 
     private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
-        if (isNew) {
-            rule.id = ZenModeConfig.newRuleId();
-            rule.creationTime = System.currentTimeMillis();
-            rule.component = automaticZenRule.getOwner();
-            rule.configurationActivity = automaticZenRule.getConfigurationActivity();
-            rule.pkg = (rule.component != null)
-                    ? rule.component.getPackageName()
-                    : rule.configurationActivity.getPackageName();
-        }
-
-        if (rule.enabled != automaticZenRule.isEnabled()) {
-            rule.snoozing = false;
-        }
         rule.name = automaticZenRule.getName();
         rule.condition = null;
         rule.conditionId = automaticZenRule.getConditionId();
@@ -607,6 +594,20 @@
         rule.zenPolicy = automaticZenRule.getZenPolicy();
         rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
                 automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+        rule.configurationActivity = automaticZenRule.getConfigurationActivity();
+
+        if (isNew) {
+            rule.id = ZenModeConfig.newRuleId();
+            rule.creationTime = System.currentTimeMillis();
+            rule.component = automaticZenRule.getOwner();
+            rule.pkg = (rule.component != null)
+                    ? rule.component.getPackageName()
+                    : rule.configurationActivity.getPackageName();
+        }
+
+        if (rule.enabled != automaticZenRule.isEnabled()) {
+            rule.snoozing = false;
+        }
     }
 
     protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index c236b4d..fa126a4 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -267,7 +267,7 @@
                                 mArtStatsLogger,
                                 sessionId,
                                 compilerFilter,
-                                sharedGid,
+                                pkg.getUid(),
                                 packageStats.getCompileTime(path),
                                 dexMetadataPath,
                                 options.getCompilationReason(),
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4eafe51..e532790 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -948,14 +948,14 @@
                 == PackageManager.PERMISSION_GRANTED);
         final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
         final boolean isUpdate = targetPackageUid != -1;
-        final InstallSourceInfo installSourceInfo = isUpdate
+        final InstallSourceInfo existingInstallSourceInfo = isUpdate
                 ? mPm.getInstallSourceInfo(packageName)
                 : null;
-        final String installerPackageName = installSourceInfo != null
-                ? installSourceInfo.getInstallingPackageName()
+        final String existingInstallerPackageName = existingInstallSourceInfo != null
+                ? existingInstallSourceInfo.getInstallingPackageName()
                 : null;
         final boolean isInstallerOfRecord = isUpdate
-                && Objects.equals(installerPackageName, getInstallerPackageName());
+                && Objects.equals(existingInstallerPackageName, getInstallerPackageName());
         final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
         final boolean isPermissionGranted = isInstallPermissionGranted
                 || (isUpdatePermissionGranted && isUpdate)
@@ -972,7 +972,7 @@
             return USER_ACTION_NOT_NEEDED;
         }
 
-        if (mPm.isInstallDisabledForPackage(installerPackageName, mInstallerUid, userId)) {
+        if (mPm.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid, userId)) {
             // show the installer to account for device poslicy or unknown sources use cases
             return USER_ACTION_REQUIRED;
         }
@@ -4157,7 +4157,7 @@
         if (stageDir != null && !params.isStaged) {
             try {
                 if (incrementalFileStorages != null) {
-                    incrementalFileStorages.cleanUp();
+                    incrementalFileStorages.cleanUpAndMarkComplete();
                 }
                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
             } catch (InstallerException ignored) {
@@ -4183,7 +4183,7 @@
         }
         try {
             if (incrementalFileStorages != null) {
-                incrementalFileStorages.cleanUp();
+                incrementalFileStorages.cleanUpAndMarkComplete();
             }
             mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
         } catch (InstallerException ignored) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2dcc8d9..9477464 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5966,7 +5966,8 @@
                 backgroundHandler,
                 SYSTEM_PARTITIONS,
                 (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
-                (i, pm) -> PermissionManagerService.create(context),
+                (i, pm) -> PermissionManagerService.create(context,
+                        i.getSystemConfig().getAvailableFeatures()),
                 (i, pm) -> new UserManagerService(context, pm,
                         new UserDataPreparer(installer, installLock, context, onlyCore),
                         lock),
@@ -12271,19 +12272,18 @@
 
     public ArraySet<String> getOptimizablePackages() {
         ArraySet<String> pkgs = new ArraySet<>();
-        final boolean hibernationEnabled = AppHibernationService.isAppHibernationEnabled();
-        AppHibernationManagerInternal appHibernationManager =
-                mInjector.getLocalService(AppHibernationManagerInternal.class);
         synchronized (mLock) {
             for (AndroidPackage p : mPackages.values()) {
-                // Checking hibernation state is an inexpensive call.
-                boolean isHibernating = hibernationEnabled
-                        && appHibernationManager.isHibernatingGlobally(p.getPackageName());
-                if (PackageDexOptimizer.canOptimizePackage(p) && !isHibernating) {
+                if (PackageDexOptimizer.canOptimizePackage(p)) {
                     pkgs.add(p.getPackageName());
                 }
             }
         }
+        if (AppHibernationService.isAppHibernationEnabled()) {
+            AppHibernationManagerInternal appHibernationManager =
+                    mInjector.getLocalService(AppHibernationManagerInternal.class);
+            pkgs.removeIf(pkgName -> appHibernationManager.isHibernatingGlobally(pkgName));
+        }
         return pkgs;
     }
 
@@ -21456,6 +21456,8 @@
         synchronized (mLock) {
             if (outInfo != null) {
                 outInfo.uid = ps.appId;
+                outInfo.broadcastAllowList = mAppsFilter.getVisibilityAllowList(ps,
+                        allUserHandles, mSettings.getPackagesLocked());
             }
         }
 
@@ -23464,10 +23466,12 @@
             }
         }
         if (shouldUnhibernate) {
-            AppHibernationManagerInternal ah =
-                    mInjector.getLocalService(AppHibernationManagerInternal.class);
-            ah.setHibernatingForUser(packageName, userId, false);
-            ah.setHibernatingGlobally(packageName, false);
+            mHandler.post(() -> {
+                AppHibernationManagerInternal ah =
+                        mInjector.getLocalService(AppHibernationManagerInternal.class);
+                ah.setHibernatingForUser(packageName, userId, false);
+                ah.setHibernatingGlobally(packageName, 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 ef37201..091d820 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -127,18 +127,12 @@
     private static final Map<String, Integer> ISA_MAP = new HashMap();
 
     static {
-        COMPILE_FILTER_MAP.put("arm", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_ARM);
-        COMPILE_FILTER_MAP.put("arm64", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_ARM64);
-        COMPILE_FILTER_MAP.put("x86", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_X86);
-        COMPILE_FILTER_MAP.put("x86_64", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_X86_64);
-        COMPILE_FILTER_MAP.put("mips", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_MIPS);
-        COMPILE_FILTER_MAP.put("mips64", ArtStatsLog.
-                ART_DATUM_REPORTED__ISA__ART_ISA_MIPS64);
+        ISA_MAP.put("arm", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_ARM);
+        ISA_MAP.put("arm64", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_ARM64);
+        ISA_MAP.put("x86", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_X86);
+        ISA_MAP.put("x86_64", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_X86_64);
+        ISA_MAP.put("mips", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_MIPS);
+        ISA_MAP.put("mips64", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_MIPS64);
     }
 
     public static void writeStatsLog(
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 310ef23..2d1178a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -69,8 +69,10 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
+import android.content.AttributionSource;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.PermissionGroupInfoFlags;
 import android.content.pm.PackageManager.PermissionInfoFlags;
@@ -85,7 +87,6 @@
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.metrics.LogMaker;
 import android.os.AsyncTask;
-import android.content.AttributionSource;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Debug;
@@ -177,6 +178,10 @@
 
     private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
 
+    // For automotive products, CarService enforces allow-listing of the privileged permissions
+    // com.android.car is the package name which declares auto specific permissions
+    private static final String CAR_PACKAGE_NAME = "com.android.car";
+
     /** Cap the size of permission trees that 3rd party apps can define; in characters of text */
     private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;
     /** Empty array to avoid allocations */
@@ -210,6 +215,10 @@
         STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
     }
 
+    /** Set of source package names for Privileged Permission Allowlist */
+    private final ArraySet<String> mPrivilegedPermissionAllowlistSourcePackageNames =
+            new ArraySet<>();
+
     /** Lock to protect internal data access */
     private final Object mLock = new Object();
 
@@ -356,7 +365,8 @@
         }
     };
 
-    PermissionManagerService(@NonNull Context context) {
+    PermissionManagerService(@NonNull Context context,
+            @NonNull ArrayMap<String, FeatureInfo> availableFeatures) {
         // The package info cache is the cache for package and permission information.
         // Disable the package info and package permission caches locally but leave the
         // checkPermission cache active.
@@ -368,6 +378,13 @@
         mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
 
+        mPrivilegedPermissionAllowlistSourcePackageNames.add(PLATFORM_PACKAGE_NAME);
+        // PackageManager.hasSystemFeature() is not used here because PackageManagerService
+        // isn't ready yet.
+        if (availableFeatures.containsKey(PackageManager.FEATURE_AUTOMOTIVE)) {
+            mPrivilegedPermissionAllowlistSourcePackageNames.add(CAR_PACKAGE_NAME);
+        }
+
         mHandlerThread = new ServiceThread(TAG,
                 Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
         mHandlerThread.start();
@@ -422,7 +439,8 @@
      * lock created by the permission manager itself.
      */
     @NonNull
-    public static PermissionManagerServiceInternal create(@NonNull Context context) {
+    public static PermissionManagerServiceInternal create(@NonNull Context context,
+            ArrayMap<String, FeatureInfo> availableFeatures) {
         final PermissionManagerServiceInternal permMgrInt =
                 LocalServices.getService(PermissionManagerServiceInternal.class);
         if (permMgrInt != null) {
@@ -431,7 +449,7 @@
         PermissionManagerService permissionService =
                 (PermissionManagerService) ServiceManager.getService("permissionmgr");
         if (permissionService == null) {
-            permissionService = new PermissionManagerService(context);
+            permissionService = new PermissionManagerService(context, availableFeatures);
             ServiceManager.addService("permissionmgr", permissionService);
         }
         return LocalServices.getService(PermissionManagerServiceInternal.class);
@@ -3318,7 +3336,8 @@
         if (!pkg.isPrivileged()) {
             return true;
         }
-        if (!Objects.equals(permission.getPackageName(), PLATFORM_PACKAGE_NAME)) {
+        if (!mPrivilegedPermissionAllowlistSourcePackageNames
+                .contains(permission.getPackageName())) {
             return true;
         }
         final String permissionName = permission.getName();
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index fa27b4b4..25709d4 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -21,11 +21,22 @@
 import android.app.AppOpsManager;
 import android.app.AppOpsManagerInternal;
 import android.app.SyncNotedAppOp;
+import android.app.role.RoleManager;
 import android.content.AttributionSource;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.location.LocationManagerInternal;
+import android.net.Uri;
 import android.os.IBinder;
+import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.function.HeptFunction;
@@ -35,6 +46,8 @@
 import com.android.internal.util.function.TriFunction;
 import com.android.server.LocalServices;
 
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -42,9 +55,21 @@
  * This class defines policy for special behaviors around app ops.
  */
 public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegate {
+    private static final String LOG_TAG = AppOpsPolicy.class.getName();
+
+    private static final String ACTIVITY_RECOGNITION_TAGS =
+            "android:activity_recognition_allow_listed_tags";
+    private static final String ACTIVITY_RECOGNITION_TAGS_SEPARATOR = ";";
+
     @NonNull
     private final Object mLock = new Object();
 
+    @NonNull
+    private final Context mContext;
+
+    @NonNull
+    private final RoleManager mRoleManager;
+
     /**
      * The locking policy around the location tags is a bit special. Since we want to
      * avoid grabbing the lock on every op note we are taking the approach where the
@@ -60,48 +85,57 @@
     private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags =
             new ConcurrentHashMap<>();
 
-    public AppOpsPolicy() {
+    @GuardedBy("mLock - writes only - see above")
+    @NonNull
+    private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>>
+            mActivityRecognitionTags = new ConcurrentHashMap<>();
+
+    public AppOpsPolicy(@NonNull Context context) {
+        mContext = context;
+        mRoleManager = mContext.getSystemService(RoleManager.class);
+
         final LocationManagerInternal locationManagerInternal = LocalServices.getService(
                 LocationManagerInternal.class);
         locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> {
             synchronized (mLock) {
-                final int uid = providerTagInfo.getUid();
-                // We make a copy of the per UID state to limit our mutation to one
-                // operation in the underlying concurrent data structure.
-                ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
-                if (uidTags != null) {
-                    uidTags = new ArrayMap<>(uidTags);
-                }
-
-                final String packageName = providerTagInfo.getPackageName();
-                ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
-                if (packageTags != null) {
-                    packageTags = new ArraySet<>(packageTags);
-                }
-
-                final Set<String> providerTags = providerTagInfo.getTags();
-                if (providerTags != null && !providerTags.isEmpty()) {
-                    if (packageTags != null) {
-                        packageTags.clear();
-                        packageTags.addAll(providerTags);
-                    } else {
-                        packageTags = new ArraySet<>(providerTags);
-                    }
-                    if (uidTags == null) {
-                        uidTags = new ArrayMap<>();
-                    }
-                    uidTags.put(packageName, packageTags);
-                    mLocationTags.put(uid, uidTags);
-                } else if (uidTags != null) {
-                    uidTags.remove(packageName);
-                    if (!uidTags.isEmpty()) {
-                        mLocationTags.put(uid, uidTags);
-                    } else {
-                        mLocationTags.remove(uid);
-                    }
-                }
+                updateAllowListedTagsForPackageLocked(providerTagInfo.getUid(),
+                        providerTagInfo.getPackageName(), providerTagInfo.getTags(),
+                        mLocationTags);
             }
         });
+
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        intentFilter.addDataScheme("package");
+
+        context.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final Uri uri = intent.getData();
+                if (uri == null) {
+                    return;
+                }
+                final String packageName = uri.getSchemeSpecificPart();
+                if (TextUtils.isEmpty(packageName)) {
+                    return;
+                }
+                final List<String> activityRecognizers = mRoleManager.getRoleHolders(
+                        RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER);
+                if (activityRecognizers.contains(packageName)) {
+                    updateActivityRecognizerTags(packageName);
+                }
+            }
+        }, UserHandle.SYSTEM, intentFilter, null, null);
+
+        mRoleManager.addOnRoleHoldersChangedListenerAsUser(context.getMainExecutor(),
+                (String roleName, UserHandle user) -> {
+            if (RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER.equals(roleName)) {
+                initializeActivityRecognizersTags();
+            }
+        }, UserHandle.SYSTEM);
+
+        initializeActivityRecognizersTags();
     }
 
     @Override
@@ -121,7 +155,7 @@
             @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable
             String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer,
                     String, String, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
-        return superImpl.apply(resolveOpCode(code, uid, packageName, attributionTag), uid,
+        return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag), uid,
                 packageName, attributionTag, shouldCollectAsyncNotedOp,
                 message, shouldCollectMessage);
     }
@@ -132,7 +166,7 @@
             boolean shouldCollectMessage, boolean skipProxyOperation, @NonNull HexFunction<Integer,
                     AttributionSource, Boolean, String, Boolean, Boolean,
             SyncNotedAppOp> superImpl) {
-        return superImpl.apply(resolveOpCode(code, attributionSource.getUid(),
+        return superImpl.apply(resolveDatasourceOp(code, attributionSource.getUid(),
                 attributionSource.getPackageName(), attributionSource.getAttributionTag()),
                 attributionSource, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
                 skipProxyOperation);
@@ -144,7 +178,7 @@
             boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
             boolean skipProxyOperation, @NonNull OctFunction<IBinder, Integer, AttributionSource,
                     Boolean, Boolean, String, Boolean, Boolean, SyncNotedAppOp> superImpl) {
-        return superImpl.apply(token, resolveOpCode(code, attributionSource.getUid(),
+        return superImpl.apply(token, resolveDatasourceOp(code, attributionSource.getUid(),
                 attributionSource.getPackageName(), attributionSource.getAttributionTag()),
                 attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
                 shouldCollectMessage, skipProxyOperation);
@@ -154,36 +188,129 @@
     public void finishProxyOperation(IBinder clientId, int code,
             @NonNull AttributionSource attributionSource,
             @NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl) {
-        superImpl.apply(clientId, resolveOpCode(code, attributionSource.getUid(),
+        superImpl.apply(clientId, resolveDatasourceOp(code, attributionSource.getUid(),
                 attributionSource.getPackageName(), attributionSource.getAttributionTag()),
                 attributionSource);
     }
 
-    private int resolveOpCode(int code, int uid, @NonNull String packageName,
+    private int resolveDatasourceOp(int code, int uid, @NonNull String packageName,
             @Nullable String attributionTag) {
-        if (isHandledOp(code) && attributionTag != null) {
-            // Only a single lookup from the underlying concurrent data structure
-            final ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
-            if (uidTags != null) {
-                final ArraySet<String> packageTags = uidTags.get(packageName);
-                if (packageTags != null && packageTags.contains(attributionTag)) {
-                    return resolveHandledOp(code);
+        if (attributionTag == null) {
+            return code;
+        }
+        int resolvedCode = resolveLocationOp(code);
+        if (resolvedCode != code) {
+            if (isDatasourceAttributionTag(uid, packageName, attributionTag,
+                    mLocationTags)) {
+                return resolvedCode;
+            }
+        } else {
+            resolvedCode = resolveArOp(code);
+            if (resolvedCode != code) {
+                if (isDatasourceAttributionTag(uid, packageName, attributionTag,
+                        mActivityRecognitionTags)) {
+                    return resolvedCode;
                 }
             }
         }
         return code;
     }
 
-    private static boolean isHandledOp(int code) {
-        switch (code) {
-            case AppOpsManager.OP_FINE_LOCATION:
-            case AppOpsManager.OP_COARSE_LOCATION:
+    private void initializeActivityRecognizersTags() {
+        final List<String> activityRecognizers = mRoleManager.getRoleHolders(
+                RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER);
+        final int recognizerCount = activityRecognizers.size();
+        if (recognizerCount > 0) {
+            for (int i = 0; i < recognizerCount; i++) {
+                final String activityRecognizer = activityRecognizers.get(i);
+                updateActivityRecognizerTags(activityRecognizer);
+            }
+        } else {
+            clearActivityRecognitionTags();
+        }
+    }
+
+    private void clearActivityRecognitionTags() {
+        synchronized (mLock) {
+            mActivityRecognitionTags.clear();
+        }
+    }
+
+    private void updateActivityRecognizerTags(@NonNull String activityRecognizer) {
+        try {
+            final ApplicationInfo recognizerAppInfo = mContext.getPackageManager()
+                    .getApplicationInfoAsUser(activityRecognizer, PackageManager.GET_META_DATA,
+                            UserHandle.USER_SYSTEM);
+            if (recognizerAppInfo.metaData == null) {
+                return;
+            }
+            final String tagsList = recognizerAppInfo.metaData.getString(ACTIVITY_RECOGNITION_TAGS);
+            if (tagsList != null) {
+                final String[] tags = tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR);
+                synchronized (mLock) {
+                    updateAllowListedTagsForPackageLocked(recognizerAppInfo.uid,
+                            recognizerAppInfo.packageName, new ArraySet<>(tags),
+                            mActivityRecognitionTags);
+                }
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.wtf(LOG_TAG, "Missing " + RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER
+                    + " role holder package " + activityRecognizer);
+        }
+    }
+
+    private static void updateAllowListedTagsForPackageLocked(int uid, String packageName,
+            Set<String> allowListedTags, ConcurrentHashMap<Integer, ArrayMap<String,
+            ArraySet<String>>> datastore) {
+        // We make a copy of the per UID state to limit our mutation to one
+        // operation in the underlying concurrent data structure.
+        ArrayMap<String, ArraySet<String>> uidTags = datastore.get(uid);
+        if (uidTags != null) {
+            uidTags = new ArrayMap<>(uidTags);
+        }
+
+        ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
+        if (packageTags != null) {
+            packageTags = new ArraySet<>(packageTags);
+        }
+
+        if (allowListedTags != null && !allowListedTags.isEmpty()) {
+            if (packageTags != null) {
+                packageTags.clear();
+                packageTags.addAll(allowListedTags);
+            } else {
+                packageTags = new ArraySet<>(allowListedTags);
+            }
+            if (uidTags == null) {
+                uidTags = new ArrayMap<>();
+            }
+            uidTags.put(packageName, packageTags);
+            datastore.put(uid, uidTags);
+        } else if (uidTags != null) {
+            uidTags.remove(packageName);
+            if (!uidTags.isEmpty()) {
+                datastore.put(uid, uidTags);
+            } else {
+                datastore.remove(uid);
+            }
+        }
+    }
+
+    private static boolean isDatasourceAttributionTag(int uid, @NonNull String packageName,
+            @NonNull String attributionTag, @NonNull Map<Integer, ArrayMap<String,
+            ArraySet<String>>> mappedOps) {
+        // Only a single lookup from the underlying concurrent data structure
+        final ArrayMap<String, ArraySet<String>> uidTags = mappedOps.get(uid);
+        if (uidTags != null) {
+            final ArraySet<String> packageTags = uidTags.get(packageName);
+            if (packageTags != null && packageTags.contains(attributionTag)) {
                 return true;
+            }
         }
         return false;
     }
 
-    private static int resolveHandledOp(int code) {
+    private static int resolveLocationOp(int code) {
         switch (code) {
             case AppOpsManager.OP_FINE_LOCATION:
                 return AppOpsManager.OP_FINE_LOCATION_SOURCE;
@@ -192,4 +319,11 @@
         }
         return code;
     }
+
+    private static int resolveArOp(int code) {
+        if (code == AppOpsManager.OP_ACTIVITY_RECOGNITION) {
+            return AppOpsManager.OP_ACTIVITY_RECOGNITION_SOURCE;
+        }
+        return code;
+    }
 }
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
index 52ff48b..d91e9c2 100644
--- a/services/core/java/com/android/server/timedetector/ServerFlags.java
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -28,6 +28,8 @@
 import com.android.server.timezonedetector.ConfigurationChangeListener;
 import com.android.server.timezonedetector.ServiceConfigAccessor;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.time.Duration;
 import java.util.Map;
 import java.util.Objects;
@@ -53,14 +55,15 @@
      */
     @StringDef(prefix = "KEY_", value = {
             KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
-            KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
-            KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+            KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+            KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
             KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
             KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
             KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
             KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
             KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
     })
+    @Retention(RetentionPolicy.SOURCE)
     @interface DeviceConfigKey {}
 
     /**
@@ -75,19 +78,19 @@
 
     /**
      * The key for the server flag that can override the device config for whether the primary
-     * location time zone provider is enabled or disabled.
+     * location time zone provider is enabled, disabled, or (for testing) in simulation mode.
      */
     @DeviceConfigKey
-    public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
-            "primary_location_time_zone_provider_enabled_override";
+    public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
+            "primary_location_time_zone_provider_mode_override";
 
     /**
      * The key for the server flag that can override the device config for whether the secondary
-     * location time zone provider is enabled or disabled.
+     * location time zone provider is enabled or disabled, or (for testing) in simulation mode.
      */
     @DeviceConfigKey
-    public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
-            "secondary_location_time_zone_provider_enabled_override";
+    public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
+            "secondary_location_time_zone_provider_mode_override";
 
     /**
      * The key for the minimum delay after location time zone detection has been enabled before the
@@ -196,6 +199,16 @@
     }
 
     /**
+     * Returns an optional string value from {@link DeviceConfig} from the system_time
+     * namespace, returns {@link Optional#empty()} if there is no explicit value set.
+     */
+    @NonNull
+    public Optional<String> getOptionalString(@DeviceConfigKey String key) {
+        String value = DeviceConfig.getProperty(NAMESPACE_SYSTEM_TIME, key);
+        return Optional.ofNullable(value);
+    }
+
+    /**
      * Returns an optional boolean value from {@link DeviceConfig} from the system_time
      * namespace, returns {@link Optional#empty()} if there is no explicit value set.
      */
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 222e852..50d37f4 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringDef;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
@@ -27,6 +28,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.timedetector.ServerFlags;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.time.Duration;
 import java.util.Collections;
 import java.util.Objects;
@@ -40,13 +45,38 @@
  */
 public final class ServiceConfigAccessor {
 
+    @StringDef(prefix = "PROVIDER_MODE_",
+            value = { PROVIDER_MODE_SIMULATED, PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED})
+    @Retention(RetentionPolicy.SOURCE)
+    @Target(ElementType.TYPE_USE)
+    @interface ProviderMode {}
+
+    /**
+     * The "simulated" provider mode.
+     * For use with {@link #getPrimaryLocationTimeZoneProviderMode()} and {@link
+     * #getSecondaryLocationTimeZoneProviderMode()}.
+     */
+    public static final @ProviderMode String PROVIDER_MODE_SIMULATED = "simulated";
+
+    /**
+     * The "disabled" provider mode. For use with {@link #getPrimaryLocationTimeZoneProviderMode()}
+     * and {@link #getSecondaryLocationTimeZoneProviderMode()}.
+     */
+    public static final @ProviderMode String PROVIDER_MODE_DISABLED = "disabled";
+
+    /**
+     * The "enabled" provider mode. For use with {@link #getPrimaryLocationTimeZoneProviderMode()}
+     * and {@link #getSecondaryLocationTimeZoneProviderMode()}.
+     */
+    public static final @ProviderMode String PROVIDER_MODE_ENABLED = "enabled";
+
     private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet(
             new ArraySet<>(new String[] {
                     ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
                     ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
                     ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
-                    ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
-                    ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+                    ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+                    ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
                     ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
                     ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
                     ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS
@@ -139,14 +169,28 @@
 
     /**
      * Returns {@code true} if the location-based time zone detection feature is supported on the
-     * device. This can be used during feature testing on builds that are capable of location time
-     * zone detection to enable / disable the feature for some users.
+     * device.
      */
     public boolean isGeoTimeZoneDetectionFeatureSupported() {
+        // For the feature to be enabled it must:
+        // 1) Be turned on in config.
+        // 2) Not be turned off via a server flag.
+        // 3) There must be at least one location time zone provider enabled / configured.
         return mGeoDetectionFeatureSupportedInConfig
-                && isGeoTimeZoneDetectionFeatureSupportedInternal();
+                && isGeoTimeZoneDetectionFeatureSupportedInternal()
+                && atLeastOneProviderIsEnabled();
     }
 
+    private boolean atLeastOneProviderIsEnabled() {
+        return !(Objects.equals(getPrimaryLocationTimeZoneProviderMode(), PROVIDER_MODE_DISABLED)
+                && Objects.equals(getSecondaryLocationTimeZoneProviderMode(),
+                PROVIDER_MODE_DISABLED));
+    }
+
+    /**
+     * Returns {@code true} if the location-based time zone detection feature is not explicitly
+     * disabled by a server flag.
+     */
     private boolean isGeoTimeZoneDetectionFeatureSupportedInternal() {
         final boolean defaultEnabled = true;
         return mServerFlags.getBoolean(
@@ -154,42 +198,49 @@
                 defaultEnabled);
     }
 
+    @NonNull
+    public String getPrimaryLocationTimeZoneProviderPackageName() {
+        return mContext.getResources().getString(
+                R.string.config_primaryLocationTimeZoneProviderPackageName);
+    }
+
+    @NonNull
+    public String getSecondaryLocationTimeZoneProviderPackageName() {
+        return mContext.getResources().getString(
+                R.string.config_secondaryLocationTimeZoneProviderPackageName);
+    }
+
     /**
      * Returns {@code true} if the primary location time zone provider can be used.
      */
-    public boolean isPrimaryLocationTimeZoneProviderEnabled() {
-        return getPrimaryLocationTimeZoneProviderEnabledOverride()
-                .orElse(isPrimaryLocationTimeZoneProviderEnabledInConfig());
-    }
-
-    private boolean isPrimaryLocationTimeZoneProviderEnabledInConfig() {
-        int providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
-        return getConfigBoolean(providerEnabledConfigId);
+    @NonNull
+    public @ProviderMode String getPrimaryLocationTimeZoneProviderMode() {
+        return mServerFlags.getOptionalString(
+                ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
+                .orElse(getPrimaryLocationTimeZoneProviderModeFromConfig());
     }
 
     @NonNull
-    private Optional<Boolean> getPrimaryLocationTimeZoneProviderEnabledOverride() {
-        return mServerFlags.getOptionalBoolean(
-                ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
+    private @ProviderMode String getPrimaryLocationTimeZoneProviderModeFromConfig() {
+        int providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
+        return getConfigBoolean(providerEnabledConfigId)
+                ? PROVIDER_MODE_ENABLED : PROVIDER_MODE_DISABLED;
     }
 
     /**
-     * Returns {@code true} if the secondary location time zone provider can be used.
+     * Returns the mode for the secondary location time zone provider can be used.
      */
-    public boolean isSecondaryLocationTimeZoneProviderEnabled() {
-        return getSecondaryLocationTimeZoneProviderEnabledOverride()
-                .orElse(isSecondaryLocationTimeZoneProviderEnabledInConfig());
-    }
-
-    private boolean isSecondaryLocationTimeZoneProviderEnabledInConfig() {
-        int providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
-        return getConfigBoolean(providerEnabledConfigId);
+    public @ProviderMode String getSecondaryLocationTimeZoneProviderMode() {
+        return mServerFlags.getOptionalString(
+                ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
+                .orElse(getSecondaryLocationTimeZoneProviderModeFromConfig());
     }
 
     @NonNull
-    private Optional<Boolean> getSecondaryLocationTimeZoneProviderEnabledOverride() {
-        return mServerFlags.getOptionalBoolean(
-                ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
+    private @ProviderMode String getSecondaryLocationTimeZoneProviderModeFromConfig() {
+        int providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
+        return getConfigBoolean(providerEnabledConfigId)
+                ? PROVIDER_MODE_ENABLED : PROVIDER_MODE_DISABLED;
     }
 
     /**
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
index fb2a184..d2190fd 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
@@ -30,6 +30,7 @@
 import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
 
 import android.annotation.DurationMillisLong;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.RemoteCallback;
@@ -604,14 +605,14 @@
      * known provider, then the command is logged and discarded.
      */
     void handleProviderTestCommand(
-            @NonNull String providerName, @NonNull TestCommand testCommand,
+            @IntRange(from = 0, to = 1) int providerIndex, @NonNull TestCommand testCommand,
             @Nullable RemoteCallback callback) {
         mThreadingDomain.assertCurrentThread();
 
-        LocationTimeZoneProvider targetProvider = getLocationTimeZoneProvider(providerName);
+        LocationTimeZoneProvider targetProvider = getLocationTimeZoneProvider(providerIndex);
         if (targetProvider == null) {
             warnLog("Unable to process test command:"
-                    + " providerName=" + providerName + ", testCommand=" + testCommand);
+                    + " providerIndex=" + providerIndex + ", testCommand=" + testCommand);
             return;
         }
 
@@ -620,7 +621,7 @@
                 targetProvider.handleTestCommand(testCommand, callback);
             } catch (Exception e) {
                 warnLog("Unable to process test command:"
-                        + " providerName=" + providerName + ", testCommand=" + testCommand, e);
+                        + " providerIndex=" + providerIndex + ", testCommand=" + testCommand, e);
             }
         }
     }
@@ -658,14 +659,15 @@
     }
 
     @Nullable
-    private LocationTimeZoneProvider getLocationTimeZoneProvider(@NonNull String providerName) {
+    private LocationTimeZoneProvider getLocationTimeZoneProvider(
+            @IntRange(from = 0, to = 1) int providerIndex) {
         LocationTimeZoneProvider targetProvider;
-        if (Objects.equals(mPrimaryProvider.getName(), providerName)) {
+        if (providerIndex == 0) {
             targetProvider = mPrimaryProvider;
-        } else if (Objects.equals(mSecondaryProvider.getName(), providerName)) {
+        } else if (providerIndex == 1) {
             targetProvider = mSecondaryProvider;
         } else {
-            warnLog("Bad providerName=" + providerName);
+            warnLog("Bad providerIndex=" + providerIndex);
             targetProvider = null;
         }
         return targetProvider;
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index ca4a640..d8d44d4 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -16,13 +16,12 @@
 
 package com.android.server.timezonedetector.location;
 
-import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
-import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
 import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
 
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_DISABLED;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_SIMULATED;
+
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -37,12 +36,12 @@
 import android.util.Log;
 import android.util.Slog;
 
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
+import com.android.server.timezonedetector.Dumpable;
 import com.android.server.timezonedetector.ServiceConfigAccessor;
 import com.android.server.timezonedetector.TimeZoneDetectorInternal;
 import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
@@ -77,15 +76,7 @@
  * bound (ensuring no real location events will be received) and simulated events / behaviors
  * can be injected via the command line.
  *
- * <p>To enter simulation mode for a provider, use {@code adb shell cmd location_time_zone_manager
- * set_provider_mode_override &lt;provider name&gt; simulated} and restart the service with {@code
- * adb shell cmd location_time_zone_manager stop} and {@code adb shell cmd
- * location_time_zone_manager start}.
- *
- * <p>e.g. {@code adb shell cmd location_time_zone_manager set_provider_mode_override primary
- * simulated}.
- *
- * <p>See {@code adb shell cmd location_time_zone_manager help}" for more options.
+ * <p>See {@code adb shell cmd location_time_zone_manager help}" for details and more options.
  */
 public class LocationTimeZoneManagerService extends Binder {
 
@@ -139,11 +130,15 @@
 
     private static final String ATTRIBUTION_TAG = "LocationTimeZoneService";
 
-    private static final String PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
-            TimeZoneProviderService.PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE;
-    private static final String SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
-            TimeZoneProviderService.SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE;
+    @GuardedBy("mSharedLock")
+    private final ProviderConfig mPrimaryProviderConfig = new ProviderConfig(
+            0 /* index */, "primary",
+            TimeZoneProviderService.PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE);
 
+    @GuardedBy("mSharedLock")
+    private final ProviderConfig mSecondaryProviderConfig = new ProviderConfig(
+            1 /* index */, "secondary",
+            TimeZoneProviderService.SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE);
 
     @NonNull private final Context mContext;
 
@@ -173,14 +168,6 @@
     @GuardedBy("mSharedLock")
     private ControllerEnvironmentImpl mEnvironment;
 
-    @GuardedBy("mSharedLock")
-    @NonNull
-    private String mPrimaryProviderModeOverride = PROVIDER_MODE_OVERRIDE_NONE;
-
-    @GuardedBy("mSharedLock")
-    @NonNull
-    private String mSecondaryProviderModeOverride = PROVIDER_MODE_OVERRIDE_NONE;
-
     LocationTimeZoneManagerService(Context context) {
         mContext = context.createAttributionContext(ATTRIBUTION_TAG);
         mHandler = FgThread.getHandler();
@@ -208,9 +195,14 @@
         mThreadingDomain.assertCurrentThread();
 
         synchronized (mSharedLock) {
-            // Stop and start the service, waiting until completion.
-            stopOnDomainThread();
-            startOnDomainThread();
+            // Avoid starting the service if it is currently stopped. This is required because
+            // server flags are used by tests to set behavior with the service stopped, and we don't
+            // want the service being restarted after each flag is set.
+            if (mLocationTimeZoneDetectorController != null) {
+                // Stop and start the service, waiting until completion.
+                stopOnDomainThread();
+                startOnDomainThread();
+            }
         }
     }
 
@@ -265,8 +257,8 @@
             }
 
             if (mLocationTimeZoneDetectorController == null) {
-                LocationTimeZoneProvider primary = createPrimaryProvider();
-                LocationTimeZoneProvider secondary = createSecondaryProvider();
+                LocationTimeZoneProvider primary = mPrimaryProviderConfig.createProvider();
+                LocationTimeZoneProvider secondary = mSecondaryProviderConfig.createProvider();
 
                 ControllerImpl controller =
                         new ControllerImpl(mThreadingDomain, primary, secondary);
@@ -281,88 +273,6 @@
         }
     }
 
-    @NonNull
-    private LocationTimeZoneProvider createPrimaryProvider() {
-        LocationTimeZoneProviderProxy proxy;
-        if (isProviderInSimulationMode(PRIMARY_PROVIDER_NAME)) {
-            proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
-        } else if (!isProviderEnabled(PRIMARY_PROVIDER_NAME)) {
-            proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
-        } else {
-            proxy = new RealLocationTimeZoneProviderProxy(
-                    mContext,
-                    mHandler,
-                    mThreadingDomain,
-                    PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
-                    R.bool.config_enablePrimaryLocationTimeZoneOverlay,
-                    R.string.config_primaryLocationTimeZoneProviderPackageName
-            );
-        }
-        ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(0);
-        return new BinderLocationTimeZoneProvider(
-                providerMetricsLogger, mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
-    }
-
-    @NonNull
-    private LocationTimeZoneProvider createSecondaryProvider() {
-        LocationTimeZoneProviderProxy proxy;
-        if (isProviderInSimulationMode(SECONDARY_PROVIDER_NAME)) {
-            proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
-        } else if (!isProviderEnabled(SECONDARY_PROVIDER_NAME)) {
-            proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
-        } else {
-            proxy = new RealLocationTimeZoneProviderProxy(
-                    mContext,
-                    mHandler,
-                    mThreadingDomain,
-                    SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
-                    R.bool.config_enableSecondaryLocationTimeZoneOverlay,
-                    R.string.config_secondaryLocationTimeZoneProviderPackageName
-            );
-        }
-        ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(1);
-        return new BinderLocationTimeZoneProvider(
-                providerMetricsLogger, mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
-    }
-
-    /** Used for bug triage and in tests to simulate provider events. */
-    private boolean isProviderInSimulationMode(@NonNull String providerName) {
-        return isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_SIMULATED);
-    }
-
-    /** Used for bug triage, and by tests and experiments to remove a provider. */
-    private boolean isProviderEnabled(@NonNull String providerName) {
-        if (isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED)) {
-            return false;
-        }
-
-        switch (providerName) {
-            case PRIMARY_PROVIDER_NAME: {
-                return mServiceConfigAccessor.isPrimaryLocationTimeZoneProviderEnabled();
-            }
-            case SECONDARY_PROVIDER_NAME: {
-                return mServiceConfigAccessor.isSecondaryLocationTimeZoneProviderEnabled();
-            }
-            default: {
-                throw new IllegalArgumentException(providerName);
-            }
-        }
-    }
-
-    private boolean isProviderModeOverrideSet(@NonNull String providerName, @NonNull String mode) {
-        switch (providerName) {
-            case PRIMARY_PROVIDER_NAME: {
-                return Objects.equals(mPrimaryProviderModeOverride, mode);
-            }
-            case SECONDARY_PROVIDER_NAME: {
-                return Objects.equals(mSecondaryProviderModeOverride, mode);
-            }
-            default: {
-                throw new IllegalArgumentException(providerName);
-            }
-        }
-    }
-
     /**
      * Stops the service for tests and other rare cases. To avoid tests needing to sleep, this
      * method will not return until all the system server components have stopped.
@@ -398,33 +308,6 @@
     }
 
     /** Sets this service into provider state recording mode for tests. */
-    void setProviderModeOverride(@NonNull String providerName, @NonNull String mode) {
-        enforceManageTimeZoneDetectorPermission();
-
-        Preconditions.checkArgument(
-                PRIMARY_PROVIDER_NAME.equals(providerName)
-                        || SECONDARY_PROVIDER_NAME.equals(providerName));
-        Preconditions.checkArgument(PROVIDER_MODE_OVERRIDE_DISABLED.equals(mode)
-                || PROVIDER_MODE_OVERRIDE_SIMULATED.equals(mode)
-                || PROVIDER_MODE_OVERRIDE_NONE.equals(mode));
-
-        mThreadingDomain.postAndWait(() -> {
-            synchronized (mSharedLock) {
-                switch (providerName) {
-                    case PRIMARY_PROVIDER_NAME: {
-                        mPrimaryProviderModeOverride = mode;
-                        break;
-                    }
-                    case SECONDARY_PROVIDER_NAME: {
-                        mSecondaryProviderModeOverride = mode;
-                        break;
-                    }
-                }
-            }
-        }, BLOCKING_OP_WAIT_DURATION_MILLIS);
-    }
-
-    /** Sets this service into provider state recording mode for tests. */
     void setProviderStateRecordingEnabled(boolean enabled) {
         enforceManageTimeZoneDetectorPermission();
 
@@ -437,8 +320,11 @@
         }, BLOCKING_OP_WAIT_DURATION_MILLIS);
     }
 
-    /** Returns a snapshot of the current controller state for tests. */
-    @NonNull
+    /**
+     * Returns a snapshot of the current controller state for tests. Returns {@code null} if the
+     * service is stopped.
+     */
+    @Nullable
     LocationTimeZoneManagerServiceState getStateForTests() {
         enforceManageTimeZoneDetectorPermission();
 
@@ -462,8 +348,8 @@
      * Passes a {@link TestCommand} to the specified provider and waits for the response.
      */
     @NonNull
-    Bundle handleProviderTestCommand(
-            @NonNull String providerName, @NonNull TestCommand testCommand) {
+    Bundle handleProviderTestCommand(@IntRange(from = 0, to = 1) int providerIndex,
+            @NonNull TestCommand testCommand) {
         enforceManageTimeZoneDetectorPermission();
 
         // Because this method blocks and posts work to the threading domain thread, it would cause
@@ -484,7 +370,7 @@
                     return;
                 }
                 mLocationTimeZoneDetectorController.handleProviderTestCommand(
-                        providerName, testCommand, remoteCallback);
+                        providerIndex, testCommand, remoteCallback);
             }
         });
 
@@ -510,6 +396,17 @@
         synchronized (mSharedLock) {
             ipw.println("LocationTimeZoneManagerService:");
             ipw.increaseIndent();
+
+            ipw.println("Primary provider config:");
+            ipw.increaseIndent();
+            mPrimaryProviderConfig.dump(ipw, args);
+            ipw.decreaseIndent();
+
+            ipw.println("Secondary provider config:");
+            ipw.increaseIndent();
+            mSecondaryProviderConfig.dump(ipw, args);
+            ipw.decreaseIndent();
+
             if (mLocationTimeZoneDetectorController == null) {
                 ipw.println("{Stopped}");
             } else {
@@ -546,4 +443,75 @@
                 android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION,
                 "manage time and time zone detection");
     }
+
+    /** An inner class for managing a provider's config. */
+    private final class ProviderConfig implements Dumpable {
+        @IntRange(from = 0, to = 1) private final int mIndex;
+        @NonNull private final String mName;
+        @NonNull private final String mServiceAction;
+
+        ProviderConfig(@IntRange(from = 0, to = 1) int index, @NonNull String name,
+                @NonNull String serviceAction) {
+            Preconditions.checkArgument(index >= 0 && index <= 1);
+            mIndex = index;
+            mName = Objects.requireNonNull(name);
+            mServiceAction = Objects.requireNonNull(serviceAction);
+        }
+
+        @NonNull
+        LocationTimeZoneProvider createProvider() {
+            LocationTimeZoneProviderProxy proxy = createProxy();
+            ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(mIndex);
+            return new BinderLocationTimeZoneProvider(
+                    providerMetricsLogger, mThreadingDomain, mName, proxy);
+        }
+
+        @GuardedBy("mSharedLock")
+        @Override
+        public void dump(IndentingPrintWriter ipw, String[] args) {
+            ipw.printf("getMode()=%s\n", getMode());
+            ipw.printf("getPackageName()=%s\n", getPackageName());
+        }
+
+        @NonNull
+        private LocationTimeZoneProviderProxy createProxy() {
+            String mode = getMode();
+            if (Objects.equals(mode, PROVIDER_MODE_SIMULATED)) {
+                return new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
+            } else if (Objects.equals(mode, PROVIDER_MODE_DISABLED)) {
+                return new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
+            } else {
+                // mode == PROVIDER_MODE_OVERRIDE_ENABLED (or unknown).
+                return createRealProxy();
+            }
+        }
+
+        /** Returns the mode of the provider. */
+        @NonNull
+        private String getMode() {
+            if (mIndex == 0) {
+                return mServiceConfigAccessor.getPrimaryLocationTimeZoneProviderMode();
+            } else {
+                return mServiceConfigAccessor.getSecondaryLocationTimeZoneProviderMode();
+            }
+        }
+
+        @NonNull
+        private RealLocationTimeZoneProviderProxy createRealProxy() {
+            String providerServiceAction = mServiceAction;
+            String providerPackageName = getPackageName();
+            return new RealLocationTimeZoneProviderProxy(
+                    mContext, mHandler, mThreadingDomain, providerServiceAction,
+                    providerPackageName);
+        }
+
+        @NonNull
+        private String getPackageName() {
+            if (mIndex == 0) {
+                return mServiceConfigAccessor.getPrimaryLocationTimeZoneProviderPackageName();
+            } else {
+                return mServiceConfigAccessor.getSecondaryLocationTimeZoneProviderPackageName();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
index bdf4a70..c6df624 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
@@ -16,19 +16,25 @@
 package com.android.server.timezonedetector.location;
 
 import static android.app.time.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO;
-import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
-import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
 import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_DUMP_STATE;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_RECORD_PROVIDER_STATES;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND;
-import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP;
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
 
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS;
+import static com.android.server.timedetector.ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
+import static com.android.server.timedetector.ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_DISABLED;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_ENABLED;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_SIMULATED;
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
@@ -53,16 +59,12 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
 /** Implements the shell command interface for {@link LocationTimeZoneManagerService}. */
 class LocationTimeZoneManagerShellCommand extends ShellCommand {
 
-    private static final List<String> VALID_PROVIDER_NAMES =
-            Arrays.asList(PRIMARY_PROVIDER_NAME, SECONDARY_PROVIDER_NAME);
-
     private final LocationTimeZoneManagerService mService;
 
     LocationTimeZoneManagerShellCommand(LocationTimeZoneManagerService service) {
@@ -82,9 +84,6 @@
             case SHELL_COMMAND_STOP: {
                 return runStop();
             }
-            case SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE: {
-                return runSetProviderModeOverride();
-            }
             case SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND: {
                 return runSendProviderTestCommand();
             }
@@ -110,10 +109,6 @@
         pw.println("    Starts the location_time_zone_manager, creating time zone providers.");
         pw.printf("  %s\n", SHELL_COMMAND_STOP);
         pw.println("    Stops the location_time_zone_manager, destroying time zone providers.");
-        pw.printf("  %s <provider name> <mode>\n", SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE);
-        pw.println("    Sets a provider into a test mode next time the service started.");
-        pw.printf("    Values: %s|%s|%s\n", PROVIDER_MODE_OVERRIDE_NONE,
-                PROVIDER_MODE_OVERRIDE_DISABLED, PROVIDER_MODE_OVERRIDE_SIMULATED);
         pw.printf("  %s (true|false)\n", SHELL_COMMAND_RECORD_PROVIDER_STATES);
         pw.printf("    Enables / disables provider state recording mode. See also %s. The default"
                 + " state is always \"false\".\n", SHELL_COMMAND_DUMP_STATE);
@@ -126,11 +121,11 @@
         pw.println("    Dumps Location Time Zone Manager state for tests as text or binary proto"
                 + " form.");
         pw.println("    See the LocationTimeZoneManagerServiceStateProto definition for details.");
-        pw.printf("  %s <provider name> <test command>\n",
+        pw.printf("  %s <provider index> <test command>\n",
                 SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
         pw.println("    Passes a test command to the named provider.");
         pw.println();
-        pw.printf("<provider name> = One of %s\n", VALID_PROVIDER_NAMES);
+        pw.println("<provider index> = 0 (primary), 1 (secondary)");
         pw.println();
         pw.printf("%s details:\n", SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
         pw.println();
@@ -146,6 +141,47 @@
         pw.println();
         pw.println("Test commands cannot currently be passed to real provider implementations.");
         pw.println();
+        pw.printf("This service is also affected by the following device_config flags in the"
+                + " %s namespace:\n", NAMESPACE_SYSTEM_TIME);
+        pw.printf("    %s - [default=true], only observed if the feature is enabled in config,"
+                        + "set this to false to disable the feature\n",
+                KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED);
+        pw.printf("    %s - [default=false]. Only used if the device does not have an explicit"
+                        + " 'location time zone detection enabled' setting configured [*].\n",
+                KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT);
+        pw.printf("    %s - [default=<unset>]. Used to override the device's 'location time zone"
+                        + " detection enabled' setting [*]\n",
+                KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE);
+        pw.printf("    %s - Overrides the mode of the primary provider. Values=%s|%s|%s\n",
+                KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+                PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED, PROVIDER_MODE_SIMULATED);
+        pw.printf("    %s - Overrides the mode of the secondary provider. Values=%s|%s|%s\n",
+                KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+                PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED, PROVIDER_MODE_SIMULATED);
+        pw.printf("    %s - \n",
+                KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE);
+        pw.printf("    %s - Sets the amount of time the service waits when uncertain before making"
+                        + " an 'uncertain' suggestion to the time zone detector.\n",
+                KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS);
+        pw.printf("    %s - Sets the initialization time passed to the location time zone providers"
+                        + "\n",
+                KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS);
+        pw.printf("    %s - Sets the amount of extra time added to the location time zone providers"
+                        + " initialization time\n",
+                KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS);
+        pw.println();
+        pw.println("[*] The user must still have location = on / auto time zone detection = on");
+        pw.println();
+        pw.printf("Typically, use '%s' to stop the service before setting individual"
+                + " flags and '%s' after to restart it.\n",
+                SHELL_COMMAND_STOP, SHELL_COMMAND_START);
+        pw.println();
+        pw.println("Example:");
+        pw.printf("    $ adb shell cmd device_config put %s %s %s\n",
+                NAMESPACE_SYSTEM_TIME, KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
+                "true");
+        pw.println("See adb shell cmd device_config for more information.");
+        pw.println();
     }
 
     private int runStart() {
@@ -172,21 +208,6 @@
         return 0;
     }
 
-    private int runSetProviderModeOverride() {
-        PrintWriter outPrintWriter = getOutPrintWriter();
-        try {
-            String providerName = getNextArgRequired();
-            String modeOverride = getNextArgRequired();
-            outPrintWriter.println("Setting provider mode override for " + providerName
-                    + " to " + modeOverride);
-            mService.setProviderModeOverride(providerName, modeOverride);
-        } catch (RuntimeException e) {
-            reportError(e);
-            return 1;
-        }
-        return 0;
-    }
-
     private int runRecordProviderStates() {
         PrintWriter outPrintWriter = getOutPrintWriter();
         boolean enabled;
@@ -217,6 +238,11 @@
             return 1;
         }
 
+        if (state == null) {
+            // Controller is stopped.
+            return 0;
+        }
+
         DualDumpOutputStream outputStream;
         boolean useProto = Objects.equals(DUMP_STATE_OPTION_PROTO, getNextOption());
         if (useProto) {
@@ -288,10 +314,10 @@
     private int runSendProviderTestCommand() {
         PrintWriter outPrintWriter = getOutPrintWriter();
 
-        String providerName;
+        int providerIndex;
         TestCommand testCommand;
         try {
-            providerName = validateProviderName(getNextArgRequired());
+            providerIndex = parseProviderIndex(getNextArgRequired());
             testCommand = createTestCommandFromNextShellArg();
         } catch (RuntimeException e) {
             reportError(e);
@@ -299,9 +325,9 @@
         }
 
         outPrintWriter.println("Injecting testCommand=" + testCommand
-                + " to providerName=" + providerName);
+                + " to providerIndex=" + providerIndex);
         try {
-            Bundle result = mService.handleProviderTestCommand(providerName, testCommand);
+            Bundle result = mService.handleProviderTestCommand(providerIndex, testCommand);
             outPrintWriter.println(result);
         } catch (RuntimeException e) {
             reportError(e);
@@ -321,11 +347,11 @@
         e.printStackTrace(errPrintWriter);
     }
 
-    @NonNull
-    static String validateProviderName(@NonNull String value) {
-        if (!VALID_PROVIDER_NAMES.contains(value)) {
-            throw new IllegalArgumentException("Unknown provider name=" + value);
+    private static int parseProviderIndex(@NonNull String providerIndexString) {
+        int providerIndex = Integer.parseInt(providerIndexString);
+        if (providerIndex < 0 || providerIndex > 1) {
+            throw new IllegalArgumentException(providerIndexString);
         }
-        return value;
+        return providerIndex;
     }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
index 6c3f016..b5ac712 100644
--- a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
@@ -62,15 +62,17 @@
     RealLocationTimeZoneProviderProxy(
             @NonNull Context context, @NonNull Handler handler,
             @NonNull ThreadingDomain threadingDomain, @NonNull String action,
-            int enableOverlayResId, int nonOverlayPackageResId) {
+            @NonNull String providerPackageName) {
         super(context, threadingDomain);
         mManagerProxy = null;
         mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
+
+        Objects.requireNonNull(providerPackageName);
         mServiceWatcher = ServiceWatcher.create(context,
                 handler,
                 "RealLocationTimeZoneProviderProxy",
-                new CurrentUserServiceSupplier(context, action, enableOverlayResId,
-                        nonOverlayPackageResId, BIND_TIME_ZONE_PROVIDER_SERVICE,
+                new CurrentUserServiceSupplier(context, action,
+                        providerPackageName, BIND_TIME_ZONE_PROVIDER_SERVICE,
                         INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE),
                 this);
     }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 9f25daf..15f5765 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -95,8 +95,8 @@
         "libaudioclient",
         "libbase",
         "libappfuse",
-        "libbinder",
         "libbinder_ndk",
+        "libbinder",
         "libcutils",
         "libcrypto",
         "liblog",
@@ -105,6 +105,7 @@
         "libhardware_legacy",
         "libhidlbase",
         "libmeminfo",
+        "libmemtrackproxy",
         "libmtp",
         "libnativehelper",
         "libnativewindow",
@@ -154,6 +155,7 @@
         "android.hardware.input.classifier@1.0",
         "android.hardware.ir@1.0",
         "android.hardware.light@2.0",
+        "android.hardware.memtrack-V1-ndk_platform",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
         "android.hardware.power-V1-cpp",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 6cb4a63..fe728ab 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -24,11 +24,13 @@
 #include <nativehelper/JNIHelp.h>
 
 #include <android/binder_manager.h>
+#include <android/binder_stability.h>
 #include <android/hidl/manager/1.2/IServiceManager.h>
 #include <binder/IServiceManager.h>
 #include <hidl/HidlTransportSupport.h>
 #include <incremental_service.h>
 
+#include <memtrackproxy/MemtrackProxy.h>
 #include <schedulerservice/SchedulingPolicyService.h>
 #include <sensorservice/SensorService.h>
 #include <sensorservicehidl/SensorManager.h>
@@ -83,6 +85,21 @@
     }
 }
 
+static void android_server_SystemServer_startMemtrackProxyService(JNIEnv* env,
+                                                                  jobject /* clazz */) {
+    using aidl::android::hardware::memtrack::MemtrackProxy;
+
+    const char* memtrackProxyService = "memtrack.proxy";
+
+    std::shared_ptr<MemtrackProxy> memtrack_proxy = ndk::SharedRefBase::make<MemtrackProxy>();
+    auto binder = memtrack_proxy->asBinder();
+
+    AIBinder_forceDowngradeToLocalStability(binder.get());
+
+    const binder_exception_t err = AServiceManager_addService(binder.get(), memtrackProxyService);
+    LOG_ALWAYS_FATAL_IF(err != EX_NONE, "Cannot register %s: %d", memtrackProxyService, err);
+}
+
 static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /* clazz */) {
     using ::android::frameworks::schedulerservice::V1_0::ISchedulingPolicyService;
     using ::android::frameworks::schedulerservice::V1_0::implementation::SchedulingPolicyService;
@@ -144,6 +161,8 @@
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
         {"startSensorService", "()V", (void*)android_server_SystemServer_startSensorService},
+        {"startMemtrackProxyService", "()V",
+         (void*)android_server_SystemServer_startMemtrackProxyService},
         {"startHidlServices", "()V", (void*)android_server_SystemServer_startHidlServices},
         {"initZygoteChildHeapProfiling", "()V",
          (void*)android_server_SystemServer_initZygoteChildHeapProfiling},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index a419bf8..fb0265e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -41,13 +41,13 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
-import android.util.Slog;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.UserRestrictionsUtils;
+import com.android.server.utils.Slogf;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -469,7 +469,7 @@
                     try {
                         trustAgentInfo.options.saveToXml(out);
                     } catch (XmlPullParserException e) {
-                        Slog.e(LOG_TAG, e, "Failed to save TrustAgent options");
+                        Slogf.e(LOG_TAG, e, "Failed to save TrustAgent options");
                     }
                     out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
                 }
@@ -651,7 +651,7 @@
             String tag = parser.getName();
             if (TAG_POLICIES.equals(tag)) {
                 if (shouldOverridePolicies) {
-                    Slog.d(LOG_TAG, "Overriding device admin policies from XML.");
+                    Slogf.d(LOG_TAG, "Overriding device admin policies from XML.");
                     info.readPoliciesFromXml(parser);
                 }
             } else if (TAG_PASSWORD_QUALITY.equals(tag)) {
@@ -747,14 +747,14 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     shortSupportMessage = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing text when loading short support message");
+                    Slogf.w(LOG_TAG, "Missing text when loading short support message");
                 }
             } else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) {
                 type = parser.next();
                 if (type == TypedXmlPullParser.TEXT) {
                     longSupportMessage = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing text when loading long support message");
+                    Slogf.w(LOG_TAG, "Missing text when loading long support message");
                 }
             } else if (TAG_PARENT_ADMIN.equals(tag)) {
                 Preconditions.checkState(!isParent);
@@ -767,7 +767,7 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     organizationName = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing text when loading organization name");
+                    Slogf.w(LOG_TAG, "Missing text when loading organization name");
                 }
             } else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
                 isLogoutEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false);
@@ -776,14 +776,14 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     startUserSessionMessage = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing text when loading start session message");
+                    Slogf.w(LOG_TAG, "Missing text when loading start session message");
                 }
             } else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) {
                 type = parser.next();
                 if (type == TypedXmlPullParser.TEXT) {
                     endUserSessionMessage = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing text when loading end session message");
+                    Slogf.w(LOG_TAG, "Missing text when loading end session message");
                 }
             } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
                 mCrossProfileCalendarPackages = readPackageList(parser, tag);
@@ -822,14 +822,14 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     mOrganizationId = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing Organization ID.");
+                    Slogf.w(LOG_TAG, "Missing Organization ID.");
                 }
             } else if (TAG_ENROLLMENT_SPECIFIC_ID.equals(tag)) {
                 type = parser.next();
                 if (type == TypedXmlPullParser.TEXT) {
                     mEnrollmentSpecificId = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing Enrollment-specific ID.");
+                    Slogf.w(LOG_TAG, "Missing Enrollment-specific ID.");
                 }
             } else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) {
                 mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE,
@@ -838,7 +838,7 @@
                 mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE,
                         USB_DATA_SIGNALING_ENABLED_DEFAULT);
             } else {
-                Slog.w(LOG_TAG, "Unknown admin tag: %s", tag);
+                Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag);
                 XmlUtils.skipCurrentTag(parser);
             }
         }
@@ -860,10 +860,10 @@
                 if (packageName != null) {
                     result.add(packageName);
                 } else {
-                    Slog.w(LOG_TAG, "Package name missing under %s", outerTag);
+                    Slogf.w(LOG_TAG, "Package name missing under %s", outerTag);
                 }
             } else {
-                Slog.w(LOG_TAG, "Unknown tag under %s: ", tag, outerTag);
+                Slogf.w(LOG_TAG, "Unknown tag under %s: ", tag, outerTag);
             }
         }
         return result;
@@ -884,7 +884,7 @@
             if (tag.equals(tagDAM)) {
                 result.add(parser.getAttributeValue(null, ATTR_VALUE));
             } else {
-                Slog.e(LOG_TAG, "Expected tag %s but found %s", tag, tagDAM);
+                Slogf.e(LOG_TAG, "Expected tag %s but found %s", tag, tagDAM);
             }
         }
     }
@@ -906,7 +906,7 @@
                 final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag);
                 result.put(component, trustAgentInfo);
             } else {
-                Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
+                Slogf.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
             }
         }
         return result;
@@ -926,7 +926,7 @@
             if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
                 result.options = PersistableBundle.restoreFromXml(parser);
             } else {
-                Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
+                Slogf.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
             }
         }
         return result;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
index 8027e5b..cc385c7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
@@ -35,11 +35,11 @@
 import android.security.Credentials;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
-import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.server.utils.Slogf;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -79,16 +79,16 @@
             X509Certificate cert = parseCert(certBuffer);
             pemCert = Credentials.convertToPem(cert);
         } catch (CertificateException | IOException ce) {
-            Slog.e(LOG_TAG, "Problem converting cert", ce);
+            Slogf.e(LOG_TAG, "Problem converting cert", ce);
             return null;
         }
 
         try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) {
             return keyChainConnection.getService().installCaCertificate(pemCert);
         } catch (RemoteException e) {
-            Slog.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
+            Slogf.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
         } catch (InterruptedException e1) {
-            Slog.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
+            Slogf.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
             Thread.currentThread().interrupt();
         }
         return null;
@@ -100,9 +100,9 @@
                 keyChainConnection.getService().deleteCaCertificate(aliases[i]);
             }
         } catch (RemoteException e) {
-            Slog.e(LOG_TAG, "from CaCertUninstaller: ", e);
+            Slogf.e(LOG_TAG, "from CaCertUninstaller: ", e);
         } catch (InterruptedException ie) {
-            Slog.w(LOG_TAG, "CaCertUninstaller: ", ie);
+            Slogf.w(LOG_TAG, "CaCertUninstaller: ", ie);
             Thread.currentThread().interrupt();
         }
     }
@@ -147,7 +147,7 @@
         try {
             installedCerts = getInstalledCaCertificates(userHandle);
         } catch (RemoteException | RuntimeException e) {
-            Slog.e(LOG_TAG, e, "Could not retrieve certificates from KeyChain service for user %d",
+            Slogf.e(LOG_TAG, e, "Could not retrieve certificates from KeyChain service for user %d",
                     userId);
             return;
         }
@@ -170,7 +170,7 @@
         try {
             userContext = mInjector.createContextAsUser(userHandle);
         } catch (PackageManager.NameNotFoundException e) {
-            Slog.e(LOG_TAG, e, "Create context as %s failed", userHandle);
+            Slogf.e(LOG_TAG, e, "Create context as %s failed", userHandle);
             return null;
         }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index 00e0292..8f0af91 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -27,13 +27,13 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.util.IndentingPrintWriter;
-import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.am.PersistentConnection;
 import com.android.server.appbinding.AppBindingUtils;
+import com.android.server.utils.Slogf;
 
 /**
  * Manages connections to persistent services in owner packages.
@@ -114,7 +114,8 @@
                 final ServiceInfo service = findService(packageName, userId);
                 if (service == null) {
                     if (DEBUG) {
-                        Slog.d(TAG, "Owner package %s on u%d has no service.", packageName, userId);
+                        Slogf.d(TAG, "Owner package %s on u%d has no service.", packageName,
+                                userId);
                     }
                     disconnectServiceOnUserLocked(userId, actionForLog);
                     return;
@@ -127,14 +128,14 @@
                     // would have died at this point due to a package update.  So we disconnect
                     // anyway and re-connect.
                     if (DEBUG) {
-                        Slog.d("Disconnecting from existing service connection.", packageName,
+                        Slogf.d("Disconnecting from existing service connection.", packageName,
                                 userId);
                     }
                     disconnectServiceOnUserLocked(userId, actionForLog);
                 }
 
                 if (DEBUG) {
-                    Slog.d("Owner package %s on u%d has service %s for %s", packageName, userId,
+                    Slogf.d("Owner package %s on u%d has service %s for %s", packageName, userId,
                         service.getComponentName().flattenToShortString(), actionForLog);
                 }
 
@@ -168,7 +169,7 @@
         final DevicePolicyServiceConnection conn = mConnections.get(userId);
         if (conn != null) {
             if (DEBUG) {
-                Slog.d(TAG, "Stopping service for u%d if already running for %s.", userId,
+                Slogf.d(TAG, "Stopping service for u%d if already running for %s.", userId,
                         actionForLog);
             }
             conn.unbind();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 2825eea..6de341d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -17,7 +17,8 @@
 
 import android.util.IndentingPrintWriter;
 import android.util.KeyValueListParser;
-import android.util.Slog;
+
+import com.android.server.utils.Slogf;
 
 import java.util.concurrent.TimeUnit;
 
@@ -99,7 +100,7 @@
         } catch (IllegalArgumentException e) {
             // Failed to parse the settings string, log this and move on
             // with defaults.
-            Slog.e(TAG, "Bad device policy settings: %s", settings);
+            Slogf.e(TAG, "Bad device policy settings: %s", settings);
         }
 
         long dasDiedServiceReconnectBackoffSec = parser.getLong(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 52cdce6..26c442d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -27,13 +27,13 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.IndentingPrintWriter;
-import android.util.Slog;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
+import com.android.server.utils.Slogf;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -183,7 +183,7 @@
         try {
             chooseForWrite = file.chooseForWrite();
             if (VERBOSE_LOG) {
-                Slog.v(TAG, "Storing data for user %d on %s ", policyData.mUserId, chooseForWrite);
+                Slogf.v(TAG, "Storing data for user %d on %s ", policyData.mUserId, chooseForWrite);
             }
             stream = new FileOutputStream(chooseForWrite, false);
             TypedXmlSerializer out = Xml.resolveSerializer(stream);
@@ -195,7 +195,7 @@
                         policyData.mRestrictionsProvider.flattenToString());
             }
             if (policyData.mUserSetupComplete) {
-                if (VERBOSE_LOG) Slog.v(TAG, "setting %s to true", ATTR_SETUP_COMPLETE);
+                if (VERBOSE_LOG) Slogf.v(TAG, "setting %s to true", ATTR_SETUP_COMPLETE);
                 out.attributeBoolean(null, ATTR_SETUP_COMPLETE, true);
             }
             if (policyData.mPaired) {
@@ -216,7 +216,7 @@
 
             if (policyData.mFactoryResetFlags != 0) {
                 if (VERBOSE_LOG) {
-                    Slog.v(TAG, "Storing factory reset flags for user %d: %s", policyData.mUserId,
+                    Slogf.v(TAG, "Storing factory reset flags for user %d: %s", policyData.mUserId,
                             factoryResetFlagsToString(policyData.mFactoryResetFlags));
                 }
                 out.attributeInt(null, ATTR_FACTORY_RESET_FLAGS, policyData.mFactoryResetFlags);
@@ -382,7 +382,7 @@
             file.commit();
             return true;
         } catch (XmlPullParserException | IOException e) {
-            Slog.w(TAG, e, "failed writing file %s", chooseForWrite);
+            Slogf.w(TAG, e, "failed writing file %s", chooseForWrite);
             try {
                 if (stream != null) {
                     stream.close();
@@ -404,7 +404,7 @@
             ComponentName ownerComponent) {
         FileInputStream stream = null;
         File file = journaledFile.chooseForRead();
-        if (VERBOSE_LOG) Slog.v(TAG, "Loading data for user %d from %s", policy.mUserId, file);
+        if (VERBOSE_LOG) Slogf.v(TAG, "Loading data for user %d from %s", policy.mUserId, file);
         boolean needsRewrite = false;
         try {
             stream = new FileInputStream(file);
@@ -428,7 +428,7 @@
             }
             String userSetupComplete = parser.getAttributeValue(null, ATTR_SETUP_COMPLETE);
             if (Boolean.toString(true).equals(userSetupComplete)) {
-                if (VERBOSE_LOG) Slog.v(TAG, "setting mUserSetupComplete to true");
+                if (VERBOSE_LOG) Slogf.v(TAG, "setting mUserSetupComplete to true");
                 policy.mUserSetupComplete = true;
             }
             String paired = parser.getAttributeValue(null, ATTR_DEVICE_PAIRED);
@@ -452,7 +452,7 @@
 
             policy.mFactoryResetFlags = parser.getAttributeInt(null, ATTR_FACTORY_RESET_FLAGS, 0);
             if (VERBOSE_LOG) {
-                Slog.v(TAG, "Restored factory reset flags for user %d: %s", policy.mUserId,
+                Slogf.v(TAG, "Restored factory reset flags for user %d: %s", policy.mUserId,
                         factoryResetFlagsToString(policy.mFactoryResetFlags));
             }
             policy.mFactoryResetReason = parser.getAttributeValue(null, ATTR_FACTORY_RESET_REASON);
@@ -486,7 +486,7 @@
                             policy.mAdminMap.put(ap.info.getComponent(), ap);
                         }
                     } catch (RuntimeException e) {
-                        Slog.w(TAG, e, "Failed loading admin %s", name);
+                        Slogf.w(TAG, e, "Failed loading admin %s", name);
                     }
                 } else if ("delegation".equals(tag)) {
                     // Parse delegation info.
@@ -558,7 +558,7 @@
                     policy.mAppsSuspended =
                             parser.getAttributeBoolean(null, ATTR_VALUE, false);
                 } else {
-                    Slog.w(TAG, "Unknown tag: %s", tag);
+                    Slogf.w(TAG, "Unknown tag: %s", tag);
                     XmlUtils.skipCurrentTag(parser);
                 }
             }
@@ -566,7 +566,7 @@
             // Don't be noisy, this is normal if we haven't defined any policies.
         } catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException
                 | IndexOutOfBoundsException e) {
-            Slog.w(TAG, e, "failed parsing %s", file);
+            Slogf.w(TAG, e, "failed parsing %s", file);
         }
         try {
             if (stream != null) {
@@ -590,7 +590,7 @@
                 }
             }
             if (!haveOwner) {
-                Slog.w(TAG, "Previous password owner %s no longer active; disabling",
+                Slogf.w(TAG, "Previous password owner %s no longer active; disabling",
                         mPasswordOwner);
                 mPasswordOwner = -1;
             }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 00a1786d..8e361eb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -283,7 +283,6 @@
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Pair;
-import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
@@ -333,6 +332,7 @@
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.utils.Slogf;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import com.google.android.collect.Sets;
@@ -734,7 +734,7 @@
         if (Thread.holdsLock(mLockDoNoUseDirectly)) {
             return;
         }
-        Slog.wtfStack(LOG_TAG, "Not holding DPMS lock.");
+        Slogf.wtfStack(LOG_TAG, "Not holding DPMS lock.");
     }
 
     @VisibleForTesting
@@ -843,7 +843,7 @@
             if (Intent.ACTION_BOOT_COMPLETED.equals(action)
                     || ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
                 if (VERBOSE_LOG) {
-                    Slog.v(LOG_TAG, "Sending password expiration notifications for action "
+                    Slogf.v(LOG_TAG, "Sending password expiration notifications for action "
                             + action + " for user " + userHandle);
                 }
                 mHandler.post(new Runnable() {
@@ -887,7 +887,7 @@
             } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
                 sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STOPPED, userHandle);
                 if (isManagedProfile(userHandle)) {
-                    Slog.d(LOG_TAG, "Managed profile was stopped");
+                    Slogf.d(LOG_TAG, "Managed profile was stopped");
                     updatePersonalAppsSuspension(userHandle, false /* unlocked */);
                 }
             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
@@ -897,7 +897,7 @@
                     maybeSendAdminEnabledBroadcastLocked(userHandle);
                 }
                 if (isManagedProfile(userHandle)) {
-                    Slog.d(LOG_TAG, "Managed profile became unlocked");
+                    Slogf.d(LOG_TAG, "Managed profile became unlocked");
                     final boolean suspended =
                             updatePersonalAppsSuspension(userHandle, true /* unlocked */);
                     triggerPolicyComplianceCheckIfNeeded(userHandle, suspended);
@@ -929,15 +929,15 @@
                     updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId));
                 }
             } else if (ACTION_PROFILE_OFF_DEADLINE.equals(action)) {
-                Slog.i(LOG_TAG, "Profile off deadline alarm was triggered");
+                Slogf.i(LOG_TAG, "Profile off deadline alarm was triggered");
                 final int userId = getManagedUserId(UserHandle.USER_SYSTEM);
                 if (userId >= 0) {
                     updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId));
                 } else {
-                    Slog.wtf(LOG_TAG, "Got deadline alarm for nonexistent profile");
+                    Slogf.wtf(LOG_TAG, "Got deadline alarm for nonexistent profile");
                 }
             } else if (ACTION_TURN_PROFILE_ON_NOTIFICATION.equals(action)) {
-                Slog.i(LOG_TAG, "requesting to turn on the profile: " + userHandle);
+                Slogf.i(LOG_TAG, "requesting to turn on the profile: " + userHandle);
                 mUserManager.requestQuietModeEnabled(false, UserHandle.of(userHandle));
             }
         }
@@ -986,7 +986,7 @@
 
                 // Always reset filters on the parent user, which handles cross profile intent
                 // filters between the parent and its profiles.
-                Slog.i(LOG_TAG, "Resetting cross-profile intent filters on restriction "
+                Slogf.i(LOG_TAG, "Resetting cross-profile intent filters on restriction "
                         + "change");
                 mDpms.resetDefaultCrossProfileIntentFilters(parentId);
                 mContext.sendBroadcastAsUser(new Intent(
@@ -1007,7 +1007,7 @@
     private void handlePackagesChanged(@Nullable String packageName, int userHandle) {
         boolean removedAdmin = false;
         if (VERBOSE_LOG) {
-            Slog.d(LOG_TAG, "Handling package changes package " + packageName
+            Slogf.d(LOG_TAG, "Handling package changes package " + packageName
                     + " for user " + userHandle);
         }
         DevicePolicyData policy = getUserData(userHandle);
@@ -1077,7 +1077,7 @@
                     service.removeCredentialManagementApp();
                 }
             } catch (RemoteException | InterruptedException | IllegalStateException e) {
-                Slog.e(LOG_TAG, "Unable to remove the credential management app");
+                Slogf.e(LOG_TAG, "Unable to remove the credential management app");
             }
         });
     }
@@ -1103,7 +1103,7 @@
         // Check if package is considered not suspendable?
         if (mInjector.getPackageManager(userHandle)
                 .getUnsuspendablePackages(packagesToSuspend).length != 0) {
-            Slog.i(LOG_TAG, "Newly installed package is unsuspendable: " + packageName);
+            Slogf.i(LOG_TAG, "Newly installed package is unsuspendable: " + packageName);
             return;
         }
         try {
@@ -1127,7 +1127,7 @@
      * Used by {@code setDevicePolicySafetyChecker()} above and {@link OneTimeSafetyChecker}.
      */
     void setDevicePolicySafetyCheckerUnchecked(DevicePolicySafetyChecker safetyChecker) {
-        Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as %s", safetyChecker);
+        Slogf.i(LOG_TAG, "Setting DevicePolicySafetyChecker as %s", safetyChecker);
         mSafetyChecker = safetyChecker;
         mInjector.setDevicePolicySafetyChecker(safetyChecker);
     }
@@ -1170,7 +1170,7 @@
             @OperationSafetyReason int reason) {
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
-        Slog.i(LOG_TAG, "setNextOperationSafety(%s, %s)",
+        Slogf.i(LOG_TAG, "setNextOperationSafety(%s, %s)",
                 DevicePolicyManager.operationToString(operation),
                 DevicePolicyManager.operationSafetyReasonToString(reason));
         mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason);
@@ -1179,7 +1179,7 @@
     @Override
     public boolean isSafeOperation(@OperationSafetyReason int reason) {
         if (VERBOSE_LOG) {
-            Slog.v(LOG_TAG, "checking isSafeOperation(%s) using mSafetyChecker %s",
+            Slogf.v(LOG_TAG, "checking isSafeOperation(%s) using mSafetyChecker %s",
                     DevicePolicyManager.operationSafetyReasonToString(reason), mSafetyChecker);
         }
         return mSafetyChecker == null ? true : mSafetyChecker.isSafeOperation(reason);
@@ -1758,7 +1758,7 @@
     void removeUserData(int userHandle) {
         synchronized (getLockObject()) {
             if (userHandle == UserHandle.USER_SYSTEM) {
-                Slog.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring.");
+                Slogf.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring.");
                 return;
             }
             updatePasswordQualityCacheForUserGroup(userHandle);
@@ -1774,7 +1774,7 @@
             File policyFile = new File(mInjector.environmentGetUserSystemDirectory(userHandle),
                     DEVICE_POLICIES_XML);
             policyFile.delete();
-            Slog.i(LOG_TAG, "Removed device policy file " + policyFile.getAbsolutePath());
+            Slogf.i(LOG_TAG, "Removed device policy file " + policyFile.getAbsolutePath());
         }
     }
 
@@ -1872,64 +1872,64 @@
      */
     @GuardedBy("getLockObject()")
     private void migrateToProfileOnOrganizationOwnedDeviceIfCompLocked() {
-        if (VERBOSE_LOG) Slog.d(LOG_TAG, "Checking whether we need to migrate COMP ");
+        if (VERBOSE_LOG) Slogf.d(LOG_TAG, "Checking whether we need to migrate COMP ");
         final int doUserId = mOwners.getDeviceOwnerUserId();
         if (doUserId == UserHandle.USER_NULL) {
-            if (VERBOSE_LOG) Slog.d(LOG_TAG, "No DO found, skipping migration.");
+            if (VERBOSE_LOG) Slogf.d(LOG_TAG, "No DO found, skipping migration.");
             return;
         }
 
         final List<UserInfo> profiles = mUserManager.getProfiles(doUserId);
         if (profiles.size() != 2) {
             if (profiles.size() == 1) {
-                if (VERBOSE_LOG) Slog.d(LOG_TAG, "Profile not found, skipping migration.");
+                if (VERBOSE_LOG) Slogf.d(LOG_TAG, "Profile not found, skipping migration.");
             } else {
-                Slog.wtf(LOG_TAG, "Found " + profiles.size() + " profiles, skipping migration");
+                Slogf.wtf(LOG_TAG, "Found " + profiles.size() + " profiles, skipping migration");
             }
             return;
         }
 
         final int poUserId = getManagedUserId(doUserId);
         if (poUserId < 0) {
-            Slog.wtf(LOG_TAG, "Found DO and a profile, but it is not managed, skipping migration");
+            Slogf.wtf(LOG_TAG, "Found DO and a profile, but it is not managed, skipping migration");
             return;
         }
 
         final ActiveAdmin doAdmin = getDeviceOwnerAdminLocked();
         final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(poUserId);
         if (doAdmin == null || poAdmin == null) {
-            Slog.wtf(LOG_TAG, "Failed to get either PO or DO admin, aborting migration.");
+            Slogf.wtf(LOG_TAG, "Failed to get either PO or DO admin, aborting migration.");
             return;
         }
 
         final ComponentName doAdminComponent = mOwners.getDeviceOwnerComponent();
         final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(poUserId);
         if (doAdminComponent == null || poAdminComponent == null) {
-            Slog.wtf(LOG_TAG, "Cannot find PO or DO component name, aborting migration.");
+            Slogf.wtf(LOG_TAG, "Cannot find PO or DO component name, aborting migration.");
             return;
         }
         if (!doAdminComponent.getPackageName().equals(poAdminComponent.getPackageName())) {
-            Slog.e(LOG_TAG, "DO and PO are different packages, aborting migration.");
+            Slogf.e(LOG_TAG, "DO and PO are different packages, aborting migration.");
             return;
         }
 
-        Slog.i(LOG_TAG, "Migrating COMP to PO on a corp owned device; primary user: %d; "
+        Slogf.i(LOG_TAG, "Migrating COMP to PO on a corp owned device; primary user: %d; "
                 + "profile: %d", doUserId, poUserId);
 
-        Slog.i(LOG_TAG, "Giving the PO additional power...");
+        Slogf.i(LOG_TAG, "Giving the PO additional power...");
         markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId);
-        Slog.i(LOG_TAG, "Migrating DO policies to PO...");
+        Slogf.i(LOG_TAG, "Migrating DO policies to PO...");
         moveDoPoliciesToProfileParentAdminLocked(doAdmin, poAdmin.getParentActiveAdmin());
         migratePersonalAppSuspensionLocked(doUserId, poUserId, poAdmin);
         saveSettingsLocked(poUserId);
-        Slog.i(LOG_TAG, "Clearing the DO...");
+        Slogf.i(LOG_TAG, "Clearing the DO...");
         final ComponentName doAdminReceiver = doAdmin.info.getComponent();
         clearDeviceOwnerLocked(doAdmin, doUserId);
-        Slog.i(LOG_TAG, "Removing admin artifacts...");
+        Slogf.i(LOG_TAG, "Removing admin artifacts...");
         removeAdminArtifacts(doAdminReceiver, doUserId);
-        Slog.i(LOG_TAG, "Uninstalling the DO...");
+        Slogf.i(LOG_TAG, "Uninstalling the DO...");
         uninstallOrDisablePackage(doAdminComponent.getPackageName(), doUserId);
-        Slog.i(LOG_TAG, "Migration complete.");
+        Slogf.i(LOG_TAG, "Migration complete.");
 
         // Note: KeyChain keys are not removed and will remain accessible for the apps that have
         // been given grants to use them.
@@ -1945,16 +1945,16 @@
             int doUserId, int poUserId, ActiveAdmin poAdmin) {
         final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
         if (!pmi.isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, doUserId)) {
-            Slog.i(LOG_TAG, "DO is not suspending any apps.");
+            Slogf.i(LOG_TAG, "DO is not suspending any apps.");
             return;
         }
 
         if (getTargetSdk(poAdmin.info.getPackageName(), poUserId) >= Build.VERSION_CODES.R) {
-            Slog.i(LOG_TAG, "PO is targeting R+, keeping personal apps suspended.");
+            Slogf.i(LOG_TAG, "PO is targeting R+, keeping personal apps suspended.");
             getUserData(doUserId).mAppsSuspended = true;
             poAdmin.mSuspendPersonalApps = true;
         } else {
-            Slog.i(LOG_TAG, "PO isn't targeting R+, unsuspending personal apps.");
+            Slogf.i(LOG_TAG, "PO isn't targeting R+, unsuspending personal apps.");
             pmi.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, doUserId);
         }
     }
@@ -1969,11 +1969,11 @@
             return;
         }
         if (appInfo == null) {
-            Slog.wtf(LOG_TAG, "Failed to get package info for " + packageName);
+            Slogf.wtf(LOG_TAG, "Failed to get package info for " + packageName);
             return;
         }
         if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-            Slog.i(LOG_TAG, "Package %s is pre-installed, marking disabled until used",
+            Slogf.i(LOG_TAG, "Package %s is pre-installed, marking disabled until used",
                     packageName);
             mContext.getPackageManager().setApplicationEnabledSetting(packageName,
                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0);
@@ -1987,9 +1987,9 @@
                 final int status = intent.getIntExtra(
                         PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
                 if (status == PackageInstaller.STATUS_SUCCESS) {
-                    Slog.i(LOG_TAG, "Package %s uninstalled for user %d", packageName, userId);
+                    Slogf.i(LOG_TAG, "Package %s uninstalled for user %d", packageName, userId);
                 } else {
-                    Slog.e(LOG_TAG, "Failed to uninstall %s; status: %d", packageName, status);
+                    Slogf.e(LOG_TAG, "Failed to uninstall %s; status: %d", packageName, status);
                 }
             }
         };
@@ -2057,7 +2057,7 @@
     private void applyManagedProfileRestrictionIfDeviceOwnerLocked() {
         final int doUserId = mOwners.getDeviceOwnerUserId();
         if (doUserId == UserHandle.USER_NULL) {
-            if (VERBOSE_LOG) Slog.d(LOG_TAG, "No DO found, skipping application of restriction.");
+            if (VERBOSE_LOG) Slogf.d(LOG_TAG, "No DO found, skipping application of restriction.");
             return;
         }
 
@@ -2114,10 +2114,10 @@
         if (defaultRestrictions.equals(admin.defaultEnabledRestrictionsAlreadySet)) {
             return; // The same set of default restrictions has been already applied.
         }
-        Slog.i(LOG_TAG, "New user restrictions need to be set by default for user " + userId);
+        Slogf.i(LOG_TAG, "New user restrictions need to be set by default for user " + userId);
 
         if (VERBOSE_LOG) {
-            Slog.d(LOG_TAG,"Default enabled restrictions: "
+            Slogf.d(LOG_TAG, "Default enabled restrictions: "
                     + defaultRestrictions
                     + ". Restrictions already enabled: "
                     + admin.defaultEnabledRestrictionsAlreadySet);
@@ -2130,7 +2130,7 @@
                 admin.ensureUserRestrictions().putBoolean(restriction, true);
             }
             admin.defaultEnabledRestrictionsAlreadySet.addAll(restrictionsToSet);
-            Slog.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictionsToSet);
+            Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictionsToSet);
             saveUserRestrictionsLocked(userId);
         }
     }
@@ -2154,10 +2154,10 @@
         final String value = Boolean.toString(hasDeviceOwner || hasOrgOwnedProfile);
         final String currentVal = mInjector.systemPropertiesGet(PROPERTY_ORGANIZATION_OWNED, null);
         if (TextUtils.isEmpty(currentVal)) {
-            Slog.i(LOG_TAG, "Set ro.organization_owned property to " + value);
+            Slogf.i(LOG_TAG, "Set ro.organization_owned property to " + value);
             mInjector.systemPropertiesSet(PROPERTY_ORGANIZATION_OWNED, value);
         } else if (!value.equals(currentVal)) {
-            Slog.w(LOG_TAG, "Cannot change existing ro.organization_owned to " + value);
+            Slogf.w(LOG_TAG, "Cannot change existing ro.organization_owned to " + value);
         }
     }
 
@@ -2185,7 +2185,7 @@
                 doComponentName.getPackageName(),
                 mOwners.getDeviceOwnerUserId());
         if (doComponent == null) {
-            Slog.e(LOG_TAG, "Device-owner isn't registered as device-admin");
+            Slogf.e(LOG_TAG, "Device-owner isn't registered as device-admin");
         } else {
             mOwners.setDeviceOwnerWithRestrictionsMigrated(
                     doComponent,
@@ -2194,7 +2194,7 @@
                     !mOwners.getDeviceOwnerUserRestrictionsNeedsMigration());
             mOwners.writeDeviceOwner();
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "Device owner component filled in");
+                Slogf.v(LOG_TAG, "Device owner component filled in");
             }
         }
     }
@@ -2209,7 +2209,7 @@
         // except for the "system controlled" ones.
         if (mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()) {
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "Migrating DO user restrictions");
+                Slogf.v(LOG_TAG, "Migrating DO user restrictions");
             }
             migrated = true;
 
@@ -2237,7 +2237,7 @@
             final int userId = ui.id;
             if (mOwners.getProfileOwnerUserRestrictionsNeedsMigration(userId)) {
                 if (VERBOSE_LOG) {
-                    Slog.v(LOG_TAG, "Migrating PO user restrictions for user %d", userId);
+                    Slogf.v(LOG_TAG, "Migrating PO user restrictions for user %d", userId);
                 }
                 migrated = true;
 
@@ -2260,7 +2260,7 @@
             }
         }
         if (VERBOSE_LOG && migrated) {
-            Slog.v(LOG_TAG, "User restrictions migrated.");
+            Slogf.v(LOG_TAG, "User restrictions migrated.");
         }
     }
 
@@ -2288,9 +2288,9 @@
         }
 
         if (VERBOSE_LOG) {
-            Slog.v(LOG_TAG, "origRestrictions=%s", origRestrictions);
-            Slog.v(LOG_TAG, "newBaseRestrictions=%s", newBaseRestrictions);
-            Slog.v(LOG_TAG, "newOwnerRestrictions=%s", newOwnerRestrictions);
+            Slogf.v(LOG_TAG, "origRestrictions=%s", origRestrictions);
+            Slogf.v(LOG_TAG, "newBaseRestrictions=%s", newBaseRestrictions);
+            Slogf.v(LOG_TAG, "newOwnerRestrictions=%s", newOwnerRestrictions);
         }
         mUserManagerInternal.setBaseUserRestrictionsByDpmsForMigration(user.getIdentifier(),
                 newBaseRestrictions);
@@ -2299,7 +2299,7 @@
             admin.ensureUserRestrictions().clear();
             admin.ensureUserRestrictions().putAll(newOwnerRestrictions);
         } else {
-            Slog.w(LOG_TAG, "ActiveAdmin for DO/PO not found. user=" + user.getIdentifier());
+            Slogf.w(LOG_TAG, "ActiveAdmin for DO/PO not found. user=" + user.getIdentifier());
         }
         saveSettingsLocked(user.getIdentifier());
     }
@@ -2320,7 +2320,7 @@
             }
         }
         if (nFound > 1) {
-            Slog.w(LOG_TAG, "Multiple DA found; assume the first one is DO.");
+            Slogf.w(LOG_TAG, "Multiple DA found; assume the first one is DO.");
         }
         return found;
     }
@@ -2760,7 +2760,7 @@
         if (!permission.BIND_DEVICE_ADMIN.equals(ai.permission)) {
             final String message = "DeviceAdminReceiver " + adminName + " must be protected with "
                     + permission.BIND_DEVICE_ADMIN;
-            Slog.w(LOG_TAG, message);
+            Slogf.w(LOG_TAG, message);
             if (throwForMissingPermission &&
                     ai.applicationInfo.targetSdkVersion > Build.VERSION_CODES.M) {
                 throw new IllegalArgumentException(message);
@@ -2770,7 +2770,7 @@
         try {
             return new DeviceAdminInfo(mContext, ai);
         } catch (XmlPullParserException | IOException e) {
-            Slog.w(LOG_TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName,
+            Slogf.w(LOG_TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName,
                     e);
             return null;
         }
@@ -2785,7 +2785,7 @@
     private JournaledFile makeJournaledFile(@UserIdInt int userId, String fileName) {
         final String base = new File(getPolicyFileDirectory(userId), fileName)
                 .getAbsolutePath();
-        if (VERBOSE_LOG) Slog.v(LOG_TAG, "Opening %s", base);
+        if (VERBOSE_LOG) Slogf.v(LOG_TAG, "Opening %s", base);
         return new JournaledFile(new File(base), new File(base + ".tmp"));
     }
 
@@ -3033,7 +3033,7 @@
         if (!mTransferOwnershipMetadataManager.metadataFileExists()) {
             return;
         }
-        Slog.e(LOG_TAG, "Owner transfer metadata file exists! Reverting transfer.");
+        Slogf.e(LOG_TAG, "Owner transfer metadata file exists! Reverting transfer.");
         final TransferOwnershipMetadataManager.Metadata metadata =
                 mTransferOwnershipMetadataManager.loadMetadataFile();
         // Revert transfer
@@ -3079,7 +3079,7 @@
                 // STOPSHIP Prevent the DO user from being killed.
 
             } catch (RemoteException e) {
-                Slog.w(LOG_TAG, "Exception starting user", e);
+                Slogf.w(LOG_TAG, "Exception starting user", e);
             }
         }
     }
@@ -3512,7 +3512,7 @@
             }
             // Remove the admin skipping sending the broadcast.
             removeAdminArtifacts(adminReceiver, userHandle);
-            Slog.i(LOG_TAG, "Admin " + adminReceiver + " removed from user " + userHandle);
+            Slogf.i(LOG_TAG, "Admin " + adminReceiver + " removed from user " + userHandle);
         });
     }
 
@@ -3584,8 +3584,8 @@
             // Active device/profile owners must remain active admins.
             if (isDeviceOwner(adminReceiver, userHandle)
                     || isProfileOwner(adminReceiver, userHandle)) {
-                Slog.e(LOG_TAG, "Device/profile owner cannot be removed: component=" +
-                        adminReceiver);
+                Slogf.e(LOG_TAG, "Device/profile owner cannot be removed: component="
+                        + adminReceiver);
                 return;
             }
             if (admin.getUid() != mInjector.binderGetCallingUid()) {
@@ -3850,7 +3850,7 @@
                         }
                     }
                 } else {
-                    Slog.w(LOG_TAG, "Unknown user type: " + userInfo);
+                    Slogf.w(LOG_TAG, "Unknown user type: " + userInfo);
                 }
             }
         });
@@ -3959,7 +3959,7 @@
             ap.passwordExpirationDate = expiration;
             ap.passwordExpirationTimeout = timeout;
             if (timeout > 0L) {
-                Slog.w(LOG_TAG, "setPasswordExpiration(): password will expire on "
+                Slogf.w(LOG_TAG, "setPasswordExpiration(): password will expire on "
                         + DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)
                         .format(new Date(expiration)));
             }
@@ -4883,7 +4883,7 @@
     @Override
     public boolean resetPassword(@Nullable String password, int flags) throws RemoteException {
         if (!mLockPatternUtils.hasSecureLockScreen()) {
-            Slog.w(LOG_TAG, "Cannot reset password when the device has no lock screen");
+            Slogf.w(LOG_TAG, "Cannot reset password when the device has no lock screen");
             return false;
         }
         if (password == null) password = "";
@@ -4901,7 +4901,7 @@
             synchronized (getLockObject()) {
                 ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
                 if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) {
-                    Slog.e(LOG_TAG, "DPC can no longer call resetPassword()");
+                    Slogf.e(LOG_TAG, "DPC can no longer call resetPassword()");
                     return false;
                 }
                 throw new SecurityException("Device admin can no longer call resetPassword()");
@@ -4917,7 +4917,7 @@
                     "Unauthorized caller cannot call resetPassword.");
             if (getTargetSdk(admin.info.getPackageName(),
                     userHandle) <= android.os.Build.VERSION_CODES.M) {
-                Slog.e(LOG_TAG, "Device admin can no longer call resetPassword()");
+                Slogf.e(LOG_TAG, "Device admin can no longer call resetPassword()");
                 return false;
             }
             throw new SecurityException("Device admin can no longer call resetPassword()");
@@ -4944,7 +4944,7 @@
             }
 
             if (!validationErrors.isEmpty()) {
-                Slog.w(LOG_TAG, "Failed to reset password due to constraint violation: %s",
+                Slogf.w(LOG_TAG, "Failed to reset password due to constraint violation: %s",
                         validationErrors.get(0));
                 return false;
             }
@@ -4952,7 +4952,7 @@
 
         DevicePolicyData policy = getUserData(userHandle);
         if (policy.mPasswordOwner >= 0 && policy.mPasswordOwner != callingUid) {
-            Slog.w(LOG_TAG, "resetPassword: already set by another uid and not entered by user");
+            Slogf.w(LOG_TAG, "resetPassword: already set by another uid and not entered by user");
             return false;
         }
 
@@ -5266,7 +5266,7 @@
                 if (userToLock == UserHandle.USER_ALL) {
                     if (mIsAutomotive) {
                         if (VERBOSE_LOG) {
-                            Slog.v(LOG_TAG, "lockNow(): not powering off display on automotive"
+                            Slogf.v(LOG_TAG, "lockNow(): not powering off display on automotive"
                                     + " build");
                         }
                     } else {
@@ -5373,7 +5373,7 @@
         });
 
         if (alias == null) {
-            Slog.w(LOG_TAG, "Problem installing cert");
+            Slogf.w(LOG_TAG, "Problem installing cert");
             return false;
         }
 
@@ -5446,12 +5446,12 @@
                         .write();
                 return true;
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Installing certificate", e);
+                Slogf.e(LOG_TAG, "Installing certificate", e);
             } finally {
                 keyChainConnection.close();
             }
         } catch (InterruptedException e) {
-            Slog.w(LOG_TAG, "Interrupted while installing certificate", e);
+            Slogf.w(LOG_TAG, "Interrupted while installing certificate", e);
             Thread.currentThread().interrupt();
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
@@ -5496,12 +5496,12 @@
                         .write();
                 return keyChain.removeKeyPair(alias);
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Removing keypair", e);
+                Slogf.e(LOG_TAG, "Removing keypair", e);
             } finally {
                 keyChainConnection.close();
             }
         } catch (InterruptedException e) {
-            Slog.w(LOG_TAG, "Interrupted while removing keypair", e);
+            Slogf.w(LOG_TAG, "Interrupted while removing keypair", e);
             Thread.currentThread().interrupt();
         } finally {
             Binder.restoreCallingIdentity(id);
@@ -5520,9 +5520,9 @@
                          KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
                 return keyChainConnection.getService().containsKeyPair(alias);
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Querying keypair", e);
+                Slogf.e(LOG_TAG, "Querying keypair", e);
             } catch (InterruptedException e) {
-                Slog.w(LOG_TAG, "Interrupted while querying keypair", e);
+                Slogf.w(LOG_TAG, "Interrupted while querying keypair", e);
                 Thread.currentThread().interrupt();
             }
             return false;
@@ -5564,7 +5564,7 @@
                 }
                 return false;
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Querying grant to wifi auth.", e);
+                Slogf.e(LOG_TAG, "Querying grant to wifi auth.", e);
                 return false;
             }
         });
@@ -5605,11 +5605,11 @@
                 keyChain.setGrant(granteeUid, alias, hasGrant);
                 return true;
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Setting grant for package.", e);
+                Slogf.e(LOG_TAG, "Setting grant for package.", e);
                 return false;
             }
         } catch (InterruptedException e) {
-            Slog.w(LOG_TAG, "Interrupted while setting key grant", e);
+            Slogf.w(LOG_TAG, "Interrupted while setting key grant", e);
             Thread.currentThread().interrupt();
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
@@ -5636,7 +5636,7 @@
                 for (final int uid : granteeUids) {
                     final String[] packages = pm.getPackagesForUid(uid);
                     if (packages == null) {
-                        Slog.wtf(LOG_TAG, "No packages found for uid " + uid);
+                        Slogf.wtf(LOG_TAG, "No packages found for uid " + uid);
                         continue;
                     }
                     if (!result.isEmpty()) {
@@ -5646,9 +5646,9 @@
                 }
                 return result;
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Querying keypair grants", e);
+                Slogf.e(LOG_TAG, "Querying keypair grants", e);
             } catch (InterruptedException e) {
-                Slog.w(LOG_TAG, "Interrupted while querying keypair grants", e);
+                Slogf.w(LOG_TAG, "Interrupted while querying keypair grants", e);
                 Thread.currentThread().interrupt();
             }
             return Collections.emptyList();
@@ -5773,7 +5773,7 @@
         // As the caller will be granted access to the key, ensure no UID was specified, as
         // it will not have the desired effect.
         if (keySpec.getUid() != KeyStore.UID_SELF) {
-            Slog.e(LOG_TAG, "Only the caller can be granted access to the generated keypair.");
+            Slogf.e(LOG_TAG, "Only the caller can be granted access to the generated keypair.");
             logGenerateKeyPairFailure(caller, isCredentialManagementApp);
             return false;
         }
@@ -5799,7 +5799,7 @@
                 final int generationResult = keyChain.generateKeyPair(algorithm,
                         new ParcelableKeyGenParameterSpec(keySpec));
                 if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
-                    Slog.e(LOG_TAG, "KeyChain failed to generate a keypair, error %d.",
+                    Slogf.e(LOG_TAG, "KeyChain failed to generate a keypair, error %d.",
                             generationResult);
                     logGenerateKeyPairFailure(caller, isCredentialManagementApp);
                     switch (generationResult) {
@@ -5839,7 +5839,7 @@
                     attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts));
                 } catch (CertificateException e) {
                     logGenerateKeyPairFailure(caller, isCredentialManagementApp);
-                    Slog.e(LOG_TAG, "While retrieving certificate chain.", e);
+                    Slogf.e(LOG_TAG, "While retrieving certificate chain.", e);
                     return false;
                 }
 
@@ -5854,9 +5854,9 @@
                 return true;
             }
         } catch (RemoteException e) {
-            Slog.e(LOG_TAG, "KeyChain error while generating a keypair", e);
+            Slogf.e(LOG_TAG, "KeyChain error while generating a keypair", e);
         } catch (InterruptedException e) {
-            Slog.w(LOG_TAG, "Interrupted while generating keypair", e);
+            Slogf.w(LOG_TAG, "Interrupted while generating keypair", e);
             Thread.currentThread().interrupt();
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
@@ -5914,10 +5914,10 @@
                     .write();
             return true;
         } catch (InterruptedException e) {
-            Slog.w(LOG_TAG, "Interrupted while setting keypair certificate", e);
+            Slogf.w(LOG_TAG, "Interrupted while setting keypair certificate", e);
             Thread.currentThread().interrupt();
         } catch (RemoteException e) {
-            Slog.e(LOG_TAG, "Failed setting keypair certificate", e);
+            Slogf.e(LOG_TAG, "Failed setting keypair certificate", e);
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
@@ -5991,7 +5991,7 @@
         } catch (Exception e) {
             // Caller could throw RuntimeException or RemoteException back across processes. Catch
             // everything just to be sure.
-            Slog.e(LOG_TAG, "error while responding to callback", e);
+            Slogf.e(LOG_TAG, "error while responding to callback", e);
         }
     }
 
@@ -6208,7 +6208,7 @@
         if (delegates.size() == 0) {
             return null;
         } else if (delegates.size() > 1) {
-            Slog.wtf(LOG_TAG, "More than one delegate holds " + scope);
+            Slogf.wtf(LOG_TAG, "More than one delegate holds " + scope);
             return null;
         }
         final String pkg = delegates.get(0);
@@ -6224,7 +6224,7 @@
         final int count = receivers.size();
         if (count >= 1) {
             if (count > 1) {
-                Slog.w(LOG_TAG, pkg + " defines more than one delegate receiver for " + action);
+                Slogf.w(LOG_TAG, pkg + " defines more than one delegate receiver for " + action);
             }
             return receivers.get(0).activityInfo.getComponentName();
         } else {
@@ -6352,7 +6352,7 @@
             boolean isUserSelectable) {
         // Should not be user selectable
         if (isUserSelectable) {
-            Slog.e(LOG_TAG, "The credential management app is not allowed to install a "
+            Slogf.e(LOG_TAG, "The credential management app is not allowed to install a "
                     + "user selectable key pair");
             return false;
         }
@@ -6442,7 +6442,7 @@
         final int userId = caller.getUserId();
         mInjector.binderWithCleanCallingIdentity(() -> {
             if (vpnPackage != null && !isPackageInstalledForUser(vpnPackage, userId)) {
-                Slog.w(LOG_TAG, "Non-existent VPN package specified: " + vpnPackage);
+                Slogf.w(LOG_TAG, "Non-existent VPN package specified: " + vpnPackage);
                 throw new ServiceSpecificException(
                         DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, vpnPackage);
             }
@@ -6450,7 +6450,7 @@
             if (vpnPackage != null && lockdown && lockdownAllowlist != null) {
                 for (String packageName : lockdownAllowlist) {
                     if (!isPackageInstalledForUser(packageName, userId)) {
-                        Slog.w(LOG_TAG, "Non-existent package in VPN allowlist: " + packageName);
+                        Slogf.w(LOG_TAG, "Non-existent package in VPN allowlist: " + packageName);
                         throw new ServiceSpecificException(
                                 DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, packageName);
                     }
@@ -6552,7 +6552,7 @@
                 // Persist the request so the device is automatically factory-reset on next start if
                 // the system crashes or reboots before the {@code DevicePolicySafetyChecker} calls
                 // its callback.
-                Slog.i(LOG_TAG, "Persisting factory reset request as it could be delayed by %s",
+                Slogf.i(LOG_TAG, "Persisting factory reset request as it could be delayed by %s",
                         mSafetyChecker);
                 synchronized (getLockObject()) {
                     DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
@@ -6563,7 +6563,7 @@
             }
             success = true;
         } catch (IOException | SecurityException e) {
-            Slog.w(LOG_TAG, "Failed requesting data wipe", e);
+            Slogf.w(LOG_TAG, "Failed requesting data wipe", e);
         } finally {
             if (!success) SecurityLog.writeEvent(SecurityLog.TAG_WIPE_FAILURE);
         }
@@ -6577,7 +6577,7 @@
 
             if (policy.mFactoryResetReason == null) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "no persisted reason for factory resetting");
+                Slogf.e(LOG_TAG, "no persisted reason for factory resetting");
                 policy.mFactoryResetReason = "requested before boot";
             }
             FactoryResetter factoryResetter = FactoryResetter.newBuilder(mContext)
@@ -6589,16 +6589,16 @@
                     .setWipeFactoryResetProtection((policy.mFactoryResetFlags & DevicePolicyData
                             .FACTORY_RESET_FLAG_WIPE_FACTORY_RESET_PROTECTION) != 0)
                     .build();
-            Slog.i(LOG_TAG, "Factory resetting on boot using " + factoryResetter);
+            Slogf.i(LOG_TAG, "Factory resetting on boot using " + factoryResetter);
             try {
                 if (!factoryResetter.factoryReset()) {
                     // Shouldn't happen because FactoryResetter was created without a
                     // DevicePolicySafetyChecker.
-                    Slog.wtf(LOG_TAG, "Factory reset using " + factoryResetter + " failed.");
+                    Slogf.wtf(LOG_TAG, "Factory reset using " + factoryResetter + " failed.");
                 }
             } catch (IOException e) {
                 // Shouldn't happen.
-                Slog.wtf(LOG_TAG, "Could not factory reset using " + factoryResetter, e);
+                Slogf.wtf(LOG_TAG, "Could not factory reset using " + factoryResetter, e);
             }
         }
     }
@@ -6612,7 +6612,7 @@
 
             success = mUserManagerInternal.removeUserEvenWhenDisallowed(userId);
             if (!success) {
-                Slog.w(LOG_TAG, "Couldn't remove user " + userId);
+                Slogf.w(LOG_TAG, "Couldn't remove user " + userId);
             } else if (isManagedProfile(userId) && !wipeSilently) {
                 sendWipeProfileNotification(wipeReasonForUser);
             }
@@ -6667,7 +6667,7 @@
 
         int userId = admin != null ? admin.getUserHandle().getIdentifier()
                 : caller.getUserId();
-        Slog.i(LOG_TAG, "wipeDataWithReason(%s): admin=%s, user=%d", wipeReasonForUser, admin,
+        Slogf.i(LOG_TAG, "wipeDataWithReason(%s): admin=%s, user=%d", wipeReasonForUser, admin,
                 userId);
         if (calledByProfileOwnerOnOrgOwnedDevice) {
             // When wipeData is called on the parent instance, it implies wiping the entire device.
@@ -6705,7 +6705,7 @@
         } else {
             adminComp = null;
             adminName = mInjector.getPackageManager().getPackagesForUid(caller.getUid())[0];
-            Slog.i(LOG_TAG, "Logging wipeData() event admin as " + adminName);
+            Slogf.i(LOG_TAG, "Logging wipeData() event admin as " + adminName);
             event.setAdmin(adminName);
             if (mInjector.userManagerIsHeadlessSystemUserMode()) {
                 // On headless system user mode, the call is meant to factory reset the whole
@@ -6980,7 +6980,7 @@
 
         if (wipeData && strictestAdmin != null) {
             final int userId = getUserIdToWipeForFailedPasswords(strictestAdmin);
-            Slog.i(LOG_TAG, "Max failed password attempts policy reached for admin: "
+            Slogf.i(LOG_TAG, "Max failed password attempts policy reached for admin: "
                     + strictestAdmin.info.getComponent().flattenToShortString()
                     + ". Calling wipeData for user " + userId);
 
@@ -6999,7 +6999,7 @@
                         wipeReasonForUser,
                         userId);
             } catch (SecurityException e) {
-                Slog.w(LOG_TAG, "Failed to wipe user " + userId
+                Slogf.w(LOG_TAG, "Failed to wipe user " + userId
                         + " after max failed password attempts reached.", e);
             }
         }
@@ -7133,7 +7133,7 @@
 
             // If the user is not system, don't set the global proxy. Fail silently.
             if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
-                Slog.w(LOG_TAG, "Only the owner is allowed to set the global proxy. User "
+                Slogf.w(LOG_TAG, "Only the owner is allowed to set the global proxy. User "
                         + UserHandle.getCallingUserId() + " is not permitted.");
                 return null;
             }
@@ -7226,7 +7226,7 @@
         ProxyInfo proxyProperties = ProxyInfo.buildDirectProxy(data[0], proxyPort,
                 ProxyUtils.exclusionStringAsList(exclusionList));
         if (!proxyProperties.isValid()) {
-            Slog.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString());
+            Slogf.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString());
             return;
         }
         mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]);
@@ -7253,8 +7253,8 @@
             // Check for permissions
             // Only system user can set storage encryption
             if (userHandle != UserHandle.USER_SYSTEM) {
-                Slog.w(LOG_TAG, "Only owner/system user is allowed to set storage encryption. User "
-                        + UserHandle.getCallingUserId() + " is not permitted.");
+                Slogf.w(LOG_TAG, "Only owner/system user is allowed to set storage encryption. "
+                        + "User " + UserHandle.getCallingUserId() + " is not permitted.");
                 return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
             }
 
@@ -7468,7 +7468,7 @@
             try {
                 mInjector.getIWindowManager().refreshScreenCaptureDisabled(userHandle);
             } catch (RemoteException e) {
-                Slog.w(LOG_TAG, "Unable to notify WindowManager.", e);
+                Slogf.w(LOG_TAG, "Unable to notify WindowManager.", e);
             }
         });
     }
@@ -7796,7 +7796,7 @@
             intent.putExtras(extras);
         }
         if (VERBOSE_LOG) {
-            Slog.v(LOG_TAG, "sendActiveAdminCommand(): broadcasting " + action + " to "
+            Slogf.v(LOG_TAG, "sendActiveAdminCommand(): broadcasting " + action + " to "
                     + receiverComponent.flattenToShortString() + " on user " + userId);
         }
         mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
@@ -8083,7 +8083,7 @@
      * feature will be appended to it.
      */
     private void logMissingFeatureAction(String message) {
-        Slog.w(LOG_TAG, message + " because device does not have the "
+        Slogf.w(LOG_TAG, message + " because device does not have the "
                 + PackageManager.FEATURE_DEVICE_ADMIN + " feature.");
     }
 
@@ -8150,11 +8150,11 @@
             mDeviceAdminServiceController.startServiceForOwner(
                     admin.getPackageName(), userId, "set-device-owner");
 
-            Slog.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
+            Slogf.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
 
             if (mInjector.userManagerIsHeadlessSystemUserMode()) {
                 int currentForegroundUser = getCurrentForegroundUserId();
-                Slog.i(LOG_TAG, "setDeviceOwner(): setting " + admin
+                Slogf.i(LOG_TAG, "setDeviceOwner(): setting " + admin
                         + " as profile owner on user " + currentForegroundUser);
                 // Sets profile owner on current foreground user since
                 // the human user will complete the DO setup workflow from there.
@@ -8383,7 +8383,7 @@
                 return admin;
             }
         }
-        Slog.wtf(LOG_TAG, "Active admin for device owner not found. component=" + component);
+        Slogf.wtf(LOG_TAG, "Active admin for device owner not found. component=" + component);
         return null;
     }
 
@@ -8424,7 +8424,7 @@
                 sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED,
                         deviceOwnerUserId);
             });
-            Slog.i(LOG_TAG, "Device owner removed: " + deviceOwnerComponent);
+            Slogf.i(LOG_TAG, "Device owner removed: " + deviceOwnerComponent);
         }
     }
 
@@ -8518,7 +8518,7 @@
             if (parentUserId != userHandle && mUserManager.hasUserRestriction(
                     UserManager.DISALLOW_ADD_MANAGED_PROFILE,
                     UserHandle.of(parentUserId))) {
-                Slog.i(LOG_TAG, "Cannot set profile owner because of restriction.");
+                Slogf.i(LOG_TAG, "Cannot set profile owner because of restriction.");
                 return false;
             }
 
@@ -8537,7 +8537,7 @@
 
             mOwners.setProfileOwner(who, ownerName, userHandle);
             mOwners.writeProfileOwner(userHandle);
-            Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
+            Slogf.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
 
             mInjector.binderWithCleanCallingIdentity(() -> {
                 if (mUserManager.isManagedProfile(userHandle)) {
@@ -8593,7 +8593,7 @@
                 sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED,
                         userId);
             });
-            Slog.i(LOG_TAG, "Profile owner " + who + " removed from user " + userId);
+            Slogf.i(LOG_TAG, "Profile owner " + who + " removed from user " + userId);
         }
     }
 
@@ -8806,7 +8806,7 @@
             // Check if the profile is already enabled.
             UserInfo managedProfile = getUserInfo(userId);
             if (managedProfile.isEnabled()) {
-                Slog.e(LOG_TAG,
+                Slogf.e(LOG_TAG,
                         "setProfileEnabled is called when the profile is already enabled");
                 return;
             }
@@ -9019,18 +9019,18 @@
             // thrown but null data can be returned; if the appInfo for the specified package cannot
             // be found then return false to prevent crashing the app.
             if (appInfo == null) {
-                Slog.w(LOG_TAG, "appInfo could not be found for package %s", packageName);
+                Slogf.w(LOG_TAG, "appInfo could not be found for package %s", packageName);
                 return false;
             } else if (uid != appInfo.uid) {
                 String message = String.format("Package %s (uid=%d) does not match provided uid %d",
                         packageName, appInfo.uid, uid);
-                Slog.w(LOG_TAG, message);
+                Slogf.w(LOG_TAG, message);
                 throw new SecurityException(message);
             }
         } catch (RemoteException e) {
             // If an exception is caught obtaining the appInfo just return false to prevent crashing
             // apps due to an internal error.
-            Slog.e(LOG_TAG, e, "Exception caught obtaining appInfo for package %s", packageName);
+            Slogf.e(LOG_TAG, e, "Exception caught obtaining appInfo for package %s", packageName);
             return false;
         }
         return true;
@@ -9046,7 +9046,7 @@
             String message = String.format(
                     "Calling uid %d, pid %d cannot check device identifier access for package %s "
                             + "(uid=%d, pid=%d)", callingUid, callingPid, packageName, uid, pid);
-            Slog.w(LOG_TAG, message);
+            Slogf.w(LOG_TAG, message);
             throw new SecurityException(message);
         }
     }
@@ -9062,7 +9062,7 @@
                 userContext = mContext.createPackageContextAsUser(packageName, /* flags= */ 0,
                         userHandle);
             } catch (PackageManager.NameNotFoundException nnfe) {
-                Slog.w(LOG_TAG, nnfe, "%s is not installed for user %d", packageName, userId);
+                Slogf.w(LOG_TAG, nnfe, "%s is not installed for user %d", packageName, userId);
                 return null;
             }
             ApplicationInfo appInfo = userContext.getApplicationInfo();
@@ -9079,7 +9079,7 @@
      */
     private void wtfIfInLock() {
         if (Thread.holdsLock(this)) {
-            Slog.wtfStack(LOG_TAG, "Shouldn't be called with DPMS lock held");
+            Slogf.wtfStack(LOG_TAG, "Shouldn't be called with DPMS lock held");
         }
     }
 
@@ -9296,7 +9296,7 @@
         try {
             return mInjector.getIActivityManager().getCurrentUser().id;
         } catch (RemoteException e) {
-            Slog.wtf(LOG_TAG, "cannot get current user");
+            Slogf.wtf(LOG_TAG, "cannot get current user");
         }
         return UserHandle.USER_NULL;
     }
@@ -9384,7 +9384,7 @@
                 // pm.getUnsuspendablePackages() will fail if it's called for a different user;
                 // as this dump is mostly useful for system user anyways, we can just ignore the
                 // others (rather than changing the permission check in the PM method)
-                Slog.d(LOG_TAG, "skipping PersonalAppsSuspensionHelper.dump() for user " + userId);
+                Slogf.d(LOG_TAG, "skipping PersonalAppsSuspensionHelper.dump() for user " + userId);
             }
         }
     }
@@ -9681,8 +9681,8 @@
                         }
                         result.add(info.options);
                     } else {
-                        Slog.w(LOG_TAG, "Ignoring admin %s because it has trust options but doesn't"
-                                + " declare KEYGUARD_DISABLE_TRUST_AGENTS", active.info);
+                        Slogf.w(LOG_TAG, "Ignoring admin %s because it has trust options but "
+                                + "doesn't declare KEYGUARD_DISABLE_TRUST_AGENTS", active.info);
                     }
                 } else if (disablesTrust) {
                     allAdminsHaveOptions = false;
@@ -9728,7 +9728,7 @@
             try {
                 UserInfo parent = mUserManager.getProfileParent(callingUserId);
                 if (parent == null) {
-                    Slog.e(LOG_TAG, "Cannot call addCrossProfileIntentFilter if there is no "
+                    Slogf.e(LOG_TAG, "Cannot call addCrossProfileIntentFilter if there is no "
                             + "parent");
                     return;
                 }
@@ -9778,7 +9778,7 @@
             try {
                 UserInfo parent = mUserManager.getProfileParent(callingUserId);
                 if (parent == null) {
-                    Slog.e(LOG_TAG, "Cannot call clearCrossProfileIntentFilter if there is no "
+                    Slogf.e(LOG_TAG, "Cannot call clearCrossProfileIntentFilter if there is no "
                             + "parent");
                     return;
                 }
@@ -9820,7 +9820,7 @@
                             userIdToCheck);
                     systemService = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
                 } catch (RemoteException e) {
-                    Slog.i(LOG_TAG, "Can't talk to package managed", e);
+                    Slogf.i(LOG_TAG, "Can't talk to package managed", e);
                 }
                 if (!systemService && !permittedList.contains(enabledPackage)) {
                     return false;
@@ -9873,7 +9873,7 @@
                 }
                 if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList,
                         userId)) {
-                    Slog.e(LOG_TAG, "Cannot set permitted accessibility services, "
+                    Slogf.e(LOG_TAG, "Cannot set permitted accessibility services, "
                             + "because it contains already enabled accesibility services.");
                     return false;
                 }
@@ -10026,7 +10026,7 @@
                 }
                 if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList,
                         userId)) {
-                    Slog.e(LOG_TAG, "Cannot set permitted input methods, because the list of "
+                    Slogf.e(LOG_TAG, "Cannot set permitted input methods, because the list of "
                             + "permitted input methods excludes an already-enabled input method.");
                     return false;
                 }
@@ -10292,7 +10292,7 @@
                 }
 
                 Object token = new Object();
-                Slog.d(LOG_TAG, "Adding new pending token: " + token);
+                Slogf.d(LOG_TAG, "Adding new pending token: " + token);
                 mPendingUserCreatedCallbackTokens.add(token);
                 try {
                     UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name,
@@ -10301,7 +10301,7 @@
                         user = userInfo.getUserHandle();
                     }
                 } catch (UserManager.CheckedUserOperationException e) {
-                    Slog.e(LOG_TAG, "Couldn't createUserEvenWhenDisallowed", e);
+                    Slogf.e(LOG_TAG, "Couldn't createUserEvenWhenDisallowed", e);
                 }
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
@@ -10356,7 +10356,7 @@
             boolean showDisclaimer) {
         synchronized (getLockObject()) {
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "manageUserUnchecked(): admin=" + admin + ", po=" + profileOwner
+                Slogf.v(LOG_TAG, "manageUserUnchecked(): admin=" + admin + ", po=" + profileOwner
                         + ", userId=" + userId + ", hasAdminExtras=" + (adminExtras != null)
                         + ", showDisclaimer=" + showDisclaimer);
             }
@@ -10372,7 +10372,7 @@
             }
         } catch (RemoteException e) {
             // Does not happen, same process
-            Slog.wtf(LOG_TAG, e, "Failed to install admin package %s for user %d",
+            Slogf.wtf(LOG_TAG, e, "Failed to install admin package %s for user %d",
                     adminPkg, userId);
         }
 
@@ -10396,7 +10396,7 @@
 
     private void handleNewUserCreated(UserInfo user, @Nullable Object token) {
         if (VERBOSE_LOG) {
-            Slog.v(LOG_TAG, "handleNewUserCreated(): user=" + user.toFullString()
+            Slogf.v(LOG_TAG, "handleNewUserCreated(): user=" + user.toFullString()
                     + ", token=" + token);
         }
 
@@ -10405,7 +10405,7 @@
             synchronized (getLockObject()) {
                 if (mPendingUserCreatedCallbackTokens.contains(token)) {
                     // Ignore because it was triggered by createAndManageUser()
-                    Slog.d(LOG_TAG, "handleNewUserCreated(): ignoring for user " + userId
+                    Slogf.d(LOG_TAG, "handleNewUserCreated(): ignoring for user " + userId
                             + " due to token" + token);
                     mPendingUserCreatedCallbackTokens.remove(token);
                     return;
@@ -10417,12 +10417,12 @@
 
         if (mInjector.userManagerIsHeadlessSystemUserMode()) {
             ComponentName admin = mOwners.getDeviceOwnerComponent();
-            Slog.i(LOG_TAG, "Automatically setting profile owner (" + admin + ") on new user "
+            Slogf.i(LOG_TAG, "Automatically setting profile owner (" + admin + ") on new user "
                     + userId);
             manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
                     /* managedUser= */ userId, /* adminExtras= */ null, /* showDisclaimer= */ true);
         } else {
-            Slog.i(LOG_TAG, "User %d added on DO mode; setting ShowNewUserDisclaimer", userId);
+            Slogf.i(LOG_TAG, "User %d added on DO mode; setting ShowNewUserDisclaimer", userId);
             setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED);
         }
     }
@@ -10437,7 +10437,7 @@
     }
 
     private void setShowNewUserDisclaimer(@UserIdInt int userId, String value) {
-        Slog.i(LOG_TAG, "Setting new user disclaimer for user " + userId + " as " + value);
+        Slogf.i(LOG_TAG, "Setting new user disclaimer for user " + userId + " as " + value);
         synchronized (getLockObject()) {
             DevicePolicyData policyData = getUserData(userId);
             policyData.mNewUserDisclaimer = value;
@@ -10450,7 +10450,7 @@
         synchronized (getLockObject()) {
             DevicePolicyData policyData = getUserData(userId);
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "showNewUserDisclaimerIfNecessary(" + userId + "): "
+                Slogf.v(LOG_TAG, "showNewUserDisclaimerIfNecessary(" + userId + "): "
                         + policyData.mNewUserDisclaimer + ")");
             }
             mustShow = DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED
@@ -10461,7 +10461,7 @@
         Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_NEW_USER_DISCLAIMER);
 
         // TODO(b/172691310): add CTS tests to make sure disclaimer is shown
-        Slog.i(LOG_TAG, "Dispatching ACTION_SHOW_NEW_USER_DISCLAIMER intent");
+        Slogf.i(LOG_TAG, "Dispatching ACTION_SHOW_NEW_USER_DISCLAIMER intent");
         mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
     }
 
@@ -10478,7 +10478,7 @@
                     ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE
                     : UserManager.DISALLOW_REMOVE_USER;
             if (isAdminAffectedByRestriction(who, restriction, caller.getUserId())) {
-                Slog.w(LOG_TAG, "The device owner cannot remove a user because %s is enabled, and "
+                Slogf.w(LOG_TAG, "The device owner cannot remove a user because %s is enabled, and "
                         + "was not set by the device owner", restriction);
                 return false;
             }
@@ -10516,7 +10516,7 @@
                 }
                 return mInjector.getIActivityManager().switchUser(userId);
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Couldn't switch user", e);
+                Slogf.e(LOG_TAG, "Couldn't switch user", e);
                 return false;
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
@@ -10534,19 +10534,19 @@
 
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
-            Slog.w(LOG_TAG, "Managed profile cannot be started in background");
+            Slogf.w(LOG_TAG, "Managed profile cannot be started in background");
             return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
             if (!mInjector.getActivityManagerInternal().canStartMoreUsers()) {
-                Slog.w(LOG_TAG, "Cannot start user %d, too many users in background", userId);
+                Slogf.w(LOG_TAG, "Cannot start user %d, too many users in background", userId);
                 return UserManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS;
             }
 
             if (mInjector.getIActivityManager().startUserInBackground(userId)) {
-                Slog.i(LOG_TAG, "Started used %d in background", userId);
+                Slogf.i(LOG_TAG, "Started used %d in background", userId);
                 return UserManager.USER_OPERATION_SUCCESS;
             } else {
                 return UserManager.USER_OPERATION_ERROR_UNKNOWN;
@@ -10569,7 +10569,7 @@
 
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
-            Slog.w(LOG_TAG, "Managed profile cannot be stopped");
+            Slogf.w(LOG_TAG, "Managed profile cannot be stopped");
             return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
@@ -10592,14 +10592,14 @@
         }
 
         if (isManagedProfile(callingUserId)) {
-            Slog.w(LOG_TAG, "Managed profile cannot be logout");
+            Slogf.w(LOG_TAG, "Managed profile cannot be logout");
             return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
             if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) {
-                Slog.w(LOG_TAG, "Failed to switch to primary user");
+                Slogf.w(LOG_TAG, "Failed to switch to primary user");
                 // This should never happen as target user is UserHandle.USER_SYSTEM
                 return UserManager.USER_OPERATION_ERROR_UNKNOWN;
             }
@@ -10735,7 +10735,7 @@
                         suspended, null, null, null, PLATFORM_PACKAGE_NAME, caller.getUserId());
             } catch (RemoteException re) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Failed talking to the package manager", re);
+                Slogf.e(LOG_TAG, "Failed talking to the package manager", re);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -10748,7 +10748,7 @@
                 .write();
 
         if (nonSuspendedPackages == null) {
-            Slog.w(LOG_TAG, "PM failed to suspend packages (%s)", Arrays.toString(packageNames));
+            Slogf.w(LOG_TAG, "PM failed to suspend packages (%s)", Arrays.toString(packageNames));
             return packageNames;
         }
         if (exemptApps.isEmpty()) {
@@ -10756,7 +10756,7 @@
         }
 
         String[] result = buildNonSuspendedPackagesUnionArray(nonSuspendedPackages, exemptApps);
-        if (VERBOSE_LOG) Slog.v(LOG_TAG, "Returning %s", Arrays.toString(result));
+        if (VERBOSE_LOG) Slogf.v(LOG_TAG, "Returning %s", Arrays.toString(result));
         return result;
     }
 
@@ -10790,7 +10790,7 @@
                 return mIPackageManager.isPackageSuspendedForUser(packageName, caller.getUserId());
             } catch (RemoteException re) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Failed talking to the package manager", re);
+                Slogf.e(LOG_TAG, "Failed talking to the package manager", re);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -10978,7 +10978,7 @@
 
         List<String> exemptApps = listPolicyExemptAppsUnchecked();
         if (exemptApps.contains(packageName)) {
-            Slog.d(LOG_TAG, "setApplicationHidden(): ignoring %s as it's on policy-exempt list",
+            Slogf.d(LOG_TAG, "setApplicationHidden(): ignoring %s as it's on policy-exempt list",
                     packageName);
             return false;
         }
@@ -10998,7 +10998,7 @@
             checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_HIDDEN);
 
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "calling pm.setApplicationHiddenSettingAsUser(%s, %b, %d)",
+                Slogf.v(LOG_TAG, "calling pm.setApplicationHiddenSettingAsUser(%s, %b, %d)",
                         packageName, hidden, userId);
             }
             result = mInjector.binderWithCleanCallingIdentity(() -> mIPackageManager
@@ -11064,7 +11064,7 @@
             long id = mInjector.binderClearCallingIdentity();
             try {
                 if (VERBOSE_LOG) {
-                    Slog.v(LOG_TAG, "installing " + packageName + " for " + userId);
+                    Slogf.v(LOG_TAG, "installing " + packageName + " for " + userId);
                 }
 
                 Preconditions.checkArgument(isDemo || isSystemApp(mIPackageManager, packageName,
@@ -11082,7 +11082,7 @@
                 }
             } catch (RemoteException re) {
                 // shouldn't happen
-                Slog.wtf(LOG_TAG, "Failed to install " + packageName, re);
+                Slogf.wtf(LOG_TAG, "Failed to install " + packageName, re);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -11116,7 +11116,7 @@
                         .getList();
 
                 if (VERBOSE_LOG) {
-                    Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
+                    Slogf.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
                 }
                 if (activitiesToEnable != null) {
                     for (ResolveInfo info : activitiesToEnable) {
@@ -11129,7 +11129,7 @@
                                         PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
                                         PackageManager.INSTALL_REASON_POLICY, null);
                             } else {
-                                Slog.d(LOG_TAG, "Not enabling " + packageName + " since is not a"
+                                Slogf.d(LOG_TAG, "Not enabling " + packageName + " since is not a"
                                         + " system app");
                             }
                         }
@@ -11137,7 +11137,7 @@
                 }
             } catch (RemoteException e) {
                 // shouldn't happen
-                Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
+                Slogf.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
                 return 0;
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
@@ -11181,7 +11181,7 @@
             final long id = mInjector.binderClearCallingIdentity();
             try {
                 if (VERBOSE_LOG) {
-                    Slog.v(LOG_TAG, "installing " + packageName + " for " + caller.getUserId());
+                    Slogf.v(LOG_TAG, "installing " + packageName + " for " + caller.getUserId());
                 }
 
                 // Install the package.
@@ -11294,7 +11294,7 @@
                 mIPackageManager.setBlockUninstallForUser(packageName, uninstallBlocked, userId);
             } catch (RemoteException re) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Failed to setBlockUninstallForUser", re);
+                Slogf.e(LOG_TAG, "Failed to setBlockUninstallForUser", re);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -11332,7 +11332,7 @@
                 return mIPackageManager.getBlockUninstallForUser(packageName, userId);
             } catch (RemoteException re) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Failed to getBlockUninstallForUser", re);
+                Slogf.e(LOG_TAG, "Failed to getBlockUninstallForUser", re);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -11457,7 +11457,7 @@
                 }
                 if (isCrossProfileQuickContactDisabled(managedUserId)) {
                     if (VERBOSE_LOG) {
-                        Slog.v(LOG_TAG, "Cross-profile contacts access disabled for user %d",
+                        Slogf.v(LOG_TAG, "Cross-profile contacts access disabled for user %d",
                                 managedUserId);
                     }
                     return;
@@ -11481,16 +11481,16 @@
      * Otherwise -1.
      */
     public int getManagedUserId(@UserIdInt int callingUserId) {
-        if (VERBOSE_LOG) Slog.v(LOG_TAG, "getManagedUserId: callingUserId=%d", callingUserId);
+        if (VERBOSE_LOG) Slogf.v(LOG_TAG, "getManagedUserId: callingUserId=%d", callingUserId);
 
         for (UserInfo ui : mUserManager.getProfiles(callingUserId)) {
             if (ui.id == callingUserId || !ui.isManagedProfile()) {
                 continue; // Caller user self, or not a managed profile.  Skip.
             }
-            if (VERBOSE_LOG) Slog.v(LOG_TAG, "Managed user=%d", ui.id);
+            if (VERBOSE_LOG) Slogf.v(LOG_TAG, "Managed user=%d", ui.id);
             return ui.id;
         }
-        if (VERBOSE_LOG)  Slog.v(LOG_TAG, "Managed user not found.");
+        if (VERBOSE_LOG)  Slogf.v(LOG_TAG, "Managed user not found.");
         return -1;
     }
 
@@ -11724,13 +11724,13 @@
 
                 final List<String> lockTaskPackages = getUserData(userId).mLockTaskPackages;
                 if (!lockTaskPackages.isEmpty()) {
-                    Slog.d(LOG_TAG,
+                    Slogf.d(LOG_TAG,
                             "User id " + userId + " not affiliated. Clearing lock task packages");
                     setLockTaskPackagesLocked(userId, Collections.<String>emptyList());
                 }
                 final int lockTaskFeatures = getUserData(userId).mLockTaskFeatures;
                 if (lockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE){
-                    Slog.d(LOG_TAG,
+                    Slogf.d(LOG_TAG,
                             "User id " + userId + " not affiliated. Clearing lock task features");
                     setLockTaskFeaturesLocked(userId, DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
                 }
@@ -11789,7 +11789,7 @@
             // Some settings are no supported any more. However we do not want to throw a
             // SecurityException to avoid breaking apps.
             if (GLOBAL_SETTINGS_DEPRECATED.contains(setting)) {
-                Slog.i(LOG_TAG, "Global setting no longer supported: %s", setting);
+                Slogf.i(LOG_TAG, "Global setting no longer supported: %s", setting);
                 return;
             }
 
@@ -11910,7 +11910,7 @@
         if (targetInfo != null) {
             intent.setComponent(targetInfo.getComponentName());
         } else {
-            Slog.wtf(LOG_TAG, "Failed to resolve intent for location settings");
+            Slogf.wtf(LOG_TAG, "Failed to resolve intent for location settings");
         }
 
         // Simple notification clicks are immutable
@@ -12005,7 +12005,7 @@
                             + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY + " instead.");
                 }
                 if (!mUserManager.isManagedProfile(callingUserId)) {
-                    Slog.e(LOG_TAG, "Ignoring setSecureSetting request for "
+                    Slogf.e(LOG_TAG, "Ignoring setSecureSetting request for "
                             + setting + ". User restriction "
                             + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES + " or "
                             + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
@@ -12020,7 +12020,7 @@
                                 .setStrings(setting, value)
                                 .write();
                     } catch (NumberFormatException exc) {
-                        Slog.e(LOG_TAG, "Invalid value: " + value + " for setting " + setting);
+                        Slogf.e(LOG_TAG, "Invalid value: " + value + " for setting " + setting);
                     }
                 }
                 return;
@@ -12171,7 +12171,7 @@
                     isLockTaskMode = mInjector.getIActivityTaskManager().getLockTaskModeState()
                             != LOCK_TASK_MODE_NONE;
                 } catch (RemoteException e) {
-                    Slog.e(LOG_TAG, "Failed to get LockTask mode");
+                    Slogf.e(LOG_TAG, "Failed to get LockTask mode");
                 }
                 if (!isLockTaskMode) {
                     if (!setStatusBarDisabledInternal(disabled, userId)) {
@@ -12203,7 +12203,7 @@
                 return true;
             }
         } catch (RemoteException e) {
-            Slog.e(LOG_TAG, "Failed to disable the status bar", e);
+            Slogf.e(LOG_TAG, "Failed to disable the status bar", e);
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
         }
@@ -12535,7 +12535,7 @@
             synchronized (getLockObject()) {
                 if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING,
                         UserHandle.of(userId))) {
-                    Slog.e(LOG_TAG, "printing is enabled for user %d", userId);
+                    Slogf.e(LOG_TAG, "printing is enabled for user %d", userId);
                     return null;
                 }
                 String ownerPackage = mOwners.getProfileOwnerPackage(userId);
@@ -12548,22 +12548,22 @@
                     try {
                         return pm.getPackageInfo(packageName, 0);
                     } catch (NameNotFoundException e) {
-                        Slog.e(LOG_TAG, "getPackageInfo error", e);
+                        Slogf.e(LOG_TAG, "getPackageInfo error", e);
                         return null;
                     }
                 });
                 if (packageInfo == null) {
-                    Slog.e(LOG_TAG, "packageInfo is inexplicably null");
+                    Slogf.e(LOG_TAG, "packageInfo is inexplicably null");
                     return null;
                 }
                 ApplicationInfo appInfo = packageInfo.applicationInfo;
                 if (appInfo == null) {
-                    Slog.e(LOG_TAG, "appInfo is inexplicably null");
+                    Slogf.e(LOG_TAG, "appInfo is inexplicably null");
                     return null;
                 }
                 CharSequence appLabel = pm.getApplicationLabel(appInfo);
                 if (appLabel == null) {
-                    Slog.e(LOG_TAG, "appLabel is inexplicably null");
+                    Slogf.e(LOG_TAG, "appLabel is inexplicably null");
                     return null;
                 }
                 return ((Context) ActivityThread.currentActivityThread().getSystemUiContext())
@@ -12617,7 +12617,7 @@
             Objects.requireNonNull(intent);
             Objects.requireNonNull(parentHandle);
             final int userId = parentHandle.getIdentifier();
-            Slog.i(LOG_TAG, "Sending %s broadcast to manifest receivers.", intent.getAction());
+            Slogf.i(LOG_TAG, "Sending %s broadcast to manifest receivers.", intent.getAction());
             try {
                 final List<ResolveInfo> receivers = mIPackageManager.queryIntentReceivers(
                         intent, /* resolvedType= */ null,
@@ -12627,7 +12627,7 @@
                     if (checkCrossProfilePackagePermissions(packageName, userId,
                             requiresPermission)
                             || checkModifyQuietModePermission(packageName, userId)) {
-                        Slog.i(LOG_TAG, "Sending %s broadcast to %s.", intent.getAction(),
+                        Slogf.i(LOG_TAG, "Sending %s broadcast to %s.", intent.getAction(),
                                 packageName);
                         final Intent packageIntent = new Intent(intent)
                                 .setComponent(receiver.getComponentInfo().getComponentName())
@@ -12636,7 +12636,7 @@
                     }
                 }
             } catch (RemoteException ex) {
-                Slog.w(LOG_TAG, "Cannot get list of broadcast receivers for %s because: %s.",
+                Slogf.w(LOG_TAG, "Cannot get list of broadcast receivers for %s because: %s.",
                         intent.getAction(), ex);
             }
         }
@@ -12655,7 +12655,7 @@
                         android.Manifest.permission.MODIFY_QUIET_MODE, uid, /* owningUid= */
                         -1, /* exported= */ true);
             } catch (NameNotFoundException ex) {
-                Slog.w(LOG_TAG, "Cannot find the package %s to check for permissions.",
+                Slogf.w(LOG_TAG, "Cannot find the package %s to check for permissions.",
                         packageName);
                 return false;
             }
@@ -12685,7 +12685,7 @@
                 return crossProfileAppsService.verifyPackageHasInteractAcrossProfilePermission(
                         packageName, userId);
             } catch (NameNotFoundException ex) {
-                Slog.w(LOG_TAG, "Cannot find the package %s to check for permissions.",
+                Slogf.w(LOG_TAG, "Cannot find the package %s to check for permissions.",
                         packageName);
                 return false;
             }
@@ -12760,7 +12760,7 @@
             // TODO(b/178494483): use EventLog instead
             // TODO(b/178494483): log metrics?
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "notifyUnsafeOperationStateChanged(): %s=%b",
+                Slogf.v(LOG_TAG, "notifyUnsafeOperationStateChanged(): %s=%b",
                         DevicePolicyManager.operationSafetyReasonToString(reason), isSafe);
             }
             Preconditions.checkArgument(mSafetyChecker == checker,
@@ -12771,12 +12771,12 @@
             extras.putBoolean(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_STATE, isSafe);
 
             if (mOwners.hasDeviceOwner()) {
-                if (VERBOSE_LOG) Slog.v(LOG_TAG, "Notifying DO");
+                if (VERBOSE_LOG) Slogf.v(LOG_TAG, "Notifying DO");
                 sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
                         extras);
             }
             for (int profileOwnerId : mOwners.getProfileOwnerKeys()) {
-                if (VERBOSE_LOG) Slog.v(LOG_TAG, "Notifying PO for user " + profileOwnerId);
+                if (VERBOSE_LOG) Slogf.v(LOG_TAG, "Notifying PO for user " + profileOwnerId);
                 sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
                         extras, profileOwnerId);
             }
@@ -12893,7 +12893,7 @@
         synchronized (getLockObject()) {
             SystemUpdatePolicy policy =  mOwners.getSystemUpdatePolicy();
             if (policy != null && !policy.isValid()) {
-                Slog.w(LOG_TAG, "Stored system update policy is invalid, return null instead.");
+                Slogf.w(LOG_TAG, "Stored system update policy is invalid, return null instead.");
                 return null;
             }
             return policy;
@@ -12922,7 +12922,7 @@
      * @see SystemUpdatePolicy#validateAgainstPreviousFreezePeriod
      */
     private void updateSystemUpdateFreezePeriodsRecord(boolean saveIfChanged) {
-        Slog.d(LOG_TAG, "updateSystemUpdateFreezePeriodsRecord");
+        Slogf.d(LOG_TAG, "updateSystemUpdateFreezePeriodsRecord");
         synchronized (getLockObject()) {
             final SystemUpdatePolicy policy = mOwners.getSystemUpdatePolicy();
             if (policy == null) {
@@ -12972,7 +12972,7 @@
                         + "clearSystemUpdatePolicyFreezePeriodRecord");
         synchronized (getLockObject()) {
             // Print out current record to help diagnosed CTS failures
-            Slog.i(LOG_TAG, "Clear freeze period record: "
+            Slogf.i(LOG_TAG, "Clear freeze period record: "
                     + mOwners.getSystemUpdateFreezePeriodRecordAsString());
             if (mOwners.setSystemUpdateFreezePeriodRecord(null, null)) {
                 mOwners.writeDeviceOwner();
@@ -13013,8 +13013,8 @@
                 "Only the system update service can broadcast update information");
 
         if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
-            Slog.w(LOG_TAG, "Only the system update service in the system user " +
-                    "can broadcast update information.");
+            Slogf.w(LOG_TAG, "Only the system update service in the system user can broadcast "
+                    + "update information.");
             return;
         }
 
@@ -13043,7 +13043,7 @@
                 runningUserIds = mInjector.getIActivityManager().getRunningUserIds();
             } catch (RemoteException e) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Could not retrieve the list of running users", e);
+                Slogf.e(LOG_TAG, "Could not retrieve the list of running users", e);
                 return;
             }
             // Send broadcasts to corresponding profile owners if any.
@@ -13171,7 +13171,7 @@
                                     });
                 }
             } catch (SecurityException e) {
-                Slog.e(LOG_TAG, "Could not set permission grant state", e);
+                Slogf.e(LOG_TAG, "Could not set permission grant state", e);
 
                 callback.sendResult(null);
             } finally {
@@ -13288,7 +13288,7 @@
         }
         final int code = checkProvisioningPreConditionSkipPermissionNoLog(action, packageName);
         if (code != CODE_OK) {
-            Slog.d(LOG_TAG, "checkProvisioningPreCondition(" + action + ", " + packageName
+            Slogf.d(LOG_TAG, "checkProvisioningPreCondition(" + action + ", " + packageName
                     + ") failed: "
                     + computeProvisioningErrorString(code, mInjector.userHandleGetCallingUserId()));
         }
@@ -13353,7 +13353,7 @@
 
         if (isHeadlessSystemUserMode) {
             if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
-                Slog.e(LOG_TAG, "In headless system user mode, "
+                Slogf.e(LOG_TAG, "In headless system user mode, "
                         + "device owner can only be set on headless system user.");
                 return CODE_NOT_SYSTEM_USER;
             }
@@ -13377,7 +13377,7 @@
                 if (callingUserId != currentForegroundUser
                         && mInjector.userManagerIsHeadlessSystemUserMode()
                         && currentForegroundUser == UserHandle.USER_SYSTEM) {
-                    Slog.wtf(LOG_TAG, "In headless system user mode, "
+                    Slogf.wtf(LOG_TAG, "In headless system user mode, "
                             + "current user cannot be system user when setting device owner");
                     return CODE_SYSTEM_USER;
                 }
@@ -13404,7 +13404,7 @@
             final int deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
                     ? UserHandle.USER_SYSTEM
                     : callingUserId;
-            Slog.i(LOG_TAG, "Calling user %d, device owner will be set on user %d",
+            Slogf.i(LOG_TAG, "Calling user %d, device owner will be set on user %d",
                     callingUserId, deviceOwnerUserId);
             // hasIncompatibleAccountsOrNonAdb doesn't matter since the caller is not adb.
             return checkDeviceOwnerProvisioningPreConditionLocked(/* owner unknown */ null,
@@ -13435,7 +13435,8 @@
                     UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle);
 
             if (mUserManager.getUserInfo(callingUserId).isProfile()) {
-                Slog.i(LOG_TAG, "Calling user %d is a profile, cannot add another.", callingUserId);
+                Slogf.i(LOG_TAG, "Calling user %d is a profile, cannot add another.",
+                        callingUserId);
                 // The check is called from inside a managed profile. A managed profile cannot
                 // be provisioned from within another managed profile.
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
@@ -13443,12 +13444,12 @@
 
             // If there's a device owner, the restriction on adding a managed profile must be set.
             if (hasDeviceOwner && !addingProfileRestricted) {
-                Slog.wtf(LOG_TAG, "Has a device owner but no restriction on adding a profile.");
+                Slogf.wtf(LOG_TAG, "Has a device owner but no restriction on adding a profile.");
             }
 
             // Do not allow adding a managed profile if there's a restriction.
             if (addingProfileRestricted) {
-                Slog.i(LOG_TAG, "Adding a profile is restricted: User %s Has device owner? %b",
+                Slogf.i(LOG_TAG, "Adding a profile is restricted: User %s Has device owner? %b",
                         callingUserHandle, hasDeviceOwner);
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
@@ -13456,7 +13457,7 @@
             // Bail out if we are trying to provision a work profile but one already exists.
             if (!mUserManager.canAddMoreManagedProfiles(
                     callingUserId, /* allowedToRemoveOne= */ false)) {
-                Slog.i(LOG_TAG, "A work profile already exists.");
+                Slogf.i(LOG_TAG, "A work profile already exists.");
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
         } finally {
@@ -13944,7 +13945,7 @@
                     who.flattenToString(), userId));
         }
 
-        Slog.i(LOG_TAG, "Marking %s as profile owner on organization-owned device for user %d",
+        Slogf.i(LOG_TAG, "Marking %s as profile owner on organization-owned device for user %d",
                 who.flattenToString(), userId);
 
         // First, set restriction on removing the profile.
@@ -14089,7 +14090,7 @@
             for (int i = 0; i < userInfos.size(); i++) {
                 int userId = userInfos.get(i).id;
                 if (!isUserAffiliatedWithDeviceLocked(userId)) {
-                    Slog.d(LOG_TAG, "User id " + userId + " not affiliated.");
+                    Slogf.d(LOG_TAG, "User id " + userId + " not affiliated.");
                     return false;
                 }
             }
@@ -14218,7 +14219,7 @@
             }
             return new ParceledListSlice<SecurityEvent>(output);
         } catch (IOException e) {
-            Slog.w(LOG_TAG, "Fail to read previous events" , e);
+            Slogf.w(LOG_TAG, "Fail to read previous events" , e);
             return new ParceledListSlice<SecurityEvent>(Collections.<SecurityEvent>emptyList());
         }
     }
@@ -14387,7 +14388,7 @@
         try { // force stop the package before uninstalling
             mInjector.getIActivityManager().forceStopPackage(packageName, userId);
         } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package");
+            Slogf.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package");
         }
         final Uri packageURI = Uri.parse("package:" + packageName);
         final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
@@ -14423,7 +14424,7 @@
             updateMaximumTimeToLockLocked(userHandle);
             policy.mRemovingAdmins.remove(adminReceiver);
 
-            Slog.i(LOG_TAG, "Device admin " + adminReceiver + " removed from user " + userHandle);
+            Slogf.i(LOG_TAG, "Device admin " + adminReceiver + " removed from user " + userHandle);
         }
         // The removed admin might have disabled camera, so update user
         // restrictions.
@@ -14643,7 +14644,7 @@
             }
             synchronized (getLockObject()) {
                 if (owner == null || !isAdminTestOnlyLocked(owner, userId)) {
-                    Slog.w(LOG_TAG,
+                    Slogf.w(LOG_TAG,
                             "Non test-only owner can't be installed with existing accounts.");
                     return true;
                 }
@@ -14657,20 +14658,20 @@
             boolean compatible = true;
             for (Account account : accounts) {
                 if (hasAccountFeatures(am, account, feature_disallow)) {
-                    Slog.e(LOG_TAG, "%s has %s", account, feature_disallow[0]);
+                    Slogf.e(LOG_TAG, "%s has %s", account, feature_disallow[0]);
                     compatible = false;
                     break;
                 }
                 if (!hasAccountFeatures(am, account, feature_allow)) {
-                    Slog.e(LOG_TAG, "%s doesn't have %s", account, feature_allow[0]);
+                    Slogf.e(LOG_TAG, "%s doesn't have %s", account, feature_allow[0]);
                     compatible = false;
                     break;
                 }
             }
             if (compatible) {
-                Slog.w(LOG_TAG, "All accounts are compatible");
+                Slogf.w(LOG_TAG, "All accounts are compatible");
             } else {
-                Slog.e(LOG_TAG, "Found incompatible accounts");
+                Slogf.e(LOG_TAG, "Found incompatible accounts");
             }
             return !compatible;
         });
@@ -14680,7 +14681,7 @@
         try {
             return am.hasFeatures(account, features, null, null).getResult();
         } catch (Exception e) {
-            Slog.w(LOG_TAG, "Failed to get account feature", e);
+            Slogf.w(LOG_TAG, "Failed to get account feature", e);
             return false;
         }
     }
@@ -14740,14 +14741,14 @@
                     }
                     if (!mNetworkLogger.startNetworkLogging()) {
                         mNetworkLogger = null;
-                        Slog.wtf(LOG_TAG, "Network logging could not be started due to the logging"
+                        Slogf.wtf(LOG_TAG, "Network logging could not be started due to the logging"
                                 + " service not being available yet.");
                     }
                     maybePauseDeviceWideLoggingLocked();
                     sendNetworkLoggingNotificationLocked();
                 } else {
                     if (mNetworkLogger != null && !mNetworkLogger.stopNetworkLogging()) {
-                        Slog.wtf(LOG_TAG, "Network logging could not be stopped due to the logging"
+                        Slogf.wtf(LOG_TAG, "Network logging could not be stopped due to the logging"
                                 + " service not being available yet.");
                     }
                     mNetworkLogger = null;
@@ -14799,14 +14800,14 @@
     private void maybePauseDeviceWideLoggingLocked() {
         if (!areAllUsersAffiliatedWithDeviceLocked()) {
             if (mOwners.hasDeviceOwner()) {
-                Slog.i(LOG_TAG, "There are unaffiliated users, network logging will be "
+                Slogf.i(LOG_TAG, "There are unaffiliated users, network logging will be "
                         + "paused if enabled.");
                 if (mNetworkLogger != null) {
                     mNetworkLogger.pause();
                 }
             }
             if (!isOrganizationOwnedDeviceWithManagedProfile()) {
-                Slog.i(LOG_TAG, "Not org-owned managed profile device, security logging will be "
+                Slogf.i(LOG_TAG, "Not org-owned managed profile device, security logging will be "
                         + "paused if enabled.");
                 mSecurityLogMonitor.pause();
             }
@@ -14980,7 +14981,7 @@
                 0,  // flags
                 targetUserId);
         if (info == null || info.serviceInfo == null) {
-            Slog.e(LOG_TAG, "Fail to look up the service: %s or user %d is not running", rawIntent,
+            Slogf.e(LOG_TAG, "Fail to look up the service: %s or user %d is not running", rawIntent,
                     targetUserId);
             return null;
         }
@@ -15116,7 +15117,7 @@
                 return resetPasswordInternal(password, policy.mPasswordTokenHandle, token,
                         flags, caller);
             } else {
-                Slog.w(LOG_TAG, "No saved token handle");
+                Slogf.w(LOG_TAG, "No saved token handle");
             }
         }
         return false;
@@ -15166,8 +15167,8 @@
             // This can happen e.g. for device admin packages, do not throw out the exception,
             // because callers have no means to know beforehand for which packages this might
             // happen. If so, we send back that removal failed.
-            Slog.w(LOG_TAG, "Not allowed to clear application user data for package " + packageName,
-                    se);
+            Slogf.w(LOG_TAG, "Not allowed to clear application user data for package "
+                    + packageName, se);
             try {
                 callback.onRemoveCompleted(packageName, false);
             } catch (RemoteException re) {
@@ -15323,7 +15324,7 @@
             int profileOwnerUserId) {
         transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId);
         mOwners.transferProfileOwner(target, profileOwnerUserId);
-        Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
+        Slogf.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
         mOwners.writeProfileOwner(profileOwnerUserId);
         mDeviceAdminServiceController.startServiceForOwner(
                 target.getPackageName(), profileOwnerUserId, "transfer-profile-owner");
@@ -15335,7 +15336,7 @@
     private void transferDeviceOwnershipLocked(ComponentName admin, ComponentName target, int userId) {
         transferActiveAdminUncheckedLocked(target, admin, userId);
         mOwners.transferDeviceOwnership(target);
-        Slog.i(LOG_TAG, "Device owner set: " + target + " on user " + userId);
+        Slogf.i(LOG_TAG, "Device owner set: " + target + " on user " + userId);
         mOwners.writeDeviceOwner();
         mDeviceAdminServiceController.startServiceForOwner(
                 target.getPackageName(), userId, "transfer-device-owner");
@@ -15453,7 +15454,7 @@
                 parser.next();
                 return PersistableBundle.restoreFromXml(parser);
             } catch (IOException | XmlPullParserException | IllegalArgumentException e) {
-                Slog.e(LOG_TAG, "Caught exception while trying to load the "
+                Slogf.e(LOG_TAG, "Caught exception while trying to load the "
                         + "owner transfer parameters from file " + bundleFile, e);
                 return null;
             }
@@ -15475,7 +15476,7 @@
             return mInjector.binderWithCleanCallingIdentity(
                     () -> tm.addDevicePolicyOverrideApn(mContext, apnSetting));
         } else {
-            Slog.w(LOG_TAG, "TelephonyManager is null when trying to add override apn");
+            Slogf.w(LOG_TAG, "TelephonyManager is null when trying to add override apn");
             return Telephony.Carriers.INVALID_APN_ID;
         }
     }
@@ -15499,7 +15500,7 @@
             return mInjector.binderWithCleanCallingIdentity(
                     () -> tm.modifyDevicePolicyOverrideApn(mContext, apnId, apnSetting));
         } else {
-            Slog.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn");
+            Slogf.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn");
             return false;
         }
     }
@@ -15542,7 +15543,7 @@
             return mInjector.binderWithCleanCallingIdentity(
                     () -> tm.getDevicePolicyOverrideApns(mContext));
         }
-        Slog.w(LOG_TAG, "TelephonyManager is null when trying to get override apns");
+        Slogf.w(LOG_TAG, "TelephonyManager is null when trying to get override apns");
         return Collections.emptyList();
     }
 
@@ -15587,7 +15588,7 @@
                 return enforceCursor.getInt(enforceCursor.getColumnIndex(ENFORCE_KEY)) == 1;
             }
         } catch (IllegalArgumentException e) {
-            Slog.e(LOG_TAG, "Cursor returned from ENFORCE_MANAGED_URI doesn't contain "
+            Slogf.e(LOG_TAG, "Cursor returned from ENFORCE_MANAGED_URI doesn't contain "
                     + "correct info.", e);
         } finally {
             enforceCursor.close();
@@ -15612,7 +15613,7 @@
             serializer.endDocument();
             atomicFile.finishWrite(stream);
         } catch (IOException | XmlPullParserException e) {
-            Slog.e(LOG_TAG, "Caught exception while trying to save the "
+            Slogf.e(LOG_TAG, "Caught exception while trying to save the "
                     + "owner transfer parameters to file " + parametersFile, e);
             parametersFile.delete();
             atomicFile.failWrite(stream);
@@ -16042,7 +16043,7 @@
                 return false;
             }
             if (!isPackageAllowedToAccessCalendarForUser(packageName, workProfileUserId)) {
-                Slog.d(LOG_TAG, "Package %s is not allowed to access cross-profile calendar APIs",
+                Slogf.d(LOG_TAG, "Package %s is not allowed to access cross-profile calendar APIs",
                         packageName);
                 return false;
             }
@@ -16057,7 +16058,7 @@
             try {
                 mContext.startActivityAsUser(intent, UserHandle.of(workProfileUserId));
             } catch (ActivityNotFoundException e) {
-                Slog.e(LOG_TAG, "View event activity not found", e);
+                Slogf.e(LOG_TAG, "View event activity not found", e);
                 return false;
             }
             return true;
@@ -16071,7 +16072,7 @@
                         packageName, UserHandle.getUserId(callingUid));
                 return packageUid == callingUid;
             } catch (NameNotFoundException e) {
-                Slog.d(LOG_TAG, "Calling package not found", e);
+                Slogf.d(LOG_TAG, "Calling package not found", e);
                 return false;
             }
         });
@@ -16181,7 +16182,7 @@
             final long deadline = admin.mProfileOffDeadline;
             final int result = makeSuspensionReasons(admin.mSuspendPersonalApps,
                     deadline != 0 && mInjector.systemCurrentTimeMillis() > deadline);
-            Slog.d(LOG_TAG, "getPersonalAppsSuspendedReasons user: %d; result: %d",
+            Slogf.d(LOG_TAG, "getPersonalAppsSuspendedReasons user: %d; result: %d",
                     mInjector.userHandleGetCallingUserId(), result);
             return result;
         }
@@ -16239,7 +16240,7 @@
         synchronized (getLockObject()) {
             final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
             if (profileOwner == null) {
-                Slog.wtf(LOG_TAG, "Profile owner not found for compliance check");
+                Slogf.wtf(LOG_TAG, "Profile owner not found for compliance check");
                 return;
             }
             if (suspended) {
@@ -16271,7 +16272,7 @@
                         updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked);
                 final boolean suspendedExplicitly = profileOwner.mSuspendPersonalApps;
                 final boolean suspendedByTimeout = profileOwner.mProfileOffDeadline == -1;
-                Slog.d(LOG_TAG,
+                Slogf.d(LOG_TAG,
                         "Personal apps suspended explicitly: %b, by timeout: %b, notification: %d",
                         suspendedExplicitly, suspendedByTimeout, notificationState);
                 updateProfileOffDeadlineNotificationLocked(
@@ -16296,7 +16297,7 @@
             int profileUserId, ActiveAdmin profileOwner, boolean unlocked) {
         final long now = mInjector.systemCurrentTimeMillis();
         if (profileOwner.mProfileOffDeadline != 0 && now > profileOwner.mProfileOffDeadline) {
-            Slog.i(LOG_TAG, "Profile off deadline has been reached, unlocked: " + unlocked);
+            Slogf.i(LOG_TAG, "Profile off deadline has been reached, unlocked: " + unlocked);
             if (profileOwner.mProfileOffDeadline != -1) {
                 // Move the deadline far to the past so that it cannot be rolled back by TZ change.
                 profileOwner.mProfileOffDeadline = -1;
@@ -16315,14 +16316,14 @@
                 && (profileOwner.mProfileMaximumTimeOffMillis == 0)) {
             // There is a deadline but either there is no policy -> clear
             // the deadline.
-            Slog.i(LOG_TAG, "Profile off deadline is reset to zero");
+            Slogf.i(LOG_TAG, "Profile off deadline is reset to zero");
             profileOwner.mProfileOffDeadline = 0;
             shouldSaveSettings = true;
         } else if (profileOwner.mProfileOffDeadline == 0
                 && (profileOwner.mProfileMaximumTimeOffMillis != 0 && !unlocked)) {
             // There profile is locked and there is a policy, but the deadline is not set -> set the
             // deadline.
-            Slog.i(LOG_TAG, "Profile off deadline is set.");
+            Slogf.i(LOG_TAG, "Profile off deadline is set.");
             profileOwner.mProfileOffDeadline = now + profileOwner.mProfileMaximumTimeOffMillis;
             shouldSaveSettings = true;
         }
@@ -16357,10 +16358,10 @@
                         | PendingIntent.FLAG_IMMUTABLE);
 
         if (alarmTime == 0) {
-            Slog.i(LOG_TAG, "Profile off deadline alarm is removed.");
+            Slogf.i(LOG_TAG, "Profile off deadline alarm is removed.");
             am.cancel(pi);
         } else {
-            Slog.i(LOG_TAG, "Profile off deadline alarm is set.");
+            Slogf.i(LOG_TAG, "Profile off deadline alarm is set.");
             am.set(AlarmManager.RTC, alarmTime, pi);
         }
 
@@ -16371,7 +16372,7 @@
         if (getUserData(userId).mAppsSuspended == suspended) {
             return;
         }
-        Slog.i(LOG_TAG, "%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending",
+        Slogf.i(LOG_TAG, "%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending",
                 userId);
 
         if (suspended) {
@@ -16394,11 +16395,11 @@
                 final String[] failedApps = mIPackageManager.setPackagesSuspendedAsUser(
                         appsToSuspend, true, null, null, null, PLATFORM_PACKAGE_NAME, userId);
                 if (!ArrayUtils.isEmpty(failedApps)) {
-                    Slog.wtf(LOG_TAG, "Failed to suspend apps: " + String.join(",", failedApps));
+                    Slogf.wtf(LOG_TAG, "Failed to suspend apps: " + String.join(",", failedApps));
                 }
             } catch (RemoteException re) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Failed talking to the package manager", re);
+                Slogf.e(LOG_TAG, "Failed talking to the package manager", re);
             }
         });
     }
@@ -16568,17 +16569,17 @@
                 poAppInfo = mIPackageManager.getApplicationInfo(
                         poAdmin.info.getPackageName(), 0 /* flags */, userId);
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Failed to query PO app info", e);
+                Slogf.e(LOG_TAG, "Failed to query PO app info", e);
                 return false;
             }
             if (poAppInfo == null) {
-                Slog.wtf(LOG_TAG, "Cannot find AppInfo for profile owner");
+                Slogf.wtf(LOG_TAG, "Cannot find AppInfo for profile owner");
                 return false;
             }
             if (!poAppInfo.isEncryptionAware()) {
                 return false;
             }
-            Slog.d(LOG_TAG, "PO should be able to reset password from direct boot");
+            Slogf.d(LOG_TAG, "PO should be able to reset password from direct boot");
             return true;
         }
     }
@@ -16618,7 +16619,7 @@
         Preconditions.checkArgument(!TextUtils.isEmpty(organizationId),
                 "Enterprise ID may not be empty.");
 
-        Slog.i(LOG_TAG, "Setting Enterprise ID to %s for user %d", organizationId, userId);
+        Slogf.i(LOG_TAG, "Setting Enterprise ID to %s for user %d", organizationId, userId);
 
         final String ownerPackage;
         synchronized (getLockObject()) {
@@ -16873,11 +16874,11 @@
         final UserHandle sourceUser = UserHandle.of(sourceUserId);
         final UserHandle targetUser = UserHandle.of(targetUserId);
         if (accountToMigrate == null) {
-            Slog.d(LOG_TAG, "No account to migrate.");
+            Slogf.d(LOG_TAG, "No account to migrate.");
             return;
         }
         if (sourceUser.equals(targetUser)) {
-            Slog.w(LOG_TAG, "sourceUser and targetUser are the same, won't migrate account.");
+            Slogf.w(LOG_TAG, "sourceUser and targetUser are the same, won't migrate account.");
             return;
         }
         copyAccount(targetUser, sourceUser, accountToMigrate, callerPackage);
@@ -16906,15 +16907,15 @@
                         callerPackage);
             } else {
                 logCopyAccountStatus(COPY_ACCOUNT_FAILED, callerPackage);
-                Slog.e(LOG_TAG, "Failed to copy account to " + targetUser);
+                Slogf.e(LOG_TAG, "Failed to copy account to " + targetUser);
             }
         } catch (OperationCanceledException e) {
             // Account migration is not considered a critical operation.
             logCopyAccountStatus(COPY_ACCOUNT_TIMED_OUT, callerPackage);
-            Slog.e(LOG_TAG, "Exception copying account to " + targetUser, e);
+            Slogf.e(LOG_TAG, "Exception copying account to " + targetUser, e);
         } catch (AuthenticatorException | IOException e) {
             logCopyAccountStatus(COPY_ACCOUNT_EXCEPTION, callerPackage);
-            Slog.e(LOG_TAG, "Exception copying account to " + targetUser, e);
+            Slogf.e(LOG_TAG, "Exception copying account to " + targetUser, e);
         }
     }
 
@@ -16934,22 +16935,22 @@
         try {
             final Bundle result = bundle.getResult();
             if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, /* default */ false)) {
-                Slog.i(LOG_TAG, "Account removed from the primary user.");
+                Slogf.i(LOG_TAG, "Account removed from the primary user.");
             } else {
                 // TODO(174768447): Revisit start activity logic.
                 final Intent removeIntent = result.getParcelable(AccountManager.KEY_INTENT);
                 removeIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
                 if (removeIntent != null) {
-                    Slog.i(LOG_TAG, "Starting activity to remove account");
+                    Slogf.i(LOG_TAG, "Starting activity to remove account");
                     new Handler(Looper.getMainLooper()).post(() -> {
                         mContext.startActivity(removeIntent);
                     });
                 } else {
-                    Slog.e(LOG_TAG, "Could not remove account from the primary user.");
+                    Slogf.e(LOG_TAG, "Could not remove account from the primary user.");
                 }
             }
         } catch (OperationCanceledException | AuthenticatorException | IOException e) {
-            Slog.e(LOG_TAG, "Exception removing account from the primary user.", e);
+            Slogf.e(LOG_TAG, "Exception removing account from the primary user.", e);
         }
     }
 
@@ -17042,7 +17043,7 @@
             }
         } catch (Exception e) {
             // Do not stop provisioning and ignore this error.
-            Slog.e(LOG_TAG, "Alarm manager failed to set the system time/timezone.", e);
+            Slogf.e(LOG_TAG, "Alarm manager failed to set the system time/timezone.", e);
         }
     }
 
@@ -17056,7 +17057,7 @@
             LocalePicker.updateLocale(locale);
         } catch (Exception e) {
             // Do not stop provisioning and ignore this error.
-            Slog.e(LOG_TAG, "Failed to set the system locale.", e);
+            Slogf.e(LOG_TAG, "Failed to set the system locale.", e);
         }
     }
 
@@ -17069,21 +17070,21 @@
 
         removeNonInstalledPackages(packagesToDelete, userId);
         if (packagesToDelete.isEmpty()) {
-            Slog.i(LOG_TAG, "No packages to delete on user " + userId);
+            Slogf.i(LOG_TAG, "No packages to delete on user " + userId);
             return true;
         }
 
         NonRequiredPackageDeleteObserver packageDeleteObserver =
                 new NonRequiredPackageDeleteObserver(packagesToDelete.size());
         for (String packageName : packagesToDelete) {
-            Slog.i(LOG_TAG, "Deleting package [" + packageName + "] as user " + userId);
+            Slogf.i(LOG_TAG, "Deleting package [" + packageName + "] as user " + userId);
             mContext.getPackageManager().deletePackageAsUser(
                     packageName,
                     packageDeleteObserver,
                     PackageManager.DELETE_SYSTEM_APP,
                     userId);
         }
-        Slog.i(LOG_TAG, "Waiting for non required apps to be deleted");
+        Slogf.i(LOG_TAG, "Waiting for non required apps to be deleted");
         return packageDeleteObserver.awaitPackagesDeletion();
     }
 
@@ -17099,7 +17100,7 @@
 
     private void disallowAddUser() {
         if (mInjector.userManagerIsHeadlessSystemUserMode()) {
-            Slog.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode.");
+            Slogf.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode.");
             return;
         }
         for (UserInfo userInfo : mUserManager.getUsers()) {
@@ -17286,7 +17287,7 @@
         }
         if (!mInjector.binderWithCleanCallingIdentity(
                 () -> mInjector.getUsbManager().enableUsbDataSignal(usbEnabled))) {
-            Slog.w(LOG_TAG, "Failed to set usb data signaling state");
+            Slogf.w(LOG_TAG, "Failed to set usb data signaling state");
         }
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
index 28a6987..964be38 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
@@ -26,10 +26,10 @@
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.service.persistentdata.PersistentDataBlockManager;
-import android.util.Slog;
 
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.Preconditions;
+import com.android.server.utils.Slogf;
 
 import java.io.IOException;
 import java.util.Objects;
@@ -68,16 +68,16 @@
         IResultReceiver receiver = new IResultReceiver.Stub() {
             @Override
             public void send(int resultCode, Bundle resultData) throws RemoteException {
-                Slog.i(TAG, "Factory reset confirmed by %s, proceeding", mSafetyChecker);
+                Slogf.i(TAG, "Factory reset confirmed by %s, proceeding", mSafetyChecker);
                 try {
                     factoryResetInternalUnchecked();
                 } catch (IOException e) {
                     // Shouldn't happen
-                    Slog.wtf(TAG, e, "IOException calling underlying systems");
+                    Slogf.wtf(TAG, e, "IOException calling underlying systems");
                 }
             }
         };
-        Slog.i(TAG, "Delaying factory reset until %s confirms", mSafetyChecker);
+        Slogf.i(TAG, "Delaying factory reset until %s confirms", mSafetyChecker);
         mSafetyChecker.onFactoryReset(receiver);
         return false;
     }
@@ -112,7 +112,7 @@
     }
 
     private void factoryResetInternalUnchecked() throws IOException {
-        Slog.i(TAG, "factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
+        Slogf.i(TAG, "factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
                 + "wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc,
                 mWipeAdoptableStorage, mWipeFactoryResetProtection);
 
@@ -125,15 +125,15 @@
             PersistentDataBlockManager manager = mContext
                     .getSystemService(PersistentDataBlockManager.class);
             if (manager != null) {
-                Slog.w(TAG, "Wiping factory reset protection");
+                Slogf.w(TAG, "Wiping factory reset protection");
                 manager.wipe();
             } else {
-                Slog.w(TAG, "No need to wipe factory reset protection");
+                Slogf.w(TAG, "No need to wipe factory reset protection");
             }
         }
 
         if (mWipeAdoptableStorage) {
-            Slog.w(TAG, "Wiping adoptable storage");
+            Slogf.w(TAG, "Wiping adoptable storage");
             StorageManager sm = mContext.getSystemService(StorageManager.class);
             sm.wipeAdoptableDisks();
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index 0b9ece4..48d2d73 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -39,13 +39,13 @@
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
-import android.util.Slog;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManager;
 import android.view.inputmethod.InputMethodInfo;
 
 import com.android.internal.R;
 import com.android.server.inputmethod.InputMethodManagerInternal;
+import com.android.server.utils.Slogf;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -109,7 +109,7 @@
         }
 
         if (Log.isLoggable(LOG_TAG, Log.INFO)) {
-            Slog.i(LOG_TAG, "Packages subject to suspension: %s", String.join(",", result));
+            Slogf.i(LOG_TAG, "Packages subject to suspension: %s", String.join(",", result));
         }
         return result.toArray(new String[0]);
     }
@@ -123,7 +123,7 @@
         for (final ResolveInfo resolveInfo : matchingActivities) {
             if (resolveInfo.activityInfo == null
                     || TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) {
-                Slog.wtf(LOG_TAG, "Could not find package name for launcher app %s", resolveInfo);
+                Slogf.wtf(LOG_TAG, "Could not find package name for launcher app %s", resolveInfo);
                 continue;
             }
             final String packageName = resolveInfo.activityInfo.packageName;
@@ -134,7 +134,7 @@
                     result.add(packageName);
                 }
             } catch (PackageManager.NameNotFoundException e) {
-                Slog.e(LOG_TAG, "Could not find application info for launcher app: %s",
+                Slogf.e(LOG_TAG, "Could not find application info for launcher app: %s",
                         packageName);
             }
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
index 5f35a26..d7cbd9b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
@@ -45,11 +45,11 @@
 import android.provider.Settings;
 import android.text.format.DateUtils;
 import android.util.Pair;
-import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.server.utils.Slogf;
 
 import java.io.FileNotFoundException;
 import java.lang.annotation.Retention;
@@ -135,7 +135,7 @@
         if (targetInfo != null) {
             dialogIntent.setComponent(targetInfo.getComponentName());
         } else {
-            Slog.wtf(LOG_TAG, "Failed to resolve intent for remote bugreport dialog");
+            Slogf.wtf(LOG_TAG, "Failed to resolve intent for remote bugreport dialog");
         }
 
         // Simple notification clicks are immutable
@@ -191,7 +191,7 @@
     public boolean requestBugreport() {
         if (mRemoteBugreportServiceIsActive.get()
                 || (mService.getDeviceOwnerRemoteBugreportUriAndHash() != null)) {
-            Slog.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running.");
+            Slogf.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running");
             return false;
         }
 
@@ -208,7 +208,7 @@
             return true;
         } catch (RemoteException re) {
             // should never happen
-            Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
+            Slogf.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
             return false;
         } finally {
             mInjector.binderRestoreCallingIdentity(callingIdentity);
@@ -222,7 +222,7 @@
             mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
         } catch (IntentFilter.MalformedMimeTypeException e) {
             // should never happen, as setting a constant
-            Slog.w(LOG_TAG, e, "Failed to set type %s", BUGREPORT_MIMETYPE);
+            Slogf.w(LOG_TAG, e, "Failed to set type %s", BUGREPORT_MIMETYPE);
         }
         final IntentFilter filterConsent = new IntentFilter();
         filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 63488f9..f843ea4 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -150,6 +150,11 @@
     return ok();
 }
 
+binder::Status BinderIncrementalService::onInstallationComplete(int32_t storageId) {
+    mImpl.onInstallationComplete(storageId);
+    return ok();
+}
+
 binder::Status BinderIncrementalService::makeBindMount(int32_t storageId,
                                                        const std::string& sourcePath,
                                                        const std::string& targetFullPath,
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index ebb23dc..5c8741b 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -52,6 +52,8 @@
             const ::android::sp<IStorageHealthListener>& healthListener,
             const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
             bool* _aidl_return) final;
+    binder::Status onInstallationComplete(int32_t storageId) final;
+
     binder::Status makeBindMount(int32_t storageId, const std::string& sourcePath,
                                  const std::string& targetFullPath, int32_t bindType,
                                  int32_t* _aidl_return) final;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 6695ba8..388f932 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -100,6 +100,12 @@
     return (s & (Constants::blockSize - 1)) == 0;
 }
 
+static bool getAlwaysEnableReadTimeoutsForSystemDataLoaders() {
+    return android::base::
+            GetBoolProperty("debug.incremental.always_enable_read_timeouts_for_system_dataloaders",
+                            true);
+}
+
 static bool getEnforceReadLogsMaxIntervalForSystemDataLoaders() {
     return android::base::GetBoolProperty("debug.incremental.enforce_readlogs_max_interval_for_"
                                           "system_dataloaders",
@@ -315,19 +321,11 @@
     ::rmdir(path::c_str(root));
 }
 
-void IncrementalService::IncFsMount::setReadLogsEnabled(bool value) {
+void IncrementalService::IncFsMount::setFlag(StorageFlags flag, bool value) {
     if (value) {
-        flags |= StorageFlags::ReadLogsEnabled;
+        flags |= flag;
     } else {
-        flags &= ~StorageFlags::ReadLogsEnabled;
-    }
-}
-
-void IncrementalService::IncFsMount::setReadLogsRequested(bool value) {
-    if (value) {
-        flags |= StorageFlags::ReadLogsRequested;
-    } else {
-        flags &= ~StorageFlags::ReadLogsRequested;
+        flags &= ~flag;
     }
 }
 
@@ -728,10 +726,17 @@
             LOG(INFO) << "Skipped data loader stub creation because it already exists";
             return false;
         }
+
         prepareDataLoaderLocked(*ifs, std::move(dataLoaderParams), std::move(statusListener),
                                 healthCheckParams, std::move(healthListener));
         CHECK(ifs->dataLoaderStub);
         dataLoaderStub = ifs->dataLoaderStub;
+
+        // Disable long read timeouts for non-system dataloaders.
+        // To be re-enabled after installation is complete.
+        ifs->setReadTimeoutsRequested(dataLoaderStub->isSystemDataLoader() &&
+                                      getAlwaysEnableReadTimeoutsForSystemDataLoaders());
+        applyStorageParamsLocked(*ifs);
     }
 
     if (dataLoaderStub->isSystemDataLoader() &&
@@ -765,6 +770,18 @@
     return dataLoaderStub->requestStart();
 }
 
+void IncrementalService::onInstallationComplete(StorageId storage) {
+    IfsMountPtr ifs = getIfs(storage);
+    if (!ifs) {
+        return;
+    }
+
+    // Always enable long read timeouts after installation is complete.
+    std::unique_lock l(ifs->lock);
+    ifs->setReadTimeoutsRequested(true);
+    applyStorageParamsLocked(*ifs);
+}
+
 IncrementalService::BindPathMap::const_iterator IncrementalService::findStorageLocked(
         std::string_view path) const {
     return findParentPath(mBindsByPath, path);
@@ -868,7 +885,7 @@
         if (!ifs->readLogsRequested()) {
             return 0;
         }
-        if (auto status = applyStorageParamsLocked(*ifs, /*enableReadLogs=*/true); status != 0) {
+        if (auto status = applyStorageParamsLocked(*ifs); status != 0) {
             return status;
         }
     }
@@ -880,10 +897,10 @@
 
 int IncrementalService::disableReadLogsLocked(IncFsMount& ifs) {
     ifs.setReadLogsRequested(false);
-    return applyStorageParamsLocked(ifs, /*enableReadLogs=*/false);
+    return applyStorageParamsLocked(ifs);
 }
 
-int IncrementalService::applyStorageParamsLocked(IncFsMount& ifs, bool enableReadLogs) {
+int IncrementalService::applyStorageParamsLocked(IncFsMount& ifs) {
     os::incremental::IncrementalFileSystemControlParcel control;
     control.cmd.reset(dup(ifs.control.cmd()));
     control.pendingReads.reset(dup(ifs.control.pendingReads()));
@@ -892,11 +909,15 @@
         control.log.reset(dup(logsFd));
     }
 
+    bool enableReadLogs = ifs.readLogsRequested();
+    bool enableReadTimeouts = ifs.readTimeoutsRequested();
+
     std::lock_guard l(mMountOperationLock);
-    auto status = mVold->setIncFsMountOptions(control, enableReadLogs);
+    auto status = mVold->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
     if (status.isOk()) {
-        // Store enabled state.
+        // Store states.
         ifs.setReadLogsEnabled(enableReadLogs);
+        ifs.setReadTimeoutsEnabled(enableReadTimeouts);
     } else {
         LOG(ERROR) << "applyStorageParams failed: " << status.toString8();
     }
@@ -1271,7 +1292,7 @@
         maxPendingTimeUs = std::max(maxPendingTimeUs, microseconds(timeouts.maxPendingTimeUs));
     }
     if (maxPendingTimeUs < Constants::minPerUidTimeout) {
-        LOG(ERROR) << "Skip setting  read timeouts (maxPendingTime < Constants::minPerUidTimeout): "
+        LOG(ERROR) << "Skip setting read timeouts (maxPendingTime < Constants::minPerUidTimeout): "
                    << duration_cast<milliseconds>(maxPendingTimeUs).count() << "ms < "
                    << Constants::minPerUidTimeout.count() << "ms";
         return;
@@ -2946,8 +2967,10 @@
         return result;
     }
 
-    LOG(DEBUG) << id() << ": pendingReads: " << control.pendingReads() << ", "
-               << mLastPendingReads.size() << ": " << mLastPendingReads.front().bootClockTsUs;
+    LOG(DEBUG) << id() << ": pendingReads: fd(" << control.pendingReads() << "), count("
+               << mLastPendingReads.size() << "), block: " << mLastPendingReads.front().block
+               << ", time: " << mLastPendingReads.front().bootClockTsUs
+               << ", uid: " << mLastPendingReads.front().uid;
 
     return getOldestTsFromLastPendingReads();
 }
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index fb6f56c..e3b1e6f 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -118,6 +118,9 @@
         ReadLogsAllowed = 1 << 0,
         ReadLogsEnabled = 1 << 1,
         ReadLogsRequested = 1 << 2,
+
+        ReadTimeoutsEnabled = 1 << 3,
+        ReadTimeoutsRequested = 1 << 4,
     };
 
     struct LoadingProgress {
@@ -160,6 +163,7 @@
                       const StorageHealthCheckParams& healthCheckParams,
                       StorageHealthListener healthListener,
                       std::vector<PerUidReadTimeouts> perUidReadTimeouts);
+    void onInstallationComplete(StorageId storage);
 
     int bind(StorageId storage, std::string_view source, std::string_view target, BindKind kind);
     int unbind(StorageId storage, std::string_view target);
@@ -316,7 +320,7 @@
         } mHealthBase = {TimePoint::max(), kMaxBootClockTsUs};
         StorageHealthCheckParams mHealthCheckParams;
         int mStreamStatus = content::pm::IDataLoaderStatusListener::STREAM_HEALTHY;
-        std::vector<incfs::ReadInfo> mLastPendingReads;
+        std::vector<incfs::ReadInfoWithUid> mLastPendingReads;
     };
     using DataLoaderStubPtr = sp<DataLoaderStub>;
 
@@ -364,13 +368,32 @@
         void disallowReadLogs() { flags &= ~StorageFlags::ReadLogsAllowed; }
         int32_t readLogsAllowed() const { return (flags & StorageFlags::ReadLogsAllowed); }
 
-        void setReadLogsEnabled(bool value);
+        void setReadLogsEnabled(bool value) {
+            return setFlag(StorageFlags::ReadLogsEnabled, value);
+        }
         int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); }
 
-        void setReadLogsRequested(bool value);
+        void setReadLogsRequested(bool value) {
+            return setFlag(StorageFlags::ReadLogsRequested, value);
+        }
         int32_t readLogsRequested() const { return (flags & StorageFlags::ReadLogsRequested); }
 
+        void setReadTimeoutsEnabled(bool value) {
+            return setFlag(StorageFlags::ReadTimeoutsEnabled, value);
+        }
+        int32_t readTimeoutsEnabled() const { return (flags & StorageFlags::ReadTimeoutsEnabled); }
+
+        void setReadTimeoutsRequested(bool value) {
+            return setFlag(StorageFlags::ReadTimeoutsRequested, value);
+        }
+        int32_t readTimeoutsRequested() const {
+            return (flags & StorageFlags::ReadTimeoutsRequested);
+        }
+
         static void cleanupFilesystem(std::string_view root);
+
+    private:
+        void setFlag(StorageFlags flag, bool value);
     };
 
     using IfsMountPtr = std::shared_ptr<IncFsMount>;
@@ -422,7 +445,7 @@
     int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode);
 
     int disableReadLogsLocked(IncFsMount& ifs);
-    int applyStorageParamsLocked(IncFsMount& ifs, bool enableReadLogs);
+    int applyStorageParamsLocked(IncFsMount& ifs);
 
     LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
 
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 8e416f3..0755a22 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -55,8 +55,8 @@
     }
     binder::Status setIncFsMountOptions(
             const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
-            bool enableReadLogs) const final {
-        return mInterface->setIncFsMountOptions(control, enableReadLogs);
+            bool enableReadLogs, bool enableReadTimeouts) const final {
+        return mInterface->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
     }
 
 private:
@@ -233,8 +233,9 @@
     ErrorCode reserveSpace(const Control& control, FileId id, IncFsSize size) const final {
         return incfs::reserveSpace(control, id, size);
     }
-    WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
-                                   std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final {
+    WaitResult waitForPendingReads(
+            const Control& control, std::chrono::milliseconds timeout,
+            std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer) const final {
         return incfs::waitForPendingReads(control, timeout, pendingReadsBuffer);
     }
     ErrorCode setUidReadTimeouts(const Control& control,
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index d4cdcbe..78e9589 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -56,8 +56,8 @@
     virtual binder::Status bindMount(const std::string& sourceDir,
                                      const std::string& targetDir) const = 0;
     virtual binder::Status setIncFsMountOptions(
-            const os::incremental::IncrementalFileSystemControlParcel& control,
-            bool enableReadLogs) const = 0;
+            const os::incremental::IncrementalFileSystemControlParcel& control, bool enableReadLogs,
+            bool enableReadTimeouts) const = 0;
 };
 
 class DataLoaderManagerWrapper {
@@ -117,7 +117,7 @@
     virtual ErrorCode reserveSpace(const Control& control, FileId id, IncFsSize size) const = 0;
     virtual WaitResult waitForPendingReads(
             const Control& control, std::chrono::milliseconds timeout,
-            std::vector<incfs::ReadInfo>* pendingReadsBuffer) const = 0;
+            std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer) const = 0;
     virtual ErrorCode setUidReadTimeouts(
             const Control& control,
             const std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts)
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 1ec446d..14bcd4e 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -56,10 +56,10 @@
     MOCK_CONST_METHOD1(unmountIncFs, binder::Status(const std::string& dir));
     MOCK_CONST_METHOD2(bindMount,
                        binder::Status(const std::string& sourceDir, const std::string& argetDir));
-    MOCK_CONST_METHOD2(
+    MOCK_CONST_METHOD3(
             setIncFsMountOptions,
             binder::Status(const ::android::os::incremental::IncrementalFileSystemControlParcel&,
-                           bool));
+                           bool, bool));
 
     void mountIncFsFails() {
         ON_CALL(*this, mountIncFs(_, _, _, _))
@@ -83,12 +83,13 @@
         ON_CALL(*this, bindMount(_, _)).WillByDefault(Return(binder::Status::ok()));
     }
     void setIncFsMountOptionsFails() const {
-        ON_CALL(*this, setIncFsMountOptions(_, _))
+        ON_CALL(*this, setIncFsMountOptions(_, _, _))
                 .WillByDefault(Return(
                         binder::Status::fromExceptionCode(1, String8("failed to set options"))));
     }
     void setIncFsMountOptionsSuccess() {
-        ON_CALL(*this, setIncFsMountOptions(_, _)).WillByDefault(Return(binder::Status::ok()));
+        ON_CALL(*this, setIncFsMountOptions(_, _, _))
+                .WillByDefault(Invoke(this, &MockVoldService::setIncFsMountOptionsOk));
     }
     binder::Status getInvalidControlParcel(const std::string& imagePath,
                                            const std::string& targetDir, int32_t flags,
@@ -103,10 +104,23 @@
         _aidl_return->log.reset(base::unique_fd(dup(STDIN_FILENO)));
         return binder::Status::ok();
     }
+    binder::Status setIncFsMountOptionsOk(
+            const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
+            bool enableReadLogs, bool enableReadTimeouts) {
+        mReadLogsEnabled = enableReadLogs;
+        mReadTimeoutsEnabled = enableReadTimeouts;
+        return binder::Status::ok();
+    }
+
+    bool readLogsEnabled() const { return mReadLogsEnabled; }
+    bool readTimeoutsEnabled() const { return mReadTimeoutsEnabled; }
 
 private:
     TemporaryFile cmdFile;
     TemporaryFile logFile;
+
+    bool mReadLogsEnabled = false;
+    bool mReadTimeoutsEnabled = true;
 };
 
 class MockDataLoader : public IDataLoader {
@@ -395,7 +409,7 @@
     MOCK_CONST_METHOD3(reserveSpace, ErrorCode(const Control& control, FileId id, IncFsSize size));
     MOCK_CONST_METHOD3(waitForPendingReads,
                        WaitResult(const Control& control, std::chrono::milliseconds timeout,
-                                  std::vector<incfs::ReadInfo>* pendingReadsBuffer));
+                                  std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer));
     MOCK_CONST_METHOD2(setUidReadTimeouts,
                        ErrorCode(const Control& control,
                                  const std::vector<PerUidReadTimeouts>& perUidReadTimeouts));
@@ -435,7 +449,7 @@
         ON_CALL(*this, waitForPendingReads(_, _, _))
                 .WillByDefault(
                         Invoke([ts](const Control& control, std::chrono::milliseconds timeout,
-                                    std::vector<incfs::ReadInfo>* pendingReadsBuffer) {
+                                    std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer) {
                             pendingReadsBuffer->push_back({.bootClockTsUs = ts});
                             return android::incfs::WaitResult::HaveData;
                         }));
@@ -1302,8 +1316,10 @@
 
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+    // on startLoading
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
     // We are calling setIncFsMountOptions(true).
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1325,8 +1341,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1353,8 +1369,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(2);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(2);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1394,8 +1410,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(3);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1435,8 +1451,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(5);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(3);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1448,9 +1464,14 @@
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
 
+    // Before install - long timeouts.
+    ASSERT_TRUE(mVold->readTimeoutsEnabled());
+
     auto dataLoaderParcel = mDataLoaderParcel;
     ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(dataLoaderParcel), {}, {},
                                                   {}, {}));
+    // During install - short timeouts.
+    ASSERT_FALSE(mVold->readTimeoutsEnabled());
 
     // Disable readlogs callback present.
     ASSERT_EQ(storageId, mTimedQueue->mId);
@@ -1463,9 +1484,15 @@
     mClock->advance(90min);
     ASSERT_GE(mDataLoader->setStorageParams(true), 0);
 
+    mIncrementalService->onInstallationComplete(storageId);
+    // After install - long timeouts.
+    ASSERT_TRUE(mVold->readTimeoutsEnabled());
+
     // New installation.
     ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
                                                   {}, {}));
+    // New installation - short timeouts.
+    ASSERT_FALSE(mVold->readTimeoutsEnabled());
 
     // New callback present.
     ASSERT_EQ(storageId, mTimedQueue->mId);
@@ -1485,6 +1512,10 @@
     // And timeout.
     mClock->advance(90min);
     ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
+
+    mIncrementalService->onInstallationComplete(storageId);
+    // After install - long timeouts.
+    ASSERT_TRUE(mVold->readTimeoutsEnabled());
 }
 
 TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) {
@@ -1495,9 +1526,9 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // We are calling setIncFsMountOptions(true).
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
     // setIncFsMountOptions(false) is called on the callback.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // After callback is called, disable read logs and remove callback.
@@ -1520,7 +1551,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // checkPermission fails, no calls to set opitions,  start or stop WatchingMode.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
@@ -1539,7 +1571,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // checkPermission fails, no calls to set opitions,  start or stop WatchingMode.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
@@ -1559,7 +1592,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // We are calling setIncFsMountOptions.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
     // setIncFsMountOptions fails, no calls to start or stop WatchingMode.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index fbf677d..aad4208 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -454,6 +454,11 @@
     private static native void startSensorService();
 
     /**
+     * Start the memtrack proxy service.
+     */
+    private static native void startMemtrackProxyService();
+
+    /**
      * Start all HIDL services that are run inside the system server. This may take some time.
      */
     private static native void startHidlServices();
@@ -1024,6 +1029,12 @@
         mSystemServiceManager.startService(PowerStatsService.class);
         t.traceEnd();
 
+        // Start MemtrackProxyService before ActivityManager, so that early calls
+        // to Memtrack::getMemory() don't fail.
+        t.traceBegin("MemtrackProxyService");
+        startMemtrackProxyService();
+        t.traceEnd();
+
         // Activity manager runs the show.
         t.traceBegin("StartActivityManager");
         // TODO: Might need to move after migration to WM.
@@ -2667,7 +2678,7 @@
 
             t.traceBegin("RegisterAppOpsPolicy");
             try {
-                mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy());
+                mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy(mSystemContext));
             } catch (Throwable e) {
                 reportWtf("registering app ops policy", e);
             }
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 75614d6..9f24d9a 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -63,6 +63,7 @@
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -105,6 +106,7 @@
 public class DataManager {
 
     private static final String TAG = "DataManager";
+    private static final boolean DEBUG = false;
 
     private static final long RECENT_NOTIFICATIONS_MAX_AGE_MS = 10 * DateUtils.DAY_IN_MILLIS;
     private static final long QUERY_EVENTS_MAX_AGE_MS = 5L * DateUtils.MINUTE_IN_MILLIS;
@@ -217,6 +219,7 @@
         List<ShortcutInfo> shortcuts = getShortcuts(packageName, userId,
                 Collections.singletonList(shortcutId));
         if (shortcuts != null && !shortcuts.isEmpty()) {
+            if (DEBUG) Log.d(TAG, "Found shortcut for " + shortcuts.get(0).getLabel());
             return shortcuts.get(0);
         }
         return null;
@@ -258,6 +261,7 @@
         }
         ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
         if (shortcutInfo == null) {
+            Slog.e(TAG, " Shortcut no longer found: " + shortcutId);
             return null;
         }
         int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
@@ -705,6 +709,7 @@
             }
         });
         for (String packageName : packagesToDelete) {
+            if (DEBUG) Log.d(TAG, "Deleting packages data for: " + packageName);
             userData.deletePackageData(packageName);
         }
     }
@@ -716,6 +721,7 @@
         @ShortcutQuery.QueryFlags int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC
                 | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
                 | ShortcutQuery.FLAG_MATCH_CACHED | ShortcutQuery.FLAG_GET_PERSONS_DATA;
+        if (DEBUG) Log.d(TAG, " Get shortcuts with IDs: " + shortcutIds);
         return mShortcutServiceInternal.getShortcuts(
                 UserHandle.USER_SYSTEM, mContext.getPackageName(),
                 /*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null,
@@ -742,7 +748,7 @@
         TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
         String defaultDialer = telecomManager != null
                 ? telecomManager.getDefaultDialerPackage(
-                        new UserHandle(userData.getUserId())) : null;
+                new UserHandle(userData.getUserId())) : null;
         userData.setDefaultDialer(defaultDialer);
     }
 
@@ -848,6 +854,9 @@
         ConversationStore conversationStore = packageData.getConversationStore();
         ConversationInfo oldConversationInfo =
                 conversationStore.getConversation(shortcutInfo.getId());
+        if (oldConversationInfo == null) {
+            if (DEBUG) Log.d(TAG, "Nothing previously stored about conversation.");
+        }
         ConversationInfo.Builder builder = oldConversationInfo != null
                 ? new ConversationInfo.Builder(oldConversationInfo)
                 : new ConversationInfo.Builder();
@@ -1083,6 +1092,7 @@
                 Set<String> shortcutIds = new HashSet<>();
                 for (ShortcutInfo shortcutInfo : shortcuts) {
                     if (packageData != null) {
+                        if (DEBUG) Log.d(TAG, "Deleting shortcut: " + shortcutInfo.getId());
                         packageData.deleteDataForConversation(shortcutInfo.getId());
                     }
                     shortcutIds.add(shortcutInfo.getId());
@@ -1309,6 +1319,7 @@
             int userId = getChangingUserId();
             UserData userData = getUnlockedUserData(userId);
             if (userData != null) {
+                if (DEBUG) Log.d(TAG, "Delete package data for: " + packageName);
                 userData.deletePackageData(packageName);
             }
         }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index 46487ea2..411c31c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -17,8 +17,12 @@
 package com.android.server.pm
 
 import android.os.Build
+import android.os.Handler
 import android.provider.DeviceConfig
 import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
 import com.android.server.apphibernation.AppHibernationManagerInternal
 import com.android.server.extendedtestutils.wheneverStatic
 import com.android.server.testutils.whenever
@@ -28,12 +32,12 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(JUnit4::class)
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
 class PackageManagerServiceHibernationTests {
 
     companion object {
@@ -60,6 +64,8 @@
         rule.system().stageNominalSystemState()
         whenever(rule.mocks().injector.getLocalService(AppHibernationManagerInternal::class.java))
             .thenReturn(appHibernationManager)
+        whenever(rule.mocks().injector.handler)
+            .thenReturn(Handler(TestableLooper.get(this).looper))
     }
 
     @Test
@@ -74,6 +80,9 @@
         ps!!.setStopped(true, TEST_USER_ID)
 
         pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
+
+        TestableLooper.get(this).processAllMessages()
+
         verify(appHibernationManager).setHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID, false)
         verify(appHibernationManager).setHibernatingGlobally(TEST_PACKAGE_NAME, false)
     }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java
index 01a641f..b8535c2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java
@@ -119,36 +119,36 @@
         when(mAccessibilitySecurityPolicy.isA11yCategoryService(
                 mMockA11yServiceInfo)).thenReturn(false);
 
-        mFakeNotificationController.onReceive(mContext, createIntent(TEST_USER_ID,
-                PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
-                TEST_COMPONENT_NAME.flattenToShortString()));
+        mFakeNotificationController.onReceive(mContext,
+                PolicyWarningUIController.createIntent(mContext, TEST_USER_ID,
+                        PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
+                        TEST_COMPONENT_NAME));
 
         verify(mNotificationManager).notify(eq(TEST_COMPONENT_NAME.flattenToShortString()),
-                eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any(
-                        Notification.class));
+                eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any(Notification.class));
     }
 
     @Test
     public void receiveActionA11ySettings_launchA11ySettingsAndDismissNotification() {
         mFakeNotificationController.onReceive(mContext,
-                createIntent(TEST_USER_ID, PolicyWarningUIController.ACTION_A11Y_SETTINGS,
-                        TEST_COMPONENT_NAME.flattenToShortString()));
+                PolicyWarningUIController.createIntent(mContext, TEST_USER_ID,
+                        PolicyWarningUIController.ACTION_A11Y_SETTINGS,
+                        TEST_COMPONENT_NAME));
 
         verifyLaunchA11ySettings();
         verify(mNotificationManager).cancel(TEST_COMPONENT_NAME.flattenToShortString(),
                 NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
-        assertNotifiedSettingsEqual(TEST_USER_ID,
-                TEST_COMPONENT_NAME.flattenToShortString());
+        assertNotifiedSettingsEqual(TEST_USER_ID, TEST_COMPONENT_NAME.flattenToShortString());
     }
 
     @Test
     public void receiveActionDismissNotification_addToNotifiedSettings() {
-        mFakeNotificationController.onReceive(mContext, createIntent(TEST_USER_ID,
-                PolicyWarningUIController.ACTION_DISMISS_NOTIFICATION,
-                TEST_COMPONENT_NAME.flattenToShortString()));
+        mFakeNotificationController.onReceive(mContext,
+                PolicyWarningUIController.createIntent(mContext, TEST_USER_ID,
+                        PolicyWarningUIController.ACTION_DISMISS_NOTIFICATION,
+                        TEST_COMPONENT_NAME));
 
-        assertNotifiedSettingsEqual(TEST_USER_ID,
-                TEST_COMPONENT_NAME.flattenToShortString());
+        assertNotifiedSettingsEqual(TEST_USER_ID, TEST_COMPONENT_NAME.flattenToShortString());
     }
 
     @Test
@@ -172,8 +172,7 @@
 
         verify(mAlarmManager).set(eq(RTC_WAKEUP), anyLong(),
                 eq(PolicyWarningUIController.createPendingIntent(mContext, TEST_USER_ID,
-                        PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
-                        TEST_COMPONENT_NAME.flattenToShortString())));
+                        PolicyWarningUIController.ACTION_SEND_NOTIFICATION, TEST_COMPONENT_NAME)));
     }
 
     @Test
@@ -184,8 +183,7 @@
 
         verify(mAlarmManager).cancel(
                 eq(PolicyWarningUIController.createPendingIntent(mContext, TEST_USER_ID,
-                        PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
-                        TEST_COMPONENT_NAME.flattenToShortString())));
+                        PolicyWarningUIController.ACTION_SEND_NOTIFICATION, TEST_COMPONENT_NAME)));
     }
 
     private void assertNotifiedSettingsEqual(int userId, String settingString) {
@@ -196,14 +194,6 @@
         assertEquals(settingString, notifiedServicesSetting);
     }
 
-    private Intent createIntent(int userId, String action, String serviceComponentName) {
-        final Intent intent = new Intent(action);
-        intent.setPackage(mContext.getPackageName())
-                .setIdentifier(serviceComponentName)
-                .putExtra(Intent.EXTRA_USER_ID, userId);
-        return intent;
-    }
-
     private void verifyLaunchA11ySettings() {
         final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         final ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 81be2e7..b7f5f4d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -331,6 +331,29 @@
     }
 
     @Test
+    public void onAccessibilityActionPerformed_magnifierEnabled_showMagnificationButton()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_WINDOW);
+
+        mMagnificationController.onAccessibilityActionPerformed(TEST_DISPLAY);
+
+        verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+                eq(MODE_WINDOW));
+    }
+
+    @Test
+    public void onAccessibilityActionPerformed_capabilityNotAll_removeMagnificationButton()
+            throws RemoteException {
+        mMagnificationController.setMagnificationCapabilities(
+                ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+        setMagnificationEnabled(MODE_WINDOW);
+
+        mMagnificationController.onAccessibilityActionPerformed(TEST_DISPLAY);
+
+        verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
+    }
+
+    @Test
     public void onWindowMagnificationActivationState_windowActivated_logWindowDuration() {
         mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index ffa0185..a20272a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -313,6 +313,17 @@
     }
 
     @Test
+    public void onAccessibilityActionPerformed_magnifierEnabled_notifyAction()
+            throws RemoteException {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+        mMockConnection.getConnectionCallback().onAccessibilityActionPerformed(TEST_DISPLAY);
+
+        verify(mMockCallback).onAccessibilityActionPerformed(eq(TEST_DISPLAY));
+    }
+
+    @Test
     public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
         mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index fc4804b..41237c8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -72,6 +72,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.util.IntArray;
 
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
@@ -121,7 +122,8 @@
         UidRecord.CHANGE_GONE,
         UidRecord.CHANGE_GONE | UidRecord.CHANGE_IDLE,
         UidRecord.CHANGE_IDLE,
-        UidRecord.CHANGE_ACTIVE
+        UidRecord.CHANGE_ACTIVE,
+        UidRecord.CHANGE_CAPABILITY,
     };
 
     private static PackageManagerInternal sPackageManagerInternal;
@@ -528,8 +530,10 @@
             ActivityManager.UID_OBSERVER_GONE,
             ActivityManager.UID_OBSERVER_IDLE,
             ActivityManager.UID_OBSERVER_ACTIVE,
+            ActivityManager.UID_OBSERVER_CAPABILITY,
             ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
                     | ActivityManager.UID_OBSERVER_ACTIVE | ActivityManager.UID_OBSERVER_IDLE
+                    | ActivityManager.UID_OBSERVER_CAPABILITY
         };
         final IUidObserver[] observers = new IUidObserver.Stub[changesToObserve.length];
         for (int i = 0; i < observers.length; ++i) {
@@ -553,7 +557,16 @@
             ActivityManager.PROCESS_STATE_NONEXISTENT,
             ActivityManager.PROCESS_STATE_CACHED_EMPTY,
             ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
-            ActivityManager.PROCESS_STATE_TOP
+            ActivityManager.PROCESS_STATE_TOP,
+            ActivityManager.PROCESS_STATE_TOP,
+        };
+        final int[] capabilitiesForPendingUidRecords = {
+            ActivityManager.PROCESS_CAPABILITY_ALL,
+            ActivityManager.PROCESS_CAPABILITY_NONE,
+            ActivityManager.PROCESS_CAPABILITY_NONE,
+            ActivityManager.PROCESS_CAPABILITY_NONE,
+            ActivityManager.PROCESS_CAPABILITY_NONE,
+            ActivityManager.PROCESS_CAPABILITY_NETWORK,
         };
         final Map<Integer, ChangeRecord> changeItems = new HashMap<>();
         for (int i = 0; i < changesForPendingUidRecords.length; ++i) {
@@ -562,6 +575,7 @@
             pendingChange.uid = i;
             pendingChange.procState = procStatesForPendingUidRecords[i];
             pendingChange.procStateSeq = i;
+            pendingChange.capability = capabilitiesForPendingUidRecords[i];
             changeItems.put(changesForPendingUidRecords[i], pendingChange);
             addPendingUidChange(pendingChange);
         }
@@ -606,20 +620,26 @@
                             verify(observer).onUidGone(changeItem.uid, changeItem.ephemeral);
                         });
             }
-            if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+            if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) != 0
+                    || (changeToObserve & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
                 // Observer listens to uid procState changes, so change items corresponding to
                 // UidRecord.CHANGE_PROCSTATE or UidRecord.CHANGE_IDLE or UidRecord.CHANGE_ACTIVE
                 // needs to be delivered to this observer.
-                final int[] changesToVerify = {
-                        UidRecord.CHANGE_PROCSTATE,
-                        UidRecord.CHANGE_ACTIVE,
-                        UidRecord.CHANGE_IDLE
-                };
-                verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems,
+                final IntArray changesToVerify = new IntArray();
+                if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
+                    changesToVerify.add(UidRecord.CHANGE_CAPABILITY);
+                } else {
+                    changesToVerify.add(UidRecord.CHANGE_PROCSTATE);
+                    changesToVerify.add(UidRecord.CHANGE_ACTIVE);
+                    changesToVerify.add(UidRecord.CHANGE_IDLE);
+                    changesToVerify.add(UidRecord.CHANGE_CAPABILITY);
+                }
+                verifyObserverReceivedChanges(observerToTest, changesToVerify.toArray(),
+                        changeItems,
                         (observer, changeItem) -> {
                             verify(observer).onUidStateChanged(changeItem.uid,
                                     changeItem.procState, changeItem.procStateSeq,
-                                    ActivityManager.PROCESS_CAPABILITY_NONE);
+                                    changeItem.capability);
                         });
             }
             // Verify there are no other callbacks for this observer.
@@ -725,11 +745,11 @@
             ActivityManager.PROCESS_STATE_CACHED_EMPTY,
             ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
             ActivityManager.PROCESS_STATE_SERVICE,
-            ActivityManager.PROCESS_STATE_RECEIVER
+            ActivityManager.PROCESS_STATE_RECEIVER,
         };
         final ArrayList<ChangeRecord> pendingItemsForUids =
-                new ArrayList<>(changesForPendingItems.length);
-        for (int i = 0; i < changesForPendingItems.length; ++i) {
+                new ArrayList<>(procStatesForPendingItems.length);
+        for (int i = 0; i < procStatesForPendingItems.length; ++i) {
             final ChangeRecord item = new ChangeRecord();
             item.uid = i;
             item.change = changesForPendingItems[i];
diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
index 1de5f6f..0db118d 100644
--- a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NETWORK;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_RECENT;
 import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
@@ -109,6 +110,7 @@
 
     @Test
     public void testMergeWithPendingChange() {
+        // Map of expectedChange -> {(currentChange, pendingChange)}
         final SparseArray<Pair<Integer, Integer>> changesToVerify = new SparseArray<>();
 
         changesToVerify.put(UidRecord.CHANGE_ACTIVE,
@@ -127,6 +129,8 @@
                 Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_ACTIVE));
         changesToVerify.put(UidRecord.CHANGE_GONE,
                 Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_CACHED));
+        changesToVerify.put(UidRecord.CHANGE_PROCSTATE | UidRecord.CHANGE_CAPABILITY,
+                Pair.create(UidRecord.CHANGE_PROCSTATE, UidRecord.CHANGE_CAPABILITY));
 
         for (int i = 0; i < changesToVerify.size(); ++i) {
             final int expectedChange = changesToVerify.keyAt(i);
@@ -149,7 +153,8 @@
                 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_ACTIVE,
                 PROCESS_STATE_IMPORTANT_FOREGROUND, TEST_PKG2, TEST_UID2);
         final IUidObserver observer2 = mock(IUidObserver.Stub.class);
-        registerObserver(observer2, ActivityManager.UID_OBSERVER_PROCSTATE,
+        registerObserver(observer2,
+                ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CAPABILITY,
                 PROCESS_STATE_SERVICE, TEST_PKG3, TEST_UID3);
 
         mUidObserverController.dispatchUidsChanged();
@@ -177,6 +182,14 @@
         verifyNoMoreInteractions(observer1);
         verifyNoMoreInteractions(observer2);
 
+        addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE | UidRecord.CHANGE_CAPABILITY,
+                PROCESS_STATE_RECEIVER, 111, PROCESS_CAPABILITY_NETWORK, false);
+        mUidObserverController.dispatchUidsChanged();
+        verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_RECEIVER,
+                111, PROCESS_CAPABILITY_NETWORK);
+        verifyNoMoreInteractions(observer1);
+        verifyNoMoreInteractions(observer2);
+
         unregisterObserver(observer1);
 
         addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_TOP,
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
index 24a8b61..edc0d46 100644
--- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -235,4 +235,37 @@
         assertEquals(GameManager.GAME_MODE_STANDARD,
                 gameManagerService.getGameMode(mPackageName, USER_ID_1));
     }
+
+    /**
+     * Test game modes are user-specific.
+     */
+    @Test
+    public void testGameModeMultipleUsers() {
+        GameManagerService gameManagerService = new GameManagerService(mMockContext);
+        gameManagerService.onUserStarting(USER_ID_1);
+        gameManagerService.onUserStarting(USER_ID_2);
+
+        mockModifyGameModeGranted();
+
+        // Set User 1 to Standard
+        gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+        assertEquals(GameManager.GAME_MODE_STANDARD,
+                gameManagerService.getGameMode(mPackageName, USER_ID_1));
+
+        // Set User 2 to Performance and verify User 1 is still Standard
+        gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE,
+                USER_ID_2);
+        assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+                gameManagerService.getGameMode(mPackageName, USER_ID_2));
+        assertEquals(GameManager.GAME_MODE_STANDARD,
+                gameManagerService.getGameMode(mPackageName, USER_ID_1));
+
+        // Set User 1 to Battery and verify User 2 is still Performance
+        gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY,
+                USER_ID_1);
+        assertEquals(GameManager.GAME_MODE_BATTERY,
+                gameManagerService.getGameMode(mPackageName, USER_ID_1));
+        assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+                gameManagerService.getGameMode(mPackageName, USER_ID_2));
+    }
 }
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 c34c00d..ba4d585 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
@@ -988,6 +988,49 @@
     }
 
     @Test
+    public void testClearPackageData() throws AppSearchException {
+        List<SchemaTypeConfigProto> existingSchemas =
+                mAppSearchImpl.getSchemaProtoLocked().getTypesList();
+
+        // Insert package schema
+        List<AppSearchSchema> schema =
+                ImmutableList.of(new AppSearchSchema.Builder("schema").build());
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                schema,
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false,
+                /*version=*/ 0);
+
+        // Insert package document
+        GenericDocument document =
+                new GenericDocument.Builder<>("namespace", "uri", "schema").build();
+        mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
+
+        // Verify the document is indexed.
+        SearchSpec searchSpec =
+                new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
+        SearchResultPage searchResultPage =
+                mAppSearchImpl.query("package", "database", /*queryExpression=*/ "", searchSpec);
+        assertThat(searchResultPage.getResults()).hasSize(1);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
+
+        // Remove the package
+        mAppSearchImpl.clearPackageData("package");
+
+        // Verify the document is cleared.
+        searchResultPage =
+                mAppSearchImpl.query("package2", "database2", /*queryExpression=*/ "", searchSpec);
+        assertThat(searchResultPage.getResults()).isEmpty();
+
+        // Verify the schema is cleared.
+        assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
+                .containsExactlyElementsIn(existingSchemas);
+    }
+
+    @Test
     public void testGetPackageToDatabases() throws Exception {
         Map<String, Set<String>> existingMapping = mAppSearchImpl.getPackageToDatabases();
         Map<String, Set<String>> expectedMapping = new ArrayMap<>();
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 4295172..7cd6028 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -346,6 +346,9 @@
         DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
         displayDeviceInfo.width = 100;
         displayDeviceInfo.height = 200;
+        displayDeviceInfo.supportedModes = new Display.Mode[1];
+        displayDeviceInfo.supportedModes[0] = new Display.Mode(1, 100, 200, 60f);
+        displayDeviceInfo.modeId = 1;
         final Rect zeroRect = new Rect();
         displayDeviceInfo.displayCutout = new DisplayCutout(
                 Insets.of(0, 10, 0, 0),
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index bcd853c..d784a22 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -239,6 +239,9 @@
         displayDeviceInfo.width = width;
         displayDeviceInfo.height = height;
         displayDeviceInfo.flags = flags;
+        displayDeviceInfo.supportedModes = new Display.Mode[1];
+        displayDeviceInfo.supportedModes[0] = new Display.Mode(1, width, height, 60f);
+        displayDeviceInfo.modeId = 1;
         displayDeviceInfo.address = new DisplayAddressImpl();
         return device;
     }
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index f5876fa..e9e2486 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -18,6 +18,12 @@
 
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.NETWORK_STACK;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
+import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY;
+import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE;
+import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
@@ -29,10 +35,17 @@
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_FOREGROUND;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_SYSTEM;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.allowedReasonsToString;
+import static android.net.NetworkPolicyManager.blockedReasonsToString;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
@@ -59,6 +72,7 @@
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
+import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -134,8 +148,10 @@
 import android.util.ArrayMap;
 import android.util.DataUnit;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Range;
 import android.util.RecurrenceRule;
+import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
@@ -1896,6 +1912,65 @@
         assertFalse(mService.isUidNetworkingBlocked(UID_E, false));
     }
 
+    @Test
+    public void testUpdateEffectiveBlockedReasons() {
+        final SparseArray<Pair<Integer, Integer>> effectiveBlockedReasons = new SparseArray<>();
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_NONE, ALLOWED_REASON_NONE));
+
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_REASON_DOZE,
+                        ALLOWED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER,
+                        ALLOWED_METERED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER
+                                | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_SYSTEM));
+
+        effectiveBlockedReasons.put(BLOCKED_METERED_REASON_DATA_SAVER,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_METERED_REASON_DATA_SAVER,
+                        ALLOWED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_APP_STANDBY,
+                Pair.create(BLOCKED_REASON_APP_STANDBY | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_SYSTEM));
+
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_REASON_DOZE,
+                        ALLOWED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER, ALLOWED_METERED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER
+                                | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_METERED_REASON_DATA_SAVER,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_METERED_REASON_DATA_SAVER,
+                        ALLOWED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_BATTERY_SAVER,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER
+                                | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_FOREGROUND));
+        // TODO: test more combinations of blocked reasons.
+
+        for (int i = 0; i < effectiveBlockedReasons.size(); ++i) {
+            final int expectedEffectiveBlockedReasons = effectiveBlockedReasons.keyAt(i);
+            final int blockedReasons = effectiveBlockedReasons.valueAt(i).first;
+            final int allowedReasons = effectiveBlockedReasons.valueAt(i).second;
+            final String errorMsg = "Expected="
+                    + blockedReasonsToString(expectedEffectiveBlockedReasons)
+                    + "; blockedReasons=" + blockedReasonsToString(blockedReasons)
+                    + ", allowedReasons=" + allowedReasonsToString(allowedReasons);
+            assertEquals(errorMsg, expectedEffectiveBlockedReasons,
+                    getEffectiveBlockedReasons(blockedReasons, allowedReasons));
+        }
+    }
+
     private String formatBlockedStateError(int uid, int rule, boolean metered,
             boolean backgroundRestricted) {
         return String.format(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
index c69ef8d..a2ad89e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
@@ -56,7 +56,7 @@
     }
 
     private StatusBarNotification getNotification(String pkg, int id, UserHandle user) {
-        Notification n = new Notification.Builder(getContext(), "test")
+        Notification n = new Notification.Builder(getContext(), "test" + id)
                 .setContentTitle("A")
                 .setWhen(1205)
                 .build();
@@ -140,4 +140,23 @@
             assertThat(expected).contains(sbn.getKey());
         }
     }
+
+    @Test
+    public void testRemoveChannelNotifications() {
+        List<String> expected = new ArrayList<>();
+        for (int i = 0; i < SIZE; i++) {
+            StatusBarNotification sbn = getNotification("pkg", i, UserHandle.of(USER_CURRENT));
+            mArchive.record(sbn, REASON_CANCEL);
+            if (i != 3) {
+                // Will delete notification for this user in channel "test3".
+                expected.add(sbn.getKey());
+            }
+        }
+        mArchive.removeChannelNotifications("pkg", USER_CURRENT, "test3");
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+        assertThat(actual).hasSize(expected.size());
+        for (StatusBarNotification sbn : actual) {
+            assertThat(expected).contains(sbn.getKey());
+        }
+    }
 }
diff --git a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
index f805904..55cbc72 100644
--- a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
+++ b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
@@ -174,7 +174,7 @@
         try {
             callbackRunnable.runOrThrow();
         } catch (RemoteException ex) {
-            Slog.w(TAG, "Failed running callback method", ex);
+            Slog.i(TAG, "Failed running callback method: " + ex);
         }
     }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index ccaeaf9..9aded89 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1439,7 +1439,6 @@
 
         @Override
         public ComponentName getActiveServiceComponentName() {
-            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
             synchronized (this) {
                 return mImpl != null ? mImpl.mComponent : null;
             }
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 5e50bea..d250297 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -571,7 +571,7 @@
      *
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
-    public static void enforeceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
+    public static void enforceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
             Context context, int subId, String message) {
         if (context.checkCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE)
                 == PERMISSION_GRANTED) {
@@ -591,7 +591,7 @@
      *
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
-    public static void enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+    public static void enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
             Context context, int subId, String message) {
         if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
                 == PERMISSION_GRANTED) {
@@ -613,7 +613,7 @@
      *
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
-    public static void enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+    public static void enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
             Context context, int subId, String message) {
         if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
                 == PERMISSION_GRANTED) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 24fcf46..cc3b6c5 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3298,6 +3298,14 @@
     public static final String KEY_USE_CALLER_ID_USSD_BOOL = "use_caller_id_ussd_bool";
 
     /**
+     * Call waiting uses USSD command without SS command.
+     * When {@code true}, the call waiting query/set by ussd command.
+     * When {@code false}, doesn't use USSD to query/set call waiting.
+     * @hide
+     */
+    public static final String KEY_USE_CALL_WAITING_USSD_BOOL = "use_call_waiting_ussd_bool";
+
+    /**
      * Specifies the service class for call waiting service.
      * Default value is
      * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_VOICE}.
@@ -5377,6 +5385,7 @@
         sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL, true);
         sDefaults.putBoolean(KEY_USE_CALL_FORWARDING_USSD_BOOL, false);
         sDefaults.putBoolean(KEY_USE_CALLER_ID_USSD_BOOL, false);
+        sDefaults.putBoolean(KEY_USE_CALL_WAITING_USSD_BOOL, false);
         sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
         sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
                 "connected_mmwave:5G,connected:5G,not_restricted_rrc_idle:5G,"
diff --git a/telephony/java/android/telephony/ims/RcsConfig.java b/telephony/java/android/telephony/ims/RcsConfig.java
index 07e95cc..8a31211 100644
--- a/telephony/java/android/telephony/ims/RcsConfig.java
+++ b/telephony/java/android/telephony/ims/RcsConfig.java
@@ -48,6 +48,9 @@
     private static final String LOG_TAG = "RcsConfig";
     private static final boolean DBG = Build.IS_ENG;
 
+    // Tag for Rcs Volte single registration defined in RCC.07 A.1.6.2
+    private static final String TAG_SINGLE_REGISTRATION = "rcsVolteSingleRegistration";
+
     private final HashMap<String, String> mValues = new HashMap<>();
 
     private RcsConfig(HashMap<String, String> values) {
@@ -145,6 +148,14 @@
         return mValues.containsKey(tag);
     }
 
+    /**
+     * Check whether Rcs Volte single registration is supported by the config.
+     */
+    public boolean isRcsVolteSingleRegistrationSupported() {
+        return getBoolean(TAG_SINGLE_REGISTRATION, false)
+                || getInteger(TAG_SINGLE_REGISTRATION, 0) != 0;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index b529563..d21fcab 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -204,7 +204,9 @@
 
     /**
      * @return the UTF-8 encoded SIP message.
+     * @deprecated Use {@link #toEncodedMessage} instead
      */
+    @Deprecated
     public @NonNull byte[] getEncodedMessage() {
         byte[] header = new StringBuilder()
                 .append(mStartLine)
@@ -216,4 +218,26 @@
         System.arraycopy(mContent, 0, sipMessage, header.length, mContent.length);
         return sipMessage;
     }
+
+    /**
+     * According RFC-3261 section 7, SIP is a text protocol and uses the UTF-8 charset. Its format
+     * consists of a start-line, one or more header fields, an empty line indicating the end of the
+     * header fields, and an optional message-body.
+     *
+     * <p>
+     * Returns a byte array with UTF-8 format representation of the encoded SipMessage.
+     *
+     * @return byte array with UTF-8 format representation of the encoded SipMessage.
+     */
+    public @NonNull byte[] toEncodedMessage() {
+        byte[] header = new StringBuilder()
+                .append(mStartLine)
+                .append(mHeaderSection)
+                .append(CRLF)
+                .toString().getBytes(UTF_8);
+        byte[] sipMessage = new byte[header.length + mContent.length];
+        System.arraycopy(header, 0, sipMessage, 0, header.length);
+        System.arraycopy(mContent, 0, sipMessage, header.length, mContent.length);
+        return sipMessage;
+    }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index af7eb59..93a2bb0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -63,6 +63,7 @@
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_BIP;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
@@ -89,6 +90,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VSIM;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
 import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
@@ -3030,10 +3032,11 @@
         // Verify NOT_RESTRICTED is set appropriately
         final NetworkCapabilities nc = new NetworkRequest.Builder().addCapability(capability)
                 .build().networkCapabilities;
-        if (capability == NET_CAPABILITY_CBS || capability == NET_CAPABILITY_DUN ||
-                capability == NET_CAPABILITY_EIMS || capability == NET_CAPABILITY_FOTA ||
-                capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS ||
-                capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP
+        if (capability == NET_CAPABILITY_CBS || capability == NET_CAPABILITY_DUN
+                || capability == NET_CAPABILITY_EIMS || capability == NET_CAPABILITY_FOTA
+                || capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS
+                || capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP
+                || capability == NET_CAPABILITY_VSIM || capability == NET_CAPABILITY_BIP
                 || capability == NET_CAPABILITY_ENTERPRISE) {
             assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
         } else {
@@ -3168,6 +3171,8 @@
         tryNetworkFactoryRequests(NET_CAPABILITY_INTERNET);
         tryNetworkFactoryRequests(NET_CAPABILITY_TRUSTED);
         tryNetworkFactoryRequests(NET_CAPABILITY_NOT_VPN);
+        tryNetworkFactoryRequests(NET_CAPABILITY_VSIM);
+        tryNetworkFactoryRequests(NET_CAPABILITY_BIP);
         // Skipping VALIDATED and CAPTIVE_PORTAL as they're disallowed.
     }
 
@@ -9251,7 +9256,7 @@
 
         final int expectedOwnerUidWithoutIncludeFlag =
                 shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag
-                        ? Process.myUid() : INVALID_UID;
+                        ? myUid : INVALID_UID;
         assertEquals(expectedOwnerUidWithoutIncludeFlag, getOwnerUidNetCapsPermission(
                 myUid, myUid, false /* includeLocationSensitiveInfo */));
 
@@ -9270,22 +9275,26 @@
 
     }
 
+    private void verifyOwnerUidAndTransportInfoNetCapsPermissionPreS() {
+        verifyOwnerUidAndTransportInfoNetCapsPermission(
+                // Ensure that owner uid is included even if the request asks to remove it (which is
+                // the default) since the app has necessary permissions and targetSdk < S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                // Ensure that location info is removed if the request asks to remove it even if the
+                // app has necessary permissions.
+                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
+        );
+    }
+
     @Test
-    public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQ()
+    public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQPreS()
             throws Exception {
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        verifyOwnerUidAndTransportInfoNetCapsPermission(
-                // Ensure that we include owner uid even if the request asks to remove it since the
-                // app has necessary permissions and targetSdk < S.
-                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                // Ensure that we remove location info if the request asks to remove it even if the
-                // app has necessary permissions.
-                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
-        );
+        verifyOwnerUidAndTransportInfoNetCapsPermissionPreS();
     }
 
     @Test
@@ -9294,16 +9303,7 @@
         setupLocationPermissions(Build.VERSION_CODES.R, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        verifyOwnerUidAndTransportInfoNetCapsPermission(
-                // Ensure that we include owner uid even if the request asks to remove it since the
-                // app has necessary permissions and targetSdk < S.
-                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                // Ensure that we remove location info if the request asks to remove it even if the
-                // app has necessary permissions.
-                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
-        );
+        verifyOwnerUidAndTransportInfoNetCapsPermissionPreS();
     }
 
     @Test
@@ -9314,13 +9314,13 @@
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
         verifyOwnerUidAndTransportInfoNetCapsPermission(
-                // Ensure that we owner UID if the request asks us to remove it even if the app
-                // has necessary permissions since targetSdk >= S.
+                // Ensure that the owner UID is removed if the request asks us to remove it even
+                // if the app has necessary permissions since targetSdk >= S.
                 false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
                 true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                // Ensure that we remove location info if the request asks to remove it even if the
+                // Ensure that location info is removed if the request asks to remove it even if the
                 // app has necessary permissions.
+                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
                 true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
         );
     }
@@ -9331,15 +9331,15 @@
         setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
+        verifyOwnerUidAndTransportInfoNetCapsPermissionPreS();
+    }
+
+    private void verifyOwnerUidAndTransportInfoNetCapsNotIncluded() {
         verifyOwnerUidAndTransportInfoNetCapsPermission(
-                // Ensure that we owner UID if the request asks us to remove it even if the app
-                // has necessary permissions since targetSdk >= S.
-                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
                 false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                // Ensure that we remove location info if the request asks to remove it even if the
-                // app has necessary permissions.
-                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
+                false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
         );
     }
 
@@ -9349,12 +9349,7 @@
         setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        verifyOwnerUidAndTransportInfoNetCapsPermission(
-                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
-        );
+        verifyOwnerUidAndTransportInfoNetCapsNotIncluded();
     }
 
     @Test
@@ -9376,26 +9371,17 @@
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        verifyOwnerUidAndTransportInfoNetCapsPermission(
-                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
-        );
+        verifyOwnerUidAndTransportInfoNetCapsNotIncluded();
     }
 
     @Test
-    public void testCreateWithLocationInfoSanitizedWithoutLocationPermission()
+    public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterS()
             throws Exception {
         // Test that not having fine location permission leads to sanitization.
-        setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
+        setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_COARSE_LOCATION,
+                Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        verifyOwnerUidAndTransportInfoNetCapsPermission(
-                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
-        );
+        verifyOwnerUidAndTransportInfoNetCapsNotIncluded();
     }
 
     @Test
diff --git a/tools/hiddenapi/OWNERS b/tools/hiddenapi/OWNERS
new file mode 100644
index 0000000..afbeef5
--- /dev/null
+++ b/tools/hiddenapi/OWNERS
@@ -0,0 +1,7 @@
+# compat-team@ for changes to hiddenapi files
+andreionea@google.com
+mathewi@google.com
+satayev@google.com
+
+# soong-team@ as the files these tools protect are tightly coupled with Soong
+file:platform/build/soong:/OWNERS
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
index 73eacc0..2924e01 100755
--- a/tools/hiddenapi/exclude.sh
+++ b/tools/hiddenapi/exclude.sh
@@ -7,11 +7,9 @@
 # the team email to use in the event of this detecting an entry in a <team> package. Also
 # add <team> to the TEAMS list. 
 LIBCORE_PACKAGES="\
-  android.icu \
   android.system \
   android.test \
   com.android.bouncycastle \
-  com.android.conscrypt \
   com.android.i18n.phonenumbers \
   com.android.okhttp \
   com.sun \
@@ -24,37 +22,54 @@
   org.json \
   org.w3c.dom \
   org.xml.sax \
+  org.xmlpull.v1 \
   sun \
   "
 LIBCORE_EMAIL=libcore-team@android.com
 
+I18N_PACKAGES="\
+  android.icu \
+  "
+
+I18N_EMAIL=$LIBCORE_EMAIL
+
+CONSCRYPT_PACKAGES="\
+  com.android.org.conscrypt \
+  "
+
+CONSCRYPT_EMAIL=$LIBCORE_EMAIL
+
 # List of teams.
-TEAMS=LIBCORE
+TEAMS="LIBCORE I18N CONSCRYPT"
+
+SHA=$1
 
 # Generate the list of packages and convert to a regular expression.
 PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
 RE=$(echo ${PACKAGES} | sed "s/ /|/g")
-git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
-    ENTRIES=$(grep -E "^L(${RE})/" || true <(git show $1:$file))
+EXIT_CODE=0
+for file in $(git show --name-only --pretty=format: $SHA | grep "config/hiddenapi-.*txt"); do
+    ENTRIES=$(grep -E "^\+L(${RE})/" <(git diff ${SHA}~1 ${SHA} $file) | sed "s|^\+||" || echo)
     if [[ -n "${ENTRIES}" ]]; then
-      echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m"
+      echo -e "\e[1m\e[31m$file $SHA contains the following entries\e[0m"
       echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m"
       echo -e "\e[1m\e[31mthese entries and add annotations instead.\e[0m"
       # Partition the entries by team and provide contact details to aid in fixing the issue.
       for t in ${TEAMS}
       do
         PACKAGES=$(eval echo \${${t}_PACKAGES})
-        RE=$(echo ${PACKAGES} | sed "s/ /|/g")
-        TEAM_ENTRIES=$(grep -E "^L(${RE})/" <(echo "${ENTRIES}"))
+        TEAM_RE=$(echo ${PACKAGES} | sed "s/ /|/g")
+        TEAM_ENTRIES=$(grep -E "^L(${TEAM_RE})/" <(echo "${ENTRIES}") || echo)
         if [[ -n "${TEAM_ENTRIES}" ]]; then
           EMAIL=$(eval echo \${${t}_EMAIL})
-          echo -e "\e[33mContact ${EMAIL} or compat- for help with the following:\e[0m"
-          for i in ${ENTRIES}
+          echo -e "\e[33mContact ${EMAIL} for help with the following:\e[0m"
+          for i in ${TEAM_ENTRIES}
           do
             echo -e "\e[33m  ${i}\e[0m"
           done
         fi
       done
-      exit 1
+      EXIT_CODE=1
     fi
 done
+exit $EXIT_CODE