Merge "Correctly fade sparkles in" 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/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 5f3a3ae..b46e85d 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -68,6 +68,10 @@
 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 {
@@ -77,6 +81,14 @@
     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.
@@ -157,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);
@@ -166,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
@@ -214,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
@@ -241,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
@@ -269,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
@@ -312,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,
@@ -359,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
@@ -394,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
@@ -424,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
@@ -470,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
@@ -509,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
@@ -567,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
@@ -603,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
@@ -643,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
@@ -673,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
@@ -712,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(mContext, 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/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..081163b 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
@@ -121,54 +121,81 @@
     private final SparseArray<UidDefaultNetworkCallback> mCurrentDefaultNetworkCallbacks =
             new SparseArray<>();
     private final Comparator<UidStats> mUidStatsComparator = new Comparator<UidStats>() {
-        private int prioritizeExistence(int v1, int v2) {
-            if (v1 > 0 && v2 > 0) {
+        private int prioritizeExistenceOver(int threshold, int v1, int v2) {
+            // Check if they're both on the same side of the threshold.
+            if ((v1 > threshold && v2 > threshold) || (v1 <= threshold && v2 <= threshold)) {
                 return 0;
             }
-            return v2 - v1;
+            // They're on opposite sides of the threshold.
+            if (v1 > threshold) {
+                return -1;
+            }
+            return 1;
         }
 
         @Override
         public int compare(UidStats us1, UidStats us2) {
-            // TODO: build a better prioritization scheme
-            // Some things to use:
-            //   * Proc state
-            //   * IMPORTANT_WHILE_IN_FOREGROUND bit
-            final int runningPriority = prioritizeExistence(us1.numRunning, us2.numRunning);
+            // Prioritize a UID ahead of another based on:
+            //   1. Already running connectivity jobs (so we don't drop the listener)
+            //   2. Waiting connectivity jobs would be ready with connectivity
+            //   3. An existing network satisfies a waiting connectivity job's requirements
+            //   4. TOP proc state
+            //   5. Existence of treat-as-EJ EJs (not just requested EJs)
+            //   6. FGS proc state
+            //   7. EJ enqueue time
+            //   8. Any other important job priorities/proc states
+            //   9. Enqueue time
+            // TODO: maybe consider number of jobs
+            // TODO: consider IMPORTANT_WHILE_FOREGROUND bit
+            final int runningPriority = prioritizeExistenceOver(0, us1.numRunning, us2.numRunning);
             if (runningPriority != 0) {
                 return runningPriority;
             }
             // Prioritize any UIDs that have jobs that would be ready ahead of UIDs that don't.
-            final int readyWithConnPriority =
-                    prioritizeExistence(us1.numReadyWithConnectivity, us2.numReadyWithConnectivity);
+            final int readyWithConnPriority = prioritizeExistenceOver(0,
+                    us1.numReadyWithConnectivity, us2.numReadyWithConnectivity);
             if (readyWithConnPriority != 0) {
                 return readyWithConnPriority;
             }
             // They both have jobs that would be ready. Prioritize the UIDs whose requested
             // network is available ahead of UIDs that don't have their requested network available.
-            final int reqAvailPriority = prioritizeExistence(
+            final int reqAvailPriority = prioritizeExistenceOver(0,
                     us1.numRequestedNetworkAvailable, us2.numRequestedNetworkAvailable);
             if (reqAvailPriority != 0) {
                 return reqAvailPriority;
             }
-            // They both have jobs with available networks. Prioritize based on:
-            //   1. (eventually) proc state
-            //   2. Existence of runnable EJs (not just requested)
-            //   3. Enqueue time
-            // TODO: maybe consider number of jobs
-            final int ejPriority = prioritizeExistence(us1.numEJs, us2.numEJs);
+            // Prioritize the top app. If neither are top apps, then use a later prioritization
+            // check.
+            final int topPriority = prioritizeExistenceOver(JobInfo.PRIORITY_TOP_APP - 1,
+                    us1.basePriority, us2.basePriority);
+            if (topPriority != 0) {
+                return topPriority;
+            }
+            // They're either both TOP or both not TOP. Prioritize the app that has runnable EJs
+            // pending.
+            final int ejPriority = prioritizeExistenceOver(0, us1.numEJs, us2.numEJs);
             if (ejPriority != 0) {
                 return ejPriority;
             }
-            // They both have EJs. Order them by EJ enqueue time to help provide low EJ latency.
+            // They both have runnable EJs.
+            // Prioritize an FGS+ app. If neither are FGS+ apps, then use a later prioritization
+            // check.
+            final int fgsPriority = prioritizeExistenceOver(JobInfo.PRIORITY_FOREGROUND_SERVICE - 1,
+                    us1.basePriority, us2.basePriority);
+            if (fgsPriority != 0) {
+                return fgsPriority;
+            }
+            // Order them by EJ enqueue time to help provide low EJ latency.
             if (us1.earliestEJEnqueueTime < us2.earliestEJEnqueueTime) {
                 return -1;
             } else if (us1.earliestEJEnqueueTime > us2.earliestEJEnqueueTime) {
                 return 1;
             }
+            // Order by any latent important proc states.
             if (us1.basePriority != us2.basePriority) {
                 return us2.basePriority - us1.basePriority;
             }
+            // Order by enqueue time.
             if (us1.earliestEnqueueTime < us2.earliestEnqueueTime) {
                 return -1;
             }
@@ -244,7 +271,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();
@@ -477,7 +507,7 @@
         UidStats uidStats = mUidStats.get(uid);
         if (uidStats != null && uidStats.basePriority != newPriority) {
             uidStats.basePriority = newPriority;
-            maybeAdjustRegisteredCallbacksLocked();
+            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/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 0871287..88080f0 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);
@@ -12441,7 +12441,7 @@
     method public void setAppIcon(@Nullable android.graphics.Bitmap);
     method public void setAppLabel(@Nullable CharSequence);
     method public void setAppPackageName(@Nullable String);
-    method public void setAutoRevokePermissionsMode(boolean);
+    method @Deprecated public void setAutoRevokePermissionsMode(boolean);
     method public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
     method public void setInstallLocation(int);
     method public void setInstallReason(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
@@ -52857,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>);
   }
@@ -55492,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);
@@ -55565,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 40f26e8..0772478 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -69,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";
@@ -2508,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();
@@ -9439,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();
   }
 
@@ -14171,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;
@@ -14180,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);
   }
 
@@ -14240,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 e50432e..35890c8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4335,17 +4335,10 @@
 
     private String getBackupAgentName(CreateBackupAgentData data) {
         String agentName = data.appInfo.backupAgentName;
-        if (!UserHandle.isCore(data.appInfo.uid)
-                && data.operationType == BackupManager.OperationType.MIGRATION) {
-            // If this is a migration, use the default backup agent regardless of the app's
-            // preferences.
+        // full backup operation but no app-supplied agent?  use the default implementation
+        if (agentName == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
+                || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) {
             agentName = DEFAULT_FULL_BACKUP_AGENT;
-        } else {
-            // full backup operation but no app-supplied agent?  use the default implementation
-            if (agentName == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
-                    || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) {
-                agentName = DEFAULT_FULL_BACKUP_AGENT;
-            }
         }
         return agentName;
     }
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/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/NotificationManager.java b/core/java/android/app/NotificationManager.java
index f0d580f..da03a3d 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -419,6 +419,14 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Importance {}
 
+    /** @hide */
+    @IntDef(prefix = { "BUBBLE_PREFERENCE_" }, value = {
+            BUBBLE_PREFERENCE_NONE, BUBBLE_PREFERENCE_SELECTED,
+            BUBBLE_PREFERENCE_ALL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BubblePreference {}
+
     /**
      * Activity Action: Launch an Automatic Zen Rule configuration screen
      * <p>
@@ -1379,7 +1387,7 @@
      * @see Notification#getBubbleMetadata()
      * @return the users' bubble preference for the app.
      */
-    public int getBubblePreference() {
+    public @BubblePreference int getBubblePreference() {
         INotificationManager service = getService();
         try {
             return service.getBubblePreferenceForPackage(mContext.getPackageName(),
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 94a4fde0..0841910 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -544,11 +544,6 @@
     }
 
     private Set<String> getExtraExcludeDirsIfAny(Context context) throws IOException {
-        if (isDeviceToDeviceMigration()) {
-            return Collections.emptySet();
-        }
-
-        // If this is not a migration, also exclude no-backup and cache dirs.
         Set<String> excludedDirs = new HashSet<>();
         excludedDirs.add(context.getCacheDir().getCanonicalPath());
         excludedDirs.add(context.getCodeCacheDir().getCanonicalPath());
@@ -556,10 +551,6 @@
         return Collections.unmodifiableSet(excludedDirs);
     }
 
-    private boolean isDeviceToDeviceMigration() {
-        return mOperationType == OperationType.MIGRATION;
-    }
-
     /** @hide */
     @VisibleForTesting
     public IncludeExcludeRules getIncludeExcludeRules(FullBackup.BackupScheme backupScheme)
@@ -905,11 +896,6 @@
     }
 
     private boolean isFileEligibleForRestore(File destination) throws IOException {
-        if (isDeviceToDeviceMigration()) {
-            // Everything is eligible for device-to-device migration.
-            return true;
-        }
-
         FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this, mOperationType);
         if (!bs.isFullRestoreEnabled()) {
             if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
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/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/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/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/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3be7f74..0be77e0 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1808,7 +1808,10 @@
          * If user explicitly enabled or disabled it via settings, this call is ignored.
          *
          * @param shouldAutoRevoke whether permissions should be auto-revoked.
+         *
+         * @deprecated No longer used
          */
+        @Deprecated
         public void setAutoRevokePermissionsMode(boolean shouldAutoRevoke) {
             autoRevokePermissionsMode = shouldAutoRevoke ? MODE_ALLOWED : MODE_IGNORED;
         }
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 9290497..bfbcfd8 100644
--- a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
@@ -275,7 +275,7 @@
          * Returns the index of collection
          */
         public @IntRange(from = 0) int getIndex() {
-            return 0;
+            return mIndex;
         }
     }
 
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 b427df7..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;
 
@@ -375,7 +369,7 @@
             // for it's uid and package name, save it.
             int usageId = usage.getPackageIdHash();
             OpUsage lastMostRecent = mostRecentUsages.get(usageId);
-            if (!usage.packageName.equals(SYSTEM_PKG) && (lastMostRecent == null
+            if (shouldShowPackage(usage.packageName) && (lastMostRecent == null
                     || usage.lastAccessTime > lastMostRecent.lastAccessTime)) {
                 mostRecentUsages.put(usageId, usage);
             }
@@ -401,8 +395,7 @@
                     // 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++;
@@ -411,7 +404,6 @@
                     }
                 }
 
-
                 if (currentUsage == null || iterNum == maxUsages
                         || currentUsage.getPackageIdHash() == start.getPackageIdHash()) {
                     // We have an invalid state, or a cycle, so break
@@ -421,7 +413,7 @@
                 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();
@@ -451,17 +443,8 @@
         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);
     }
 
     /**
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/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/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/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/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/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_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index b207ad3..04528e9 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -16,6 +16,8 @@
 
 #define ATRACE_TAG ATRACE_TAG_RESOURCES
 
+#include "signal.h"
+
 #include "android-base/logging.h"
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
@@ -353,8 +355,59 @@
   return reinterpret_cast<jlong>(apk_assets.release());
 }
 
+// STOPSHIP (b/159041693): Revert signal handler when reason for issue is found.
+static thread_local std::stringstream destroy_info;
+static struct sigaction old_handler_action;
+
+static void DestroyErrorHandler(int sig, siginfo_t* info, void* ucontext) {
+  if (sig != SIGSEGV) {
+    return;
+  }
+
+  LOG(ERROR) << "(b/159041693) - Failed to destroy ApkAssets " << destroy_info.str();
+  if (old_handler_action.sa_handler == SIG_DFL) {
+      // reset the action to default and re-raise the signal. It will kill the process
+      signal(sig, SIG_DFL);
+      raise(sig);
+      return;
+  }
+  if (old_handler_action.sa_handler == SIG_IGN) {
+      // ignoring SIGBUS won't help us much, as we'll get back right here after retrying.
+      return;
+  }
+  if (old_handler_action.sa_flags & SA_SIGINFO) {
+      old_handler_action.sa_sigaction(sig, info, ucontext);
+  } else {
+      old_handler_action.sa_handler(sig);
+  }
+}
+
 static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
-  delete reinterpret_cast<ApkAssets*>(ptr);
+  auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+  destroy_info << "{ptr=" << apk_assets;
+  if (apk_assets != nullptr) {
+    destroy_info << ", name='" << apk_assets->GetDebugName() << "'"
+                 << ", idmap=" << apk_assets->GetLoadedIdmap()
+                 << ", {arsc=" << apk_assets->GetLoadedArsc();
+    if (auto arsc = apk_assets->GetLoadedArsc()) {
+      destroy_info << ", strings=" << arsc->GetStringPool()
+                   << ", packages=" << &arsc->GetPackages()
+                   << " [";
+      for (auto& package : arsc->GetPackages()) {
+        destroy_info << "{unique_ptr=" << &package
+                     << ", package=" << package.get() << "},";
+      }
+      destroy_info << "]";
+    }
+    destroy_info << "}";
+  }
+  destroy_info << "}";
+
+  delete apk_assets;
+
+  // Deleting the apk assets did not lead to a crash.
+  destroy_info.str("");
+  destroy_info.clear();
 }
 
 static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
@@ -507,6 +560,17 @@
   jclass parcelFd = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
   gParcelFileDescriptorOffsets.detachFd = GetMethodIDOrDie(env, parcelFd, "detachFd", "()I");
 
+  // STOPSHIP (b/159041693): Revert signal handler when reason for issue is found.
+  sigset_t allowed;
+  sigemptyset(&allowed);
+  sigaddset(&allowed, SIGSEGV);
+  pthread_sigmask(SIG_UNBLOCK, &allowed, nullptr);
+  struct sigaction action = {
+          .sa_flags = SA_SIGINFO,
+          .sa_sigaction = &DestroyErrorHandler,
+  };
+  sigaction(SIGSEGV, &action, &old_handler_action);
+
   return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods,
                               arraysize(gApkAssetsMethods));
 }
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 46f2f3e..58abfeb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -345,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" />
@@ -1390,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" />
@@ -4240,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"
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..49bd853 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>
@@ -2580,10 +2572,6 @@
     <!-- Set to true if after a provisioning apn the radio should be restarted -->
     <bool name="config_restartRadioAfterProvisioning">false</bool>
 
-    <!-- Boolean indicating if RADIO POWER OFF is required on receiving SIM REFRESH with RESET.
-         This will be handled by modem if it is false. -->
-    <bool name="config_requireRadioPowerOffOnSimRefreshReset">false</bool>
-
     <!-- Vibrator pattern to be used as the default for notifications
          that specify DEFAULT_VIBRATE.
      -->
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..dbfb030 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -350,7 +350,6 @@
   <java-symbol type="bool" name="config_camera_sound_forced" />
   <java-symbol type="bool" name="config_dontPreferApn" />
   <java-symbol type="bool" name="config_restartRadioAfterProvisioning" />
-  <java-symbol type="bool" name="config_requireRadioPowerOffOnSimRefreshReset" />
   <java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" />
   <java-symbol type="bool" name="config_useFixedVolume" />
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
@@ -2180,10 +2179,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/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/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index 9ee1ef1..c317831 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -89,7 +89,7 @@
     }
 
     public boolean shouldAnimateSparkle() {
-        return mAnimateSparkle;
+        return mAnimateSparkle && ValueAnimator.getDurationScale() > 0;
     }
 
     public float getSparklePhase() {
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/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/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 5627a7e..77d99a6 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -180,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;
@@ -262,8 +249,16 @@
                 TransformCanvas transformCanvas(canvas);
                 displayList->draw(&transformCanvas);
             }
-            canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
-                                  SkCanvas::kStrict_SrcRectConstraint);
+
+            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/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/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 e2db2d6..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
diff --git a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
index 5a76cd6..2bb006d 100644
--- a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
+++ b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
@@ -40,6 +40,23 @@
  */
 @SystemApi
 public final class OemNetworkPreferences implements Parcelable {
+    // Valid production preferences must be > 0, negative values reserved for testing
+    /**
+     * This preference is only to be used for testing and nothing else.
+     * Use only TRANSPORT_TEST transport networks.
+     * @hide
+     */
+    public static final int OEM_NETWORK_PREFERENCE_TEST_ONLY = -2;
+
+    /**
+     * This preference is only to be used for testing and nothing else.
+     * If an unmetered network is available, use it.
+     * Otherwise, if a network with the TRANSPORT_TEST transport is available, use it.
+     * Otherwise, use the general default network.
+     * @hide
+     */
+    public static final int OEM_NETWORK_PREFERENCE_TEST = -1;
+
     /**
      * Default in case this value is not set. Using it will result in an error.
      */
@@ -69,6 +86,12 @@
      */
     public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
 
+    /**
+     * The max allowed value for an OEM network preference.
+     * @hide
+     */
+    public static final int OEM_NETWORK_PREFERENCE_MAX = OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+
     @NonNull
     private final Bundle mNetworkMappings;
 
@@ -96,7 +119,7 @@
 
     @Override
     public String toString() {
-        return "OemNetworkPreferences{" + "mNetworkMappings=" + mNetworkMappings + '}';
+        return "OemNetworkPreferences{" + "mNetworkMappings=" + getNetworkPreferences() + '}';
     }
 
     @Override
@@ -185,6 +208,8 @@
 
     /** @hide */
     @IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
+            OEM_NETWORK_PREFERENCE_TEST_ONLY,
+            OEM_NETWORK_PREFERENCE_TEST,
             OEM_NETWORK_PREFERENCE_UNINITIALIZED,
             OEM_NETWORK_PREFERENCE_OEM_PAID,
             OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK,
@@ -205,6 +230,10 @@
     @NonNull
     public static String oemNetworkPreferenceToString(@OemNetworkPreference int value) {
         switch (value) {
+            case OEM_NETWORK_PREFERENCE_TEST_ONLY:
+                return "OEM_NETWORK_PREFERENCE_TEST_ONLY";
+            case OEM_NETWORK_PREFERENCE_TEST:
+                return "OEM_NETWORK_PREFERENCE_TEST";
             case OEM_NETWORK_PREFERENCE_UNINITIALIZED:
                 return "OEM_NETWORK_PREFERENCE_UNINITIALIZED";
             case OEM_NETWORK_PREFERENCE_OEM_PAID:
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/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/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/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/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/res/drawable/ongoing_call_chip_bg.xml b/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
new file mode 100644
index 0000000..bdd6270
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="?android:attr/colorAccent" />
+    <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml
new file mode 100644
index 0000000..c90fc31
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml
@@ -0,0 +1,50 @@
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/ongoing_call_chip"
+    android:layout_width="wrap_content"
+    android:layout_height="@dimen/ongoing_appops_chip_height"
+    android:layout_gravity="center_vertical|start"
+    android:gravity="center_vertical"
+    android:background="@drawable/ongoing_call_chip_bg"
+    android:paddingStart="@dimen/ongoing_call_chip_side_padding"
+    android:paddingEnd="@dimen/ongoing_call_chip_side_padding"
+    android:visibility="gone"
+>
+
+    <ImageView
+        android:src="@*android:drawable/ic_phone"
+        android:layout_width="@dimen/ongoing_call_chip_icon_size"
+        android:layout_height="@dimen/ongoing_call_chip_icon_size"
+        android:paddingEnd="@dimen/ongoing_call_chip_icon_text_padding"
+        android:tint="?android:attr/colorPrimary"
+    />
+
+    <!-- TODO(b/183229367): The text in this view isn't quite centered within the chip. -->
+    <!-- TODO(b/183229367): This text view's width shouldn't change as the time increases. -->
+    <Chronometer
+        android:id="@+id/ongoing_call_chip_time"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:gravity="center"
+        android:textAppearance="@android:style/TextAppearance.Material.Small"
+        android:fontFamily="@*android:string/config_headlineFontFamily"
+        android:textColor="?android:attr/colorPrimary"
+    />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index f8db97d..8b244c7 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -82,6 +82,8 @@
                     android:gravity="center_vertical|start"
                 />
 
+                <include layout="@layout/ongoing_call_chip" />
+
                 <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                     android:id="@+id/notification_icon_area"
                     android:layout_width="0dp"
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/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 237edf8..e834f3f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1442,4 +1442,11 @@
     <dimen name="wallet_empty_state_corner_radius">24dp</dimen>
     <dimen name="wallet_tile_card_view_height">32dp</dimen>
     <dimen name="wallet_tile_card_view_width">50dp</dimen>
+
+    <!-- Ongoing call chip -->
+    <dimen name="ongoing_call_chip_side_padding">12dp</dimen>
+    <dimen name="ongoing_call_chip_icon_size">16dp</dimen>
+    <!-- The padding between the icon and the text. -->
+    <dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
+    <dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index c39db94..bbf2048 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -49,7 +49,7 @@
 
     <bool name="flag_charging_ripple">false</bool>
 
-    <bool name="flag_ongoing_call_status_bar_chip">false</bool>
+    <bool name="flag_ongoing_call_status_bar_chip">true</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 de14dbd..ba07829 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -623,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/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/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/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 405151d..aa818bf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -37,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;
@@ -243,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.
@@ -269,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();
@@ -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/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 4dd8780..6a012eb 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -38,8 +38,10 @@
     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;
+    public static final int SHADE_DRAG = 11;
+    public static final int QS_COLLAPSE = 12;
+    public static final int UDFPS_AUTHENTICATION = 13;
+    public static final int DISABLED_UDFPS_AFFORDANCE = 14;
 
     @IntDef({
             QUICK_SETTINGS,
@@ -53,6 +55,9 @@
             BOUNCER_UNLOCK,
             PULSE_EXPAND,
             BRIGHTNESS_SLIDER,
+            SHADE_DRAG,
+            QS_COLLAPSE,
+            BRIGHTNESS_SLIDER,
             UDFPS_AUTHENTICATION,
             DISABLED_UDFPS_AFFORDANCE
     })
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index 6a70622..a4e1637 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -22,6 +22,9 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
+import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
 
 import android.provider.DeviceConfig;
 import android.view.MotionEvent;
@@ -148,7 +151,9 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == Classifier.BRIGHTNESS_SLIDER
+        if (interactionType == BRIGHTNESS_SLIDER
+                || interactionType == SHADE_DRAG
+                || interactionType == QS_COLLAPSE
                 || 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/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index 6f80010..3bc24c7 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
 import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 
 import android.provider.DeviceConfig;
@@ -116,7 +117,8 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER) {
+        if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER
+                || interactionType == QS_COLLAPSE) {
             return Result.passed(0);
         }
 
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 50e94b3..1042516 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -23,8 +23,10 @@
 import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
 import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
 import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
+import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
 import static com.android.systemui.classifier.Classifier.UNLOCK;
 
 import javax.inject.Inject;
@@ -77,6 +79,12 @@
             case RIGHT_AFFORDANCE:  // Swiping from the bottom right corner for camera or similar.
                 wrongDirection = right || !up;
                 break;
+            case SHADE_DRAG:
+                wrongDirection = !vertical;
+                break;
+            case QS_COLLAPSE:
+                wrongDirection = !vertical || !up;
+                break;
             default:
                 wrongDirection = true;
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index d9197ef..e1349f2 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -21,6 +21,7 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
 import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
 
 import android.graphics.Point;
 import android.provider.DeviceConfig;
@@ -88,7 +89,7 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == BRIGHTNESS_SLIDER) {
+        if (interactionType == BRIGHTNESS_SLIDER || interactionType == SHADE_DRAG) {
             return Result.passed(0);
         }
 
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/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/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 1cb2683..375c79f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -279,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);
         }
     }
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/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 7ef88bb..5cebf7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -142,9 +142,9 @@
                 }
                 return true;
             case MotionEvent.ACTION_UP:
-                if (!mFalsingManager.isUnlockingDisabled() && !isFalseTouch()
-                        && mDragDownCallback.onDraggedDown(mStartingChild,
-                        (int) (y - mInitialTouchY))) {
+                if (!mFalsingManager.isUnlockingDisabled() && mDragDownCallback.canDragDown()
+                        && !isFalseTouch()) {
+                    mDragDownCallback.onDraggedDown(mStartingChild, (int) (y - mInitialTouchY));
                     if (mStartingChild == null) {
                         cancelExpansion();
                     } else {
@@ -263,7 +263,10 @@
         /**
          * @return true if the interaction is accepted, false if it should be cancelled
          */
-        boolean onDraggedDown(View startingChild, int dragLengthY);
+        boolean canDragDown();
+
+        /** Call when a view has been dragged. */
+        void onDraggedDown(View startingChild, int dragLengthY);
         void onDragDownReset();
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 14c73b5..20383fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -63,6 +63,7 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.SystemClock;
 import com.android.wm.shell.bubbles.Bubbles;
 
 import java.util.Optional;
@@ -234,9 +235,11 @@
     @Provides
     @SysUISingleton
     static OngoingCallController provideOngoingCallController(
-            CommonNotifCollection notifCollection, FeatureFlags featureFlags) {
+            CommonNotifCollection notifCollection,
+            FeatureFlags featureFlags,
+            SystemClock systemClock) {
         OngoingCallController ongoingCallController =
-                new OngoingCallController(notifCollection, featureFlags);
+                new OngoingCallController(notifCollection, featureFlags, systemClock);
         ongoingCallController.init();
         return ongoingCallController;
     }
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 207a894..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
@@ -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);
     }
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 ad06e7d..b28cc14 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
@@ -5499,9 +5499,16 @@
     @ShadeViewRefactor(RefactorComponent.INPUT)
     private final DragDownCallback mDragDownCallback = new DragDownCallback() {
 
+        @Override
+        public boolean canDragDown() {
+            return mStatusBarState == StatusBarState.KEYGUARD
+                    && (mController.hasActiveNotifications() || mKeyguardMediaControllorVisible)
+                    || mController.isInLockedDownShade();
+        }
+
         /* Only ever called as a consequence of a lockscreen expansion gesture. */
         @Override
-        public boolean onDraggedDown(View startingChild, int dragLengthY) {
+        public void onDraggedDown(View startingChild, int dragLengthY) {
             boolean canDragDown =
                     mController.hasActiveNotifications() || mKeyguardMediaControllorVisible;
             if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) {
@@ -5519,16 +5526,10 @@
                         row.onExpandedByGesture(true /* drag down is always an open */);
                     }
                 }
-
-                return true;
             } else if (mController.isInLockedDownShade()) {
                 mStatusbarStateController.setLeaveOpenOnKeyguardHide(true);
                 mStatusBar.dismissKeyguardThenExecute(() -> false /* dismissAction */,
                         null /* cancelRunnable */, false /* afterKeyguardGone */);
-                return true;
-            } else {
-                // abort gesture.
-                return false;
             }
         }
 
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 ce7b397..7776e69 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
@@ -59,10 +59,12 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
+import com.android.systemui.classifier.Classifier;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.media.KeyguardMediaController;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -143,6 +145,7 @@
     private final ZenModeController mZenModeController;
     private final MetricsLogger mMetricsLogger;
     private final FalsingCollector mFalsingCollector;
+    private final FalsingManager mFalsingManager;
     private final Resources mResources;
     private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
     private final ScrimController mScrimController;
@@ -556,6 +559,7 @@
             NotificationLockscreenUserManager lockscreenUserManager,
             MetricsLogger metricsLogger,
             FalsingCollector falsingCollector,
+            FalsingManager falsingManager,
             @Main Resources resources,
             NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
             StatusBar statusBar,
@@ -589,6 +593,7 @@
         mLockscreenUserManager = lockscreenUserManager;
         mMetricsLogger = metricsLogger;
         mFalsingCollector = falsingCollector;
+        mFalsingManager = falsingManager;
         mResources = resources;
         mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
         mStatusBar = statusBar;
@@ -1614,6 +1619,9 @@
                 }
             }
             if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+                // Ensure the falsing manager records the touch. we don't do anything with it
+                // at the moment.
+                mFalsingManager.isFalseTouch(Classifier.SHADE_DRAG);
                 mView.setCheckForLeaveBehind(true);
             }
             traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index f64a0e0..9d1c609 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
 import com.android.systemui.statusbar.policy.EncryptionHelper;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.NetworkController;
@@ -67,6 +68,7 @@
     private NetworkController mNetworkController;
     private LinearLayout mSystemIconArea;
     private View mClockView;
+    private ViewGroup mOngoingCallChip;
     private View mNotificationIconAreaInner;
     private View mCenteredIconArea;
     private int mDisabled1;
@@ -86,6 +88,20 @@
         }
     };
 
+    private final OngoingCallListener mOngoingCallListener = new OngoingCallListener() {
+        @Override
+        public void onOngoingCallStarted(boolean animate) {
+            disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate);
+            animateShow(mOngoingCallChip, animate);
+        }
+
+        @Override
+        public void onOngoingCallEnded(boolean animate) {
+            disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate);
+            animateHiddenState(mOngoingCallChip, View.GONE, animate);
+        }
+    };
+
     @Inject
     public CollapsedStatusBarFragment(OngoingCallController ongoingCallController) {
         mOngoingCallController = ongoingCallController;
@@ -142,6 +158,7 @@
         super.onResume();
         mCommandQueue.addCallback(this);
         mStatusBarStateController.addCallback(this);
+        mOngoingCallController.addCallback(mOngoingCallListener);
     }
 
     @Override
@@ -149,6 +166,7 @@
         super.onPause();
         mCommandQueue.removeCallback(this);
         mStatusBarStateController.removeCallback(this);
+        mOngoingCallController.removeCallback(mOngoingCallListener);
     }
 
     @Override
@@ -179,6 +197,8 @@
         }
         statusBarCenteredIconArea.addView(mCenteredIconArea);
 
+        initOngoingCallChip();
+
         // Default to showing until we know otherwise.
         showNotificationIconArea(false);
     }
@@ -228,6 +248,10 @@
             state |= DISABLE_CLOCK;
         }
 
+        if (mOngoingCallController.getHasOngoingCall()) {
+            state |= DISABLE_NOTIFICATION_ICONS;
+        }
+
         if (!mKeyguardStateController.isLaunchTransitionFadingAway()
                 && !mKeyguardStateController.isKeyguardFadingAway()
                 && shouldHideNotificationIcons()
@@ -395,6 +419,11 @@
         }
     }
 
+    private void initOngoingCallChip() {
+        mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
+        mOngoingCallController.setChipView(mOngoingCallChip);
+    }
+
     @Override
     public void onStateChanged(int newState) {
 
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 364b532..610369b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -24,6 +24,7 @@
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.systemui.classifier.Classifier.QS_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.stack.NotificationStackScrollLayout.ROWS_ALL;
@@ -1450,11 +1451,7 @@
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
                 trackMovement(event);
-                if (mQsTracking) {
-                    flingQsWithCurrentVelocity(y,
-                            event.getActionMasked() == MotionEvent.ACTION_CANCEL);
-                    mQsTracking = false;
-                }
+                mQsTracking = false;
                 break;
         }
         return false;
@@ -1526,10 +1523,17 @@
 
     private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
         float vel = getCurrentQSVelocity();
-        final boolean expandsQs = flingExpandsQs(vel);
+        boolean expandsQs = flingExpandsQs(vel);
         if (expandsQs) {
-            logQsSwipeDown(y);
+            if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) {
+                expandsQs = false;
+            } else {
+                logQsSwipeDown(y);
+            }
+        } else if (vel < 0) {
+            mFalsingManager.isFalseTouch(QS_COLLAPSE);
         }
+
         flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
     }
 
@@ -1545,9 +1549,6 @@
     }
 
     private boolean flingExpandsQs(float vel) {
-        if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) {
-            return false;
-        }
         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
             return getQsExpansionFraction() > 0.5f;
         } else {
@@ -1556,9 +1557,6 @@
     }
 
     private boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
-        if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) {
-            return false;
-        }
         if (mFalsingManager.isClassifierEnabled()) {
             return mFalsingManager.isFalseTouch(interactionType);
         }
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/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 77abe79..936e289 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.GENERIC;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.classifier.Classifier.UNLOCK;
 
@@ -430,9 +431,9 @@
                 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
                 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
             }
-            @Classifier.InteractionType int interactionType = vel > 0
-                    ? QUICK_SETTINGS : (
-                            mKeyguardStateController.canDismissLockScreen()
+            @Classifier.InteractionType int interactionType = vel == 0 ? GENERIC
+                    : vel > 0 ? QUICK_SETTINGS
+                            : (mKeyguardStateController.canDismissLockScreen()
                                     ? UNLOCK : BOUNCER_UNLOCK);
 
             fling(vel, expand, isFalseTouch(x, y, interactionType));
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 f1f34dc..c37ddd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -681,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);
                 }
             };
@@ -1822,7 +1825,7 @@
     }
 
     public boolean isFalsingThresholdNeeded() {
-        return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+        return true;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 60d3ea3..b55db6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -19,11 +19,16 @@
 import android.app.Notification
 import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
 import android.util.Log
+import android.view.ViewGroup
+import android.widget.Chronometer
+import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 
 /**
@@ -32,19 +37,39 @@
 @SysUISingleton
 class OngoingCallController @Inject constructor(
     private val notifCollection: CommonNotifCollection,
-    private val featureFlags: FeatureFlags
-) {
+    private val featureFlags: FeatureFlags,
+    private val systemClock: SystemClock
+) : CallbackController<OngoingCallListener> {
+
+    var hasOngoingCall = false
+        private set
+    private var chipView: ViewGroup? = null
+
+    private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
 
     private val notifListener = object : NotifCollectionListener {
         override fun onEntryUpdated(entry: NotificationEntry) {
-            if (isOngoingCallNotification(entry) && DEBUG) {
-                Log.d(TAG, "Ongoing call notification updated")
+            if (isOngoingCallNotification(entry)) {
+                val timeView = chipView?.findViewById<Chronometer>(R.id.ongoing_call_chip_time)
+                if (timeView != null) {
+                    hasOngoingCall = true
+                    val callStartTime = entry.sbn.notification.`when`
+                    timeView.base = callStartTime -
+                            System.currentTimeMillis() +
+                            systemClock.elapsedRealtime()
+                    timeView.start()
+                    mListeners.forEach { l -> l.onOngoingCallStarted(animate = true) }
+                } else if (DEBUG) {
+                    Log.w(TAG, "Ongoing call chip view could not be found; " +
+                            "Not displaying chip in status bar")
+                }
             }
         }
 
         override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
-            if (isOngoingCallNotification(entry) && DEBUG) {
-                Log.d(TAG, "Ongoing call notification removed")
+            if (isOngoingCallNotification(entry)) {
+                hasOngoingCall = false
+                mListeners.forEach { l -> l.onOngoingCallEnded(animate = true) }
             }
         }
     }
@@ -54,6 +79,24 @@
             notifCollection.addCollectionListener(notifListener)
         }
     }
+
+    fun setChipView(chipView: ViewGroup?) {
+        this.chipView = chipView
+    }
+
+    override fun addCallback(listener: OngoingCallListener) {
+        synchronized(mListeners) {
+            if (!mListeners.contains(listener)) {
+                mListeners.add(listener)
+            }
+        }
+    }
+
+    override fun removeCallback(listener: OngoingCallListener) {
+        synchronized(mListeners) {
+            mListeners.remove(listener)
+        }
+    }
 }
 
 private fun isOngoingCallNotification(entry: NotificationEntry): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt
new file mode 100644
index 0000000..7c583a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall
+
+/** A listener that's notified when an ongoing call is started or ended. */
+interface OngoingCallListener {
+    /** Called when an ongoing call is started. */
+    fun onOngoingCallStarted(animate: Boolean)
+
+    /** Called when an ongoing call is ended. */
+    fun onOngoingCallEnded(animate: Boolean)
+}
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/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/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/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 0f9ca7b..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();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
index 3c4fde8..895339f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
@@ -43,6 +43,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -160,6 +161,7 @@
                 mNotificationLockscreenUserManager,
                 mMetricsLogger,
                 new FalsingCollectorFake(),
+                new FalsingManagerFake(),
                 mResources,
                 mNotificationSwipeHelperBuilder,
                 mStatusBar,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 96cdaf9..67fd5eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -38,12 +38,16 @@
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
+import java.util.Objects;
+
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
@@ -53,6 +57,7 @@
     private View mNotificationAreaInner;
     private View mCenteredNotificationAreaView;
     private StatusBarStateController mStatusBarStateController;
+    private OngoingCallController mOngoingCallController;
 
     public CollapsedStatusBarFragmentTest() {
         super(CollapsedStatusBarFragment.class);
@@ -162,8 +167,51 @@
         Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
     }
 
+    @Test
+    public void onOngoingCallStarted_notificationsHiddenAndOngoingCallChipDisplayed() {
+        mFragments.dispatchResume();
+        processAllMessages();
+
+        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        fragment.initNotificationIconArea(mMockNotificiationAreaController);
+
+        ArgumentCaptor<OngoingCallListener> ongoingCallListenerCaptor = ArgumentCaptor.forClass(
+                OngoingCallListener.class);
+        Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture());
+        OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue());
+
+        when(mOngoingCallController.getHasOngoingCall()).thenReturn(true);
+        listener.onOngoingCallStarted(/* animate= */ false);
+
+        assertEquals(View.VISIBLE,
+                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+        Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+    }
+
+    @Test
+    public void onOngoingCallEnded_notificationsDisplayedAndOngoingCallChipHidden() {
+        mFragments.dispatchResume();
+        processAllMessages();
+
+        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        fragment.initNotificationIconArea(mMockNotificiationAreaController);
+
+        ArgumentCaptor<OngoingCallListener> ongoingCallListenerCaptor = ArgumentCaptor.forClass(
+                OngoingCallListener.class);
+        Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture());
+        OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue());
+
+        when(mOngoingCallController.getHasOngoingCall()).thenReturn(false);
+        listener.onOngoingCallEnded(/* animate= */ false);
+
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+        Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+    }
+
     @Override
     protected Fragment instantiate(Context context, String className, Bundle arguments) {
-        return new CollapsedStatusBarFragment(mock(OngoingCallController.class));
+        mOngoingCallController = mock(OngoingCallController.class);
+        return new CollapsedStatusBarFragment(mOngoingCallController);
     }
 }
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/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
new file mode 100644
index 0000000..d87d1d1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall
+
+import android.app.Notification
+import android.app.PendingIntent
+import android.app.Person
+import android.service.notification.NotificationListenerService.REASON_USER_STOPPED
+import androidx.test.filters.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+import android.widget.LinearLayout
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.never
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class OngoingCallControllerTest : SysuiTestCase() {
+
+    private lateinit var controller: OngoingCallController
+    private lateinit var notifCollectionListener: NotifCollectionListener
+
+    @Mock private lateinit var mockOngoingCallListener: OngoingCallListener
+
+    private lateinit var chipView: LinearLayout
+
+    @Before
+    fun setUp() {
+        allowTestableLooperAsMainThread()
+        TestableLooper.get(this).runWithLooper {
+            chipView = LayoutInflater.from(mContext)
+                    .inflate(R.layout.ongoing_call_chip, null) as LinearLayout
+        }
+
+        MockitoAnnotations.initMocks(this)
+        val featureFlags = mock(FeatureFlags::class.java)
+        `when`(featureFlags.isOngoingCallStatusBarChipEnabled).thenReturn(true)
+        val notificationCollection = mock(CommonNotifCollection::class.java)
+
+        controller = OngoingCallController(notificationCollection, featureFlags, FakeSystemClock())
+        controller.init()
+        controller.addCallback(mockOngoingCallListener)
+        controller.setChipView(chipView)
+
+        val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java)
+        verify(notificationCollection).addCollectionListener(collectionListenerCaptor.capture())
+        notifCollectionListener = collectionListenerCaptor.value!!
+    }
+
+    @Test
+    fun onEntryUpdated_isOngoingCallNotif_listenerNotifiedWithRightCallTime() {
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+        verify(mockOngoingCallListener).onOngoingCallStarted(anyBoolean())
+    }
+
+    @Test
+    fun onEntryUpdated_notOngoingCallNotif_listenerNotNotified() {
+        notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
+
+        verify(mockOngoingCallListener, never()).onOngoingCallStarted(anyBoolean())
+    }
+
+    @Test
+    fun onEntryRemoved_ongoingCallNotif_listenerNotified() {
+        notifCollectionListener.onEntryRemoved(createOngoingCallNotifEntry(), REASON_USER_STOPPED)
+
+        verify(mockOngoingCallListener).onOngoingCallEnded(anyBoolean())
+    }
+
+    @Test
+    fun onEntryRemoved_notOngoingCallNotif_listenerNotNotified() {
+        notifCollectionListener.onEntryRemoved(createNotCallNotifEntry(), REASON_USER_STOPPED)
+
+        verify(mockOngoingCallListener, never()).onOngoingCallEnded(anyBoolean())
+    }
+
+    @Test
+    fun hasOngoingCall_noOngoingCallNotifSent_returnsFalse() {
+        assertThat(controller.hasOngoingCall).isFalse()
+    }
+
+    @Test
+    fun hasOngoingCall_ongoingCallNotifSentAndChipViewSet_returnsTrue() {
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+        assertThat(controller.hasOngoingCall).isTrue()
+    }
+
+    @Test
+    fun hasOngoingCall_ongoingCallNotifSentButNoChipView_returnsFalse() {
+        controller.setChipView(null)
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+        assertThat(controller.hasOngoingCall).isFalse()
+    }
+
+    @Test
+    fun hasOngoingCall_ongoingCallNotifSentThenRemoved_returnsFalse() {
+        val ongoingCallNotifEntry = createOngoingCallNotifEntry()
+
+        notifCollectionListener.onEntryUpdated(ongoingCallNotifEntry)
+        notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
+
+        assertThat(controller.hasOngoingCall).isFalse()
+    }
+
+    private fun createOngoingCallNotifEntry(): NotificationEntry {
+        val notificationEntryBuilder = NotificationEntryBuilder()
+        notificationEntryBuilder.modifyNotification(context).style = ongoingCallStyle
+        return notificationEntryBuilder.build()
+    }
+
+    private fun createNotCallNotifEntry() = NotificationEntryBuilder().build()
+}
+
+private val ongoingCallStyle = Notification.CallStyle.forOngoingCall(
+        Person.Builder().setName("name").build(),
+        /* hangUpIntent= */ mock(PendingIntent::class.java))
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/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/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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ac042a7..23dda78 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -234,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;
@@ -3489,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);
@@ -12944,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/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/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/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 2bc81cb..9c10814 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;
@@ -57,10 +58,6 @@
 import android.os.UserManager;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.VmSocketAddress;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -82,128 +79,9 @@
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
-import java.io.FileDescriptor;
-import java.io.InterruptedIOException;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
-
-// The following class is Android Emulator specific. It is used to read and
-// write contents of the host system's clipboard.
-class HostClipboardMonitor implements Runnable {
-    public interface HostClipboardCallback {
-        void onHostClipboardUpdated(String contents);
-    }
-
-    private FileDescriptor mPipe = null;
-    private HostClipboardCallback mHostClipboardCallback;
-    private static final String PIPE_NAME = "pipe:clipboard";
-    private static final int HOST_PORT = 5000;
-
-    private static byte[] createOpenHandshake() {
-        // String.getBytes doesn't include the null terminator,
-        // but the QEMU pipe device requires the pipe service name
-        // to be null-terminated.
-
-        final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
-        bits[PIPE_NAME.length()] = 0;
-        return bits;
-    }
-
-    private boolean openPipe() {
-        try {
-            final FileDescriptor fd = Os.socket(OsConstants.AF_VSOCK, OsConstants.SOCK_STREAM, 0);
-
-            try {
-                Os.connect(fd, new VmSocketAddress(HOST_PORT, OsConstants.VMADDR_CID_HOST));
-
-                final byte[] handshake = createOpenHandshake();
-                Os.write(fd, handshake, 0, handshake.length);
-                mPipe = fd;
-                return true;
-            } catch (ErrnoException | SocketException | InterruptedIOException e) {
-                Os.close(fd);
-            }
-        } catch (ErrnoException e) {
-        }
-
-        return false;
-    }
-
-    private void closePipe() {
-        try {
-            final FileDescriptor fd = mPipe;
-            mPipe = null;
-            if (fd != null) {
-                Os.close(fd);
-            }
-        } catch (ErrnoException ignore) {
-        }
-    }
-
-    private byte[] receiveMessage() throws ErrnoException, InterruptedIOException {
-        final byte[] lengthBits = new byte[4];
-        Os.read(mPipe, lengthBits, 0, lengthBits.length);
-
-        final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
-        bb.order(ByteOrder.LITTLE_ENDIAN);
-        final int msgLen = bb.getInt();
-
-        final byte[] msg = new byte[msgLen];
-        Os.read(mPipe, msg, 0, msg.length);
-
-        return msg;
-    }
-
-    private void sendMessage(byte[] msg) throws ErrnoException, InterruptedIOException {
-        final byte[] lengthBits = new byte[4];
-        final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
-        bb.order(ByteOrder.LITTLE_ENDIAN);
-        bb.putInt(msg.length);
-
-        Os.write(mPipe, lengthBits, 0, lengthBits.length);
-        Os.write(mPipe, msg, 0, msg.length);
-    }
-
-    public HostClipboardMonitor(HostClipboardCallback cb) {
-        mHostClipboardCallback = cb;
-    }
-
-    @Override
-    public void run() {
-        while (!Thread.interrupted()) {
-            try {
-                // There's no guarantee that QEMU pipes will be ready at the moment
-                // this method is invoked. We simply try to get the pipe open and
-                // retry on failure indefinitely.
-                while ((mPipe == null) && !openPipe()) {
-                    Thread.sleep(100);
-                }
-
-                final byte[] receivedData = receiveMessage();
-                mHostClipboardCallback.onHostClipboardUpdated(
-                    new String(receivedData));
-            } catch (ErrnoException | InterruptedIOException e) {
-                closePipe();
-            } catch (InterruptedException e) {
-            }
-        }
-    }
-
-    public void setHostClipboard(String content) {
-        try {
-            if (mPipe != null) {
-                sendMessage(content.getBytes());
-            }
-        } catch (ErrnoException | InterruptedIOException e) {
-            Slog.e("HostClipboardMonitor",
-                   "Failed to set host clipboard " + e.getMessage());
-        }
-    }
-}
+import java.util.function.Consumer;
 
 /**
  * Implementation of the clipboard for copy and paste.
@@ -220,8 +98,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;
 
@@ -235,14 +111,15 @@
     private final ContentCaptureManagerInternal mContentCaptureInternal;
     private final AutofillManagerInternal mAutofillInternal;
     private final IBinder mPermissionOwner;
-    private final HostClipboardMonitor mHostClipboardMonitor;
+    private final Consumer<ClipData> mEmulatorClipboardMonitor;
     private final Handler mWorkerHandler;
 
     @GuardedBy("mLock")
     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;
@@ -267,24 +144,14 @@
         final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
         mPermissionOwner = permOwner;
         if (IS_EMULATOR) {
-            mHostClipboardMonitor = new HostClipboardMonitor(
-                new HostClipboardMonitor.HostClipboardCallback() {
-                    @Override
-                    public void onHostClipboardUpdated(String contents){
-                        ClipData clip =
-                            new ClipData("host clipboard",
-                                         new String[]{"text/plain"},
-                                         new ClipData.Item(contents));
-                        synchronized (mLock) {
-                            setPrimaryClipInternalLocked(getClipboardLocked(0), clip,
-                                    android.os.Process.SYSTEM_UID, null);
-                        }
-                    }
-                });
-            Thread hostMonitorThread = new Thread(mHostClipboardMonitor);
-            hostMonitorThread.start();
+            mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
+                synchronized (mLock) {
+                    setPrimaryClipInternalLocked(getClipboardLocked(0), clip,
+                            android.os.Process.SYSTEM_UID, null);
+                }
+            });
         } else {
-            mHostClipboardMonitor = null;
+            mEmulatorClipboardMonitor = (clip) -> {};
         }
 
         updateConfig();
@@ -310,8 +177,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);
         }
@@ -643,18 +512,7 @@
     @GuardedBy("mLock")
     private void setPrimaryClipInternalLocked(
             @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
-        // Push clipboard to host, if any
-        if (mHostClipboardMonitor != null) {
-            if (clip == null) {
-                // Someone really wants the clipboard cleared, so push empty
-                mHostClipboardMonitor.setHostClipboard("");
-            } else if (clip.getItemCount() > 0) {
-                final CharSequence text = clip.getItemAt(0).getText();
-                if (text != null) {
-                    mHostClipboardMonitor.setHostClipboard(text.toString());
-                }
-            }
-        }
+        mEmulatorClipboardMonitor.accept(clip);
 
         final int userId = UserHandle.getUserId(uid);
         if (clip != null) {
@@ -1056,11 +914,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/clipboard/EmulatorClipboardMonitor.java b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
new file mode 100644
index 0000000..62b701a
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.clipboard;
+
+import android.annotation.Nullable;
+import android.content.ClipData;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.VmSocketAddress;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+// The following class is Android Emulator specific. It is used to read and
+// write contents of the host system's clipboard.
+class EmulatorClipboardMonitor implements Consumer<ClipData> {
+    private static final String TAG = "EmulatorClipboardMonitor";
+    private static final String PIPE_NAME = "pipe:clipboard";
+    private static final int HOST_PORT = 5000;
+    private final Thread mHostMonitorThread;
+    private FileDescriptor mPipe = null;
+
+    private static byte[] createOpenHandshake() {
+        // String.getBytes doesn't include the null terminator,
+        // but the QEMU pipe device requires the pipe service name
+        // to be null-terminated.
+
+        final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
+        bits[PIPE_NAME.length()] = 0;
+        return bits;
+    }
+
+    private boolean isPipeOpened() {
+        return mPipe != null;
+    }
+
+    private synchronized boolean openPipe() {
+        if (mPipe != null) {
+            return true;
+        }
+
+        try {
+            final FileDescriptor fd = Os.socket(OsConstants.AF_VSOCK, OsConstants.SOCK_STREAM, 0);
+
+            try {
+                Os.connect(fd, new VmSocketAddress(HOST_PORT, OsConstants.VMADDR_CID_HOST));
+
+                final byte[] handshake = createOpenHandshake();
+                Os.write(fd, handshake, 0, handshake.length);
+                mPipe = fd;
+                return true;
+            } catch (ErrnoException | SocketException | InterruptedIOException e) {
+                Os.close(fd);
+            }
+        } catch (ErrnoException e) {
+        }
+
+        return false;
+    }
+
+    private synchronized void closePipe() {
+        try {
+            final FileDescriptor fd = mPipe;
+            mPipe = null;
+            if (fd != null) {
+                Os.close(fd);
+            }
+        } catch (ErrnoException ignore) {
+        }
+    }
+
+    private byte[] receiveMessage() throws ErrnoException, InterruptedIOException {
+        final byte[] lengthBits = new byte[4];
+        Os.read(mPipe, lengthBits, 0, lengthBits.length);
+
+        final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
+        bb.order(ByteOrder.LITTLE_ENDIAN);
+        final int msgLen = bb.getInt();
+
+        final byte[] msg = new byte[msgLen];
+        Os.read(mPipe, msg, 0, msg.length);
+
+        return msg;
+    }
+
+    private void sendMessage(final byte[] msg) throws ErrnoException, InterruptedIOException {
+        final byte[] lengthBits = new byte[4];
+        final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
+        bb.order(ByteOrder.LITTLE_ENDIAN);
+        bb.putInt(msg.length);
+
+        Os.write(mPipe, lengthBits, 0, lengthBits.length);
+        Os.write(mPipe, msg, 0, msg.length);
+    }
+
+    EmulatorClipboardMonitor(final Consumer<ClipData> setAndroidClipboard) {
+        this.mHostMonitorThread = new Thread(() -> {
+            while (!Thread.interrupted()) {
+                try {
+                    // There's no guarantee that QEMU pipes will be ready at the moment
+                    // this method is invoked. We simply try to get the pipe open and
+                    // retry on failure indefinitely.
+                    while (!openPipe()) {
+                        Thread.sleep(100);
+                    }
+
+                    final byte[] receivedData = receiveMessage();
+
+                    final String str = new String(receivedData);
+                    final ClipData clip = new ClipData("host clipboard",
+                                                       new String[]{"text/plain"},
+                                                       new ClipData.Item(str));
+
+                    setAndroidClipboard.accept(clip);
+                } catch (ErrnoException | InterruptedIOException e) {
+                    closePipe();
+                } catch (InterruptedException | IllegalArgumentException e) {
+                }
+            }
+        });
+
+        this.mHostMonitorThread.start();
+    }
+
+    @Override
+    public void accept(final @Nullable ClipData clip) {
+        if (clip == null) {
+            setHostClipboardImpl("");
+        } else if (clip.getItemCount() > 0) {
+            final CharSequence text = clip.getItemAt(0).getText();
+            if (text != null) {
+                setHostClipboardImpl(text.toString());
+            }
+        }
+    }
+
+    private void setHostClipboardImpl(final String value) {
+        try {
+            if (isPipeOpened()) {
+                sendMessage(value.getBytes());
+            }
+        } catch (ErrnoException | InterruptedIOException e) {
+            Slog.e(TAG, "Failed to set host clipboard " + e.getMessage());
+        } catch (IllegalArgumentException e) {
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/clipboard/OWNERS b/services/core/java/com/android/server/clipboard/OWNERS
new file mode 100644
index 0000000..5449df9
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/OWNERS
@@ -0,0 +1 @@
+per-file EmulatorClipboardMonitor.java = bohu@google.com,lfy@google.com,rkir@google.com
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/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 bcd9007..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
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/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 a6d4ed9..e532790 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -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 30ce57f..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),
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/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index fe728ab..fb664ab 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -60,7 +60,7 @@
     const std::string instance = std::string() + IStats::descriptor + "/default";
     const binder_exception_t err =
             AServiceManager_addService(statsService->asBinder().get(), instance.c_str());
-    LOG_ALWAYS_FATAL_IF(err != EX_NONE, "Cannot register %s: %d", instance.c_str(), err);
+    LOG_ALWAYS_FATAL_IF(err != EX_NONE, "Cannot register AIDL %s: %d", instance.c_str(), err);
 }
 
 static void startStatsHidlService() {
@@ -69,13 +69,18 @@
 
     android::sp<IStats> statsHal = new StatsHal();
     const android::status_t err = statsHal->registerAsService();
-    LOG_ALWAYS_FATAL_IF(err != android::OK, "Cannot register %s: %d", IStats::descriptor, err);
+    LOG_ALWAYS_FATAL_IF(err != android::OK, "Cannot register HIDL %s: %d", IStats::descriptor, err);
 }
 
 } // namespace
 
 namespace android {
 
+static void android_server_SystemServer_startIStatsService(JNIEnv* /* env */, jobject /* clazz */) {
+    startStatsHidlService();
+    startStatsAidlService();
+}
+
 static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) {
     char propBuf[PROPERTY_VALUE_MAX];
     property_get("system_init.startsensorservice", propBuf, "1");
@@ -129,9 +134,6 @@
     } else {
         ALOGW("%s is deprecated. Skipping registration.", ISchedulingPolicyService::descriptor);
     }
-
-    startStatsAidlService();
-    startStatsHidlService();
 }
 
 static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */,
@@ -160,6 +162,7 @@
  */
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
+        {"startIStatsService", "()V", (void*)android_server_SystemServer_startIStatsService},
         {"startSensorService", "()V", (void*)android_server_SystemServer_startSensorService},
         {"startMemtrackProxyService", "()V",
          (void*)android_server_SystemServer_startMemtrackProxyService},
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 9b2a1e7..1426579 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -442,15 +442,15 @@
 
     private final SystemServerDumper mDumper = new SystemServerDumper();
 
-
     /**
      * The pending WTF to be logged into dropbox.
      */
     private static LinkedList<Pair<String, ApplicationErrorReport.CrashInfo>> sPendingWtfs;
 
-    /**
-     * Start the sensor service. This is a blocking call and can take time.
-     */
+    /** Start the IStats services. This is a blocking call and can take time. */
+    private static native void startIStatsService();
+
+    /** Start the sensor service. This is a blocking call and can take time. */
     private static native void startSensorService();
 
     /**
@@ -1029,6 +1029,10 @@
         mSystemServiceManager.startService(PowerStatsService.class);
         t.traceEnd();
 
+        t.traceBegin("StartIStatsService");
+        startIStatsService();
+        t.traceEnd();
+
         // Start MemtrackProxyService before ActivityManager, so that early calls
         // to Memtrack::getMemory() don't fail.
         t.traceBegin("MemtrackProxyService");
@@ -2678,7 +2682,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/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/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/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 557c14a..9937ec1 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -98,7 +97,7 @@
         when(nextClient.getTargetUserId()).thenReturn(nextUserId);
 
         mScheduler.scheduleClientMonitor(nextClient);
-        verify(nextClient, never()).start(any());
+
         assertEquals(0, mUserStoppedCallback.numInvocations);
         assertEquals(1, mUserStartedCallback.numInvocations);
 
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/tools/hiddenapi/checksorted_sha.sh b/tools/hiddenapi/checksorted_sha.sh
index ceb705f..451fed6 100755
--- a/tools/hiddenapi/checksorted_sha.sh
+++ b/tools/hiddenapi/checksorted_sha.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 set -e
 LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
-git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
+git show --name-only --pretty=format: $1 | grep "boot/hiddenapi/hiddenapi-.*txt" | while read file; do
     diff <(git show $1:$file) <(git show $1:$file | $LOCAL_DIR/sort_api.sh )  || {
       echo -e "\e[1m\e[31m$file $1 is not sorted or contains duplicates. To sort it correctly:\e[0m"
       echo -e "\e[33m${LOCAL_DIR}/sort_api.sh $2/frameworks/base/$file\e[0m"
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
index 2924e01..822aba4 100755
--- a/tools/hiddenapi/exclude.sh
+++ b/tools/hiddenapi/exclude.sh
@@ -48,7 +48,7 @@
 PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
 RE=$(echo ${PACKAGES} | sed "s/ /|/g")
 EXIT_CODE=0
-for file in $(git show --name-only --pretty=format: $SHA | grep "config/hiddenapi-.*txt"); do
+for file in $(git show --name-only --pretty=format: $SHA | grep "boot/hiddenapi/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 $SHA contains the following entries\e[0m"