Merge "Add Settings key for UWB State"
diff --git a/Android.bp b/Android.bp
index b8c060d..9127966f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -124,7 +124,6 @@
         ":libbluetooth-binder-aidl",
         ":libcamera_client_aidl",
         ":libcamera_client_framework_aidl",
-        ":packagemanager_aidl",
         ":libupdate_engine_aidl",
         ":resourcemanager_aidl",
         ":storaged_aidl",
@@ -228,6 +227,7 @@
     name: "framework-internal-utils",
     static_libs: [
         "apex_aidl_interface-java",
+        "packagemanager_aidl-java",
         "framework-protos",
         "updatable-driver-protos",
         "ota_metadata_proto_java",
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index e58b479..6e2742c 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -88,6 +88,10 @@
 public class UserLifecycleTests {
     private static final String TAG = UserLifecycleTests.class.getSimpleName();
 
+    /** Max runtime for each test (including all runs within that test). */
+    // No point exceeding 10 minutes, since device would likely be considered non-responsive anyway.
+    private static final long TIMEOUT_MAX_TEST_TIME_MS = 9 * 60_000;
+
     private static final int TIMEOUT_IN_SECOND = 30;
     private static final int CHECK_USER_REMOVED_INTERVAL_MS = 200;
 
@@ -144,7 +148,7 @@
     }
 
     /** Tests creating a new user. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void createUser() {
         while (mRunner.keepRunning()) {
             Log.i(TAG, "Starting timer");
@@ -158,7 +162,7 @@
     }
 
     /** Tests creating and starting a new user. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void createAndStartUser() throws RemoteException {
         while (mRunner.keepRunning()) {
             Log.i(TAG, "Starting timer");
@@ -182,7 +186,7 @@
      * Tests starting an uninitialized user.
      * Measures the time until ACTION_USER_STARTED is received.
      */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void startUser() throws RemoteException {
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
@@ -206,7 +210,7 @@
      * Tests starting & unlocking an uninitialized user.
      * Measures the time until unlock listener is triggered and user is unlocked.
      */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void startAndUnlockUser() {
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
@@ -225,7 +229,7 @@
     }
 
     /** Tests switching to an uninitialized user. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void switchUser() throws RemoteException {
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
@@ -245,7 +249,7 @@
     }
 
     /** Tests switching to a previously-started, but no-longer-running, user. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void switchUser_stopped() throws RemoteException {
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
@@ -269,7 +273,7 @@
     }
 
     /** Tests switching to an already-created already-running non-owner background user. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void switchUser_running() throws RemoteException {
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
@@ -289,7 +293,7 @@
     }
 
     /** Tests stopping a background user. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void stopUser() throws RemoteException {
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
@@ -314,7 +318,7 @@
     }
 
     /** Tests reaching LOOKED_BOOT_COMPLETE when switching to uninitialized user. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void lockedBootCompleted() throws RemoteException {
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
@@ -337,7 +341,7 @@
     }
 
     /** Tests stopping an ephemeral foreground user. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void ephemeralUserStopped() throws RemoteException {
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
@@ -378,7 +382,7 @@
     }
 
     /** Tests creating a new profile. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileCreate() {
         assumeTrue(mHasManagedUserFeature);
 
@@ -395,7 +399,7 @@
     }
 
     /** Tests starting (unlocking) an uninitialized profile. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileUnlock() {
         assumeTrue(mHasManagedUserFeature);
 
@@ -415,7 +419,7 @@
     }
 
     /** Tests starting (unlocking) a previously-started, but no-longer-running, profile. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileUnlock_stopped() throws RemoteException {
         assumeTrue(mHasManagedUserFeature);
 
@@ -440,7 +444,7 @@
     /**
      * Tests starting (unlocking) & launching an already-installed app in an uninitialized profile.
      */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileUnlockAndLaunchApp() throws RemoteException {
         assumeTrue(mHasManagedUserFeature);
 
@@ -468,7 +472,7 @@
      * A sort of combination of {@link #managedProfileUnlockAndLaunchApp} and
      * {@link #managedProfileUnlock_stopped}}.
      */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileUnlockAndLaunchApp_stopped() throws RemoteException {
         assumeTrue(mHasManagedUserFeature);
 
@@ -495,7 +499,7 @@
     }
 
     /** Tests installing a pre-existing app in an uninitialized profile. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileInstall() throws RemoteException {
         assumeTrue(mHasManagedUserFeature);
 
@@ -518,7 +522,7 @@
      * Tests creating a new profile, starting (unlocking) it, installing an app,
      * and launching that app in it.
      */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileCreateUnlockInstallAndLaunchApp() throws RemoteException {
         assumeTrue(mHasManagedUserFeature);
 
@@ -541,7 +545,7 @@
     }
 
     /** Tests stopping a profile. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileStopped() throws RemoteException {
         assumeTrue(mHasManagedUserFeature);
 
@@ -566,7 +570,7 @@
 
     // TODO: This is just a POC. Do this properly and add more.
     /** Tests starting (unlocking) a newly-created profile using the user-type-pkg-whitelist. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileUnlock_usingWhitelist() {
         assumeTrue(mHasManagedUserFeature);
         final int origMode = getUserTypePackageWhitelistMode();
@@ -592,7 +596,7 @@
         }
     }
     /** Tests starting (unlocking) a newly-created profile NOT using the user-type-pkg-whitelist. */
-    @Test
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileUnlock_notUsingWhitelist() {
         assumeTrue(mHasManagedUserFeature);
         final int origMode = getUserTypePackageWhitelistMode();
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
index d493a1c..272e12d 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
@@ -96,6 +96,17 @@
         return Collections.unmodifiableMap(mAll);
     }
 
+    /**
+     * Asserts that this {@link AppSearchBatchResult} has no failures.
+     *
+     * @hide
+     */
+    public void checkSuccess() {
+        if (!isSuccess()) {
+            throw new IllegalStateException("AppSearchBatchResult has failures: " + this);
+        }
+    }
+
     @Override
     @NonNull
     public String toString() {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
index 10e014b..d6d5315 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
@@ -71,7 +71,7 @@
 
     /**
      * Checks the setSchema() call won't delete any types or has incompatible types after all {@link
-     * Migrator} has been triggered..
+     * Migrator} has been triggered.
      */
     public static void checkDeletedAndIncompatibleAfterMigration(
             @NonNull SetSchemaResponse setSchemaResponse, @NonNull Set<String> activeMigrators)
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 c33d5ec..d4e3239 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -363,6 +363,7 @@
                         schemasVisibleToPackages.put(entry.getKey(), packageIdentifiers);
                     }
                     instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
+                    // TODO(b/173532925): Implement logging for statsBuilder
                     SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema(
                             packageName,
                             databaseName,
@@ -371,7 +372,8 @@
                             schemasNotDisplayedBySystem,
                             schemasVisibleToPackages,
                             forceOverride,
-                            schemaVersion);
+                            schemaVersion,
+                            /*setSchemaStatsBuilder=*/ null);
                     ++operationSuccessCount;
                     invokeCallbackOnResult(callback,
                             AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle()));
@@ -816,8 +818,10 @@
 
                     AppSearchUserInstance instance =
                             mAppSearchUserInstanceManager.getUserInstance(targetUser);
+                    // TODO(b/173532925): Implement logging for statsBuilder
                     SearchResultPage searchResultPage =
-                            instance.getAppSearchImpl().getNextPage(packageName, nextPageToken);
+                            instance.getAppSearchImpl().getNextPage(
+                                    packageName, nextPageToken, /*statsBuilder=*/ null);
                     invokeCallbackOnResult(
                             callback,
                             AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
@@ -898,8 +902,11 @@
                                         outputStream, searchResultPage.getResults().get(i)
                                                 .getGenericDocument().getBundle());
                             }
+                            // TODO(b/173532925): Implement logging for statsBuilder
                             searchResultPage = instance.getAppSearchImpl().getNextPage(
-                                    packageName, searchResultPage.getNextPageToken());
+                                    packageName,
+                                    searchResultPage.getNextPageToken(),
+                                    /*statsBuilder=*/ null);
                         }
                     }
                     invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 15916cc..324163f 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -59,6 +59,7 @@
 import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
 import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
 import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
 import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
 
 import com.google.android.icing.IcingSearchEngine;
@@ -393,6 +394,7 @@
      * @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
      *     which do not comply with the new schema will be deleted.
      * @param version The overall version number of the request.
+     * @param setSchemaStatsBuilder Builder for {@link SetSchemaStats} to hold stats for setSchema
      * @return The response contains deleted schema types and incompatible schema types of this
      *     call.
      * @throws AppSearchException On IcingSearchEngine error. If the status code is
@@ -408,7 +410,8 @@
             @NonNull List<String> schemasNotDisplayedBySystem,
             @NonNull Map<String, List<PackageIdentifier>> schemasVisibleToPackages,
             boolean forceOverride,
-            int version)
+            int version,
+            @Nullable SetSchemaStats.Builder setSchemaStatsBuilder)
             throws AppSearchException {
         mReadWriteLock.writeLock().lock();
         try {
@@ -438,6 +441,12 @@
             mLogUtil.piiTrace(
                     "setSchema, response", setSchemaResultProto.getStatus(), setSchemaResultProto);
 
+            if (setSchemaStatsBuilder != null) {
+                setSchemaStatsBuilder.setStatusCode(
+                        statusProtoToResultCode(setSchemaResultProto.getStatus()));
+                AppSearchLoggerHelper.copyNativeStats(setSchemaResultProto, setSchemaStatsBuilder);
+            }
+
             // Determine whether it succeeded.
             try {
                 checkSuccess(setSchemaResultProto.getStatus());
@@ -1127,8 +1136,13 @@
      * @throws AppSearchException on IcingSearchEngine error or if can't advance on nextPageToken.
      */
     @NonNull
-    public SearchResultPage getNextPage(@NonNull String packageName, long nextPageToken)
+    public SearchResultPage getNextPage(
+            @NonNull String packageName,
+            long nextPageToken,
+            @Nullable SearchStats.Builder statsBuilder)
             throws AppSearchException {
+        long totalLatencyStartMillis = SystemClock.elapsedRealtime();
+
         mReadWriteLock.readLock().lock();
         try {
             throwIfClosedLocked();
@@ -1137,6 +1151,13 @@
             checkNextPageToken(packageName, nextPageToken);
             SearchResultProto searchResultProto =
                     mIcingSearchEngineLocked.getNextPage(nextPageToken);
+
+            if (statsBuilder != null) {
+                statsBuilder.setStatusCode(statusProtoToResultCode(searchResultProto.getStatus()));
+                AppSearchLoggerHelper.copyNativeStats(
+                        searchResultProto.getQueryStats(), statsBuilder);
+            }
+
             mLogUtil.piiTrace(
                     "getNextPage, response",
                     searchResultProto.getResultsCount(),
@@ -1152,9 +1173,22 @@
                     mNextPageTokensLocked.get(packageName).remove(nextPageToken);
                 }
             }
-            return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
+            long rewriteSearchResultLatencyStartMillis = SystemClock.elapsedRealtime();
+            SearchResultPage resultPage =
+                    rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
+            if (statsBuilder != null) {
+                statsBuilder.setRewriteSearchResultLatencyMillis(
+                        (int)
+                                (SystemClock.elapsedRealtime()
+                                        - rewriteSearchResultLatencyStartMillis));
+            }
+            return resultPage;
         } finally {
             mReadWriteLock.readLock().unlock();
+            if (statsBuilder != null) {
+                statsBuilder.setTotalLatencyMillis(
+                        (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
+            }
         }
     }
 
@@ -1334,7 +1368,7 @@
                         statusProtoToResultCode(deleteResultProto.getStatus()));
                 // TODO(b/187206766) also log query stats here once IcingLib returns it
                 AppSearchLoggerHelper.copyNativeStats(
-                        deleteResultProto.getDeleteStats(), removeStatsBuilder);
+                        deleteResultProto.getDeleteByQueryStats(), removeStatsBuilder);
             }
 
             // It seems that the caller wants to get success if the data matching the query is
@@ -1343,7 +1377,8 @@
                     deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
 
             // Update derived maps
-            int numDocumentsDeleted = deleteResultProto.getDeleteStats().getNumDocumentsDeleted();
+            int numDocumentsDeleted =
+                    deleteResultProto.getDeleteByQueryStats().getNumDocumentsDeleted();
             updateDocumentCountAfterRemovalLocked(packageName, numDocumentsDeleted);
         } finally {
             mReadWriteLock.writeLock().unlock();
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
index 98cedc7..1f7d44e 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
@@ -24,6 +24,7 @@
 import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
 import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
 import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
 
 /**
  * An interface for implementing client-defined logging AppSearch operations stats.
@@ -54,5 +55,8 @@
     /** Logs {@link OptimizeStats} */
     void logStats(@NonNull OptimizeStats stats);
 
+    /** Logs {@link SetSchemaStats} */
+    void logStats(@NonNull SetSchemaStats stats);
+
     // TODO(b/173532925) Add remaining logStats once we add all the stats.
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
index cd653e5..c19ba14 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
@@ -23,12 +23,15 @@
 import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
 import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
 import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
 
+import com.google.android.icing.proto.DeleteByQueryStatsProto;
 import com.google.android.icing.proto.DeleteStatsProto;
 import com.google.android.icing.proto.InitializeStatsProto;
 import com.google.android.icing.proto.OptimizeStatsProto;
 import com.google.android.icing.proto.PutDocumentStatsProto;
 import com.google.android.icing.proto.QueryStatsProto;
+import com.google.android.icing.proto.SetSchemaResultProto;
 
 import java.util.Objects;
 
@@ -142,6 +145,26 @@
     }
 
     /**
+     * Copies native DeleteByQuery stats to builder.
+     *
+     * @param fromNativeStats Stats copied from.
+     * @param toStatsBuilder Stats copied to.
+     */
+    static void copyNativeStats(
+            @NonNull DeleteByQueryStatsProto fromNativeStats,
+            @NonNull RemoveStats.Builder toStatsBuilder) {
+        Objects.requireNonNull(fromNativeStats);
+        Objects.requireNonNull(toStatsBuilder);
+
+        @SuppressWarnings("deprecation")
+        int deleteType = DeleteStatsProto.DeleteType.Code.DEPRECATED_QUERY.getNumber();
+        toStatsBuilder
+                .setNativeLatencyMillis(fromNativeStats.getLatencyMs())
+                .setDeleteType(deleteType)
+                .setDeletedDocumentCount(fromNativeStats.getNumDocumentsDeleted());
+    }
+
+    /**
      * Copies native {@link OptimizeStatsProto} to builder.
      *
      * @param fromNativeStats Stats copied from.
@@ -164,4 +187,25 @@
                 .setStorageSizeAfterBytes(fromNativeStats.getStorageSizeAfter())
                 .setTimeSinceLastOptimizeMillis(fromNativeStats.getTimeSinceLastOptimizeMs());
     }
+
+    /*
+     * Copy SetSchema result stats to builder.
+     *
+     * @param fromProto Stats copied from.
+     * @param toStatsBuilder Stats copied to.
+     */
+    static void copyNativeStats(
+            @NonNull SetSchemaResultProto fromProto,
+            @NonNull SetSchemaStats.Builder toStatsBuilder) {
+        Objects.requireNonNull(fromProto);
+        Objects.requireNonNull(toStatsBuilder);
+        toStatsBuilder
+                .setNewTypeCount(fromProto.getNewSchemaTypesCount())
+                .setDeletedTypeCount(fromProto.getDeletedSchemaTypesCount())
+                .setCompatibleTypeChangeCount(fromProto.getFullyCompatibleChangedSchemaTypesCount())
+                .setIndexIncompatibleTypeChangeCount(
+                        fromProto.getIndexIncompatibleChangedSchemaTypesCount())
+                .setBackwardsIncompatibleTypeChangeCount(
+                        fromProto.getIncompatibleSchemaTypesCount());
+    }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java
index d7904f3..75ae2d0a 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java
@@ -40,6 +40,7 @@
                 VISIBILITY_SCOPE_LOCAL,
                 // Searches the global documents. Including platform surfaceable and 3p-access.
                 VISIBILITY_SCOPE_GLOBAL,
+                VISIBILITY_SCOPE_UNKNOWN,
                 // TODO(b/173532925) Add THIRD_PARTY_ACCESS once we can distinguish platform
                 //  surfaceable from 3p access(right both of them are categorized as
                 //  VISIBILITY_SCOPE_GLOBAL)
@@ -51,6 +52,7 @@
     public static final int VISIBILITY_SCOPE_LOCAL = 1;
     // Searches the global documents. Including platform surfaceable and 3p-access.
     public static final int VISIBILITY_SCOPE_GLOBAL = 2;
+    public static final int VISIBILITY_SCOPE_UNKNOWN = 3;
 
     // TODO(b/173532925): Add a field searchType to indicate where the search is used(normal
     //  query vs in removeByQuery vs during migration)
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
index 9d789a8..3e5a80f 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
@@ -47,9 +47,6 @@
 
     private final int mTotalLatencyMillis;
 
-    /** Overall time used for the native function call. */
-    private final int mNativeLatencyMillis;
-
     /** Number of newly added schema types. */
     private final int mNewTypeCount;
 
@@ -72,7 +69,6 @@
         mStatusCode = builder.mStatusCode;
         mSchemaMigrationStats = builder.mSchemaMigrationStats;
         mTotalLatencyMillis = builder.mTotalLatencyMillis;
-        mNativeLatencyMillis = builder.mNativeLatencyMillis;
         mNewTypeCount = builder.mNewTypeCount;
         mDeletedTypeCount = builder.mDeletedTypeCount;
         mCompatibleTypeChangeCount = builder.mCompatibleTypeChangeCount;
@@ -112,11 +108,6 @@
         return mTotalLatencyMillis;
     }
 
-    /** Returns overall time used for the native function call. */
-    public int getNativeLatencyMillis() {
-        return mNativeLatencyMillis;
-    }
-
     /** Returns number of newly added schema types. */
     public int getNewTypeCount() {
         return mNewTypeCount;
@@ -159,7 +150,6 @@
         @AppSearchResult.ResultCode int mStatusCode;
         @Nullable SchemaMigrationStats mSchemaMigrationStats;
         int mTotalLatencyMillis;
-        int mNativeLatencyMillis;
         int mNewTypeCount;
         int mDeletedTypeCount;
         int mCompatibleTypeChangeCount;
@@ -193,13 +183,6 @@
             return this;
         }
 
-        /** Sets native latency in milliseconds. */
-        @NonNull
-        public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
-            mNativeLatencyMillis = nativeLatencyMillis;
-            return this;
-        }
-
         /** Sets number of new types. */
         @NonNull
         public Builder setNewTypeCount(int newTypeCount) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
index fdf6a00..4c29ece 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -36,6 +36,7 @@
 import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
 import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
 import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
 import com.android.server.appsearch.util.PackageUtil;
 
 import java.io.UnsupportedEncodingException;
@@ -180,6 +181,11 @@
         }
     }
 
+    @Override
+    public void logStats(@NonNull SetSchemaStats stats) {
+        // TODO(b/173532925): Log stats
+    }
+
     /**
      * Removes cached UID for package.
      *
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
index ce142d6..c4d1016 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
@@ -126,7 +126,8 @@
                     /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                     /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                     /*forceOverride=*/ false,
-                    /*version=*/ SCHEMA_VERSION);
+                    /*version=*/ SCHEMA_VERSION,
+                    /*setSchemaStatsBuilder=*/ null);
         }
 
         // Populate visibility settings set
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index a81d7d80..4db8355 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ie04f1ecc033faae8085afcb51eb9e40a298998d5
+bd53b062816070b64feb992c2bf58f3afa3d420e
diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp
index 5407cb4..f78d98a 100644
--- a/apex/appsearch/testing/Android.bp
+++ b/apex/appsearch/testing/Android.bp
@@ -28,6 +28,7 @@
         "framework",
         "framework-appsearch",
         "guava",
+        "service-appsearch",
         "truth-prebuilt",
     ],
     visibility: [
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
index ec9a42ea..4d8e8e9 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
@@ -19,6 +19,8 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.appsearch.AppSearchBatchResult;
 import android.app.appsearch.AppSearchSessionShim;
 import android.app.appsearch.GenericDocument;
@@ -26,12 +28,66 @@
 import android.app.appsearch.SearchResult;
 import android.app.appsearch.SearchResultsShim;
 
+import com.android.server.appsearch.external.localstorage.AppSearchLogger;
+import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
+import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
+import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Future;
 
 public class AppSearchTestUtils {
+    // Non-thread-safe logger implementation for testing
+    public static class TestLogger implements AppSearchLogger {
+        @Nullable public CallStats mCallStats;
+        @Nullable public PutDocumentStats mPutDocumentStats;
+        @Nullable public InitializeStats mInitializeStats;
+        @Nullable public SearchStats mSearchStats;
+        @Nullable public RemoveStats mRemoveStats;
+        @Nullable public OptimizeStats mOptimizeStats;
+        @Nullable public SetSchemaStats mSetSchemaStats;
+
+        @Override
+        public void logStats(@NonNull CallStats stats) {
+            mCallStats = stats;
+        }
+
+        @Override
+        public void logStats(@NonNull PutDocumentStats stats) {
+            mPutDocumentStats = stats;
+        }
+
+        @Override
+        public void logStats(@NonNull InitializeStats stats) {
+            mInitializeStats = stats;
+        }
+
+        @Override
+        public void logStats(@NonNull SearchStats stats) {
+            mSearchStats = stats;
+        }
+
+        @Override
+        public void logStats(@NonNull RemoveStats stats) {
+            mRemoveStats = stats;
+        }
+
+        @Override
+        public void logStats(@NonNull OptimizeStats stats) {
+            mOptimizeStats = stats;
+        }
+
+        @Override
+        public void logStats(@NonNull SetSchemaStats stats) {
+            mSetSchemaStats = stats;
+        }
+    }
 
     public static <K, V> AppSearchBatchResult<K, V> checkIsBatchResultSuccess(
             Future<AppSearchBatchResult<K, V>> future) throws Exception {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index 2ecc0ed..5d29515 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -28,6 +28,7 @@
 import static com.android.server.tare.EconomicPolicy.getEventType;
 import static com.android.server.tare.TareUtils.appToString;
 import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
+import static com.android.server.tare.TareUtils.narcToString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -460,8 +461,9 @@
                 Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta));
     }
 
+    @VisibleForTesting
     @GuardedBy("mLock")
-    private void recordTransactionLocked(final int userId, @NonNull final String pkgName,
+    void recordTransactionLocked(final int userId, @NonNull final String pkgName,
             @NonNull Ledger ledger, @NonNull Ledger.Transaction transaction,
             final boolean notifyOnAffordabilityChange) {
         if (transaction.delta == 0) {
@@ -476,12 +478,14 @@
         final long maxCirculationAllowed = mIrs.getMaxCirculationLocked();
         final long newArcsInCirculation = mCurrentNarcsInCirculation + transaction.delta;
         if (transaction.delta > 0 && newArcsInCirculation > maxCirculationAllowed) {
-            final long newDelta = maxCirculationAllowed - mCurrentNarcsInCirculation;
+            // Set lower bound at 0 so we don't accidentally take away credits when we were trying
+            // to _give_ the app credits.
+            final long newDelta = Math.max(0, maxCirculationAllowed - mCurrentNarcsInCirculation);
             Slog.i(TAG, "Would result in too many credits in circulation. Decreasing transaction "
                     + eventToString(transaction.eventId)
                     + (transaction.tag == null ? "" : ":" + transaction.tag)
                     + " for " + appToString(userId, pkgName)
-                    + " by " + (transaction.delta - newDelta));
+                    + " by " + narcToString(transaction.delta - newDelta));
             transaction = new Ledger.Transaction(
                     transaction.startTimeMs, transaction.endTimeMs,
                     transaction.eventId, transaction.tag, newDelta);
@@ -490,12 +494,15 @@
         if (transaction.delta > 0
                 && originalBalance + transaction.delta
                 > mCompleteEconomicPolicy.getMaxSatiatedBalance()) {
-            final long newDelta = mCompleteEconomicPolicy.getMaxSatiatedBalance() - originalBalance;
+            // Set lower bound at 0 so we don't accidentally take away credits when we were trying
+            // to _give_ the app credits.
+            final long newDelta =
+                    Math.max(0, mCompleteEconomicPolicy.getMaxSatiatedBalance() - originalBalance);
             Slog.i(TAG, "Would result in becoming too rich. Decreasing transaction "
                     + eventToString(transaction.eventId)
                     + (transaction.tag == null ? "" : ":" + transaction.tag)
                     + " for " + appToString(userId, pkgName)
-                    + " by " + (transaction.delta - newDelta));
+                    + " by " + narcToString(transaction.delta - newDelta));
             transaction = new Ledger.Transaction(
                     transaction.startTimeMs, transaction.endTimeMs,
                     transaction.eventId, transaction.tag, newDelta);
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 2fa10f0..096211b 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -264,6 +264,11 @@
     // get the cached value
     private static final long NETWORK_SCORER_CACHE_DURATION_MILLIS = 5000L;
 
+    // Cache the device provisioning package queried from resource config_deviceProvisioningPackage.
+    // Note that there is no synchronization on this method which is okay since in the worst case
+    // scenario, they might be a few extra reads from resources.
+    private String mCachedDeviceProvisioningPackage = null;
+
     // Messages for the handler
     static final int MSG_INFORM_LISTENERS = 3;
     static final int MSG_FORCE_IDLE_STATE = 4;
@@ -1641,9 +1646,11 @@
      * returns {@code false}.
      */
     private boolean isDeviceProvisioningPackage(String packageName) {
-        String deviceProvisioningPackage = mContext.getResources().getString(
-                com.android.internal.R.string.config_deviceProvisioningPackage);
-        return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
+        if (mCachedDeviceProvisioningPackage == null) {
+            mCachedDeviceProvisioningPackage = mContext.getResources().getString(
+                    com.android.internal.R.string.config_deviceProvisioningPackage);
+        }
+        return mCachedDeviceProvisioningPackage.equals(packageName);
     }
 
     private boolean isCarrierApp(String packageName) {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 61da4a7..2e22b92 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1101,17 +1101,18 @@
                 IUiAutomationConnection instrumentationUiConnection, int debugMode,
                 boolean enableBinderTracking, boolean trackAllocation,
                 boolean isRestrictedBackupMode, boolean persistent, Configuration config,
-                CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings,
+                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                 String buildSerial, AutofillOptions autofillOptions,
                 ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges,
                 SharedMemory serializedSystemFontMap) {
             if (services != null) {
                 if (false) {
                     // Test code to make sure the app could see the passed-in services.
-                    for (String name : services.keySet()) {
-                        if (services.get(name) == null) {
+                    for (Object oname : services.keySet()) {
+                        if (services.get(oname) == null) {
                             continue; // AM just passed in a null service.
                         }
+                        String name = (String) oname;
 
                         // See b/79378449 about the following exemption.
                         switch (name) {
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 2afd98e..d6ff6d3 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -77,7 +77,7 @@
             IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
             int debugMode, boolean enableBinderTracking, boolean trackAllocation,
             boolean restrictedBackupMode, boolean persistent, in Configuration config,
-            in CompatibilityInfo compatInfo, in Map<String, IBinder> services,
+            in CompatibilityInfo compatInfo, in Map services,
             in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions,
             in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges,
             in SharedMemory serializedSystemFontMap);
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 1221148..eee981d 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1331,7 +1331,10 @@
             if (alias == null) {
                 return getName();
             }
-            return alias;
+            return alias
+                    .replace('\t', ' ')
+                    .replace('\n', ' ')
+                    .replace('\r', ' ');
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f5a0c43..9ff13a47 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3215,7 +3215,7 @@
      * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#O}
      * or higher are not allowed to start background services from the background.
      * See
-     * <a href="{@docRoot}/about/versions/oreo/background">
+     * <a href="/about/versions/oreo/background">
      * Background Execution Limits</a>
      * for more details.
      *
@@ -3224,7 +3224,7 @@
      * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
      * or higher are not allowed to start foreground services from the background.
      * See
-     * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+     * <a href="/about/versions/12/behavior-changes-12">
      * Behavior changes: Apps targeting Android 12
      * </a>
      * for more details.
@@ -3278,7 +3278,7 @@
      * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
      * or higher are not allowed to start foreground services from the background.
      * See
-     * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+     * <a href="/about/versions/12/behavior-changes-12">
      * Behavior changes: Apps targeting Android 12
      * </a>
      * for more details.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 546abf8..335e703 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2739,6 +2739,22 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
+    /**
+     * Broadcast Action: One of the suspend conditions have been modified for the packages.
+     * <p>Includes the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been modified
+     * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been modified
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. It is only sent to registered receivers.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGES_SUSPENSION_CHANGED =
+            "android.intent.action.PACKAGES_SUSPENSION_CHANGED";
 
     /**
      * Broadcast Action: Distracting packages have been changed.
@@ -4918,6 +4934,12 @@
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_CAR_LAUNCHER = "android.intent.category.CAR_LAUNCHER";
     /**
+     * Used to indicate that the activity can be used in communal mode.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_COMMUNAL_MODE = "android.intent.category.COMMUNAL_MODE";
+    /**
      * Indicates a Leanback settings activity to be displayed in the Leanback launcher.
      * @hide
      */
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 95c5612..172a51a 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.TestApi;
 import android.app.Activity;
@@ -1368,8 +1369,8 @@
     }
 
     /** @hide */
-    public void setMaxAspectRatio(float maxAspectRatio) {
-        this.mMaxAspectRatio = maxAspectRatio;
+    public void setMaxAspectRatio(@FloatRange(from = 0f) float maxAspectRatio) {
+        this.mMaxAspectRatio = maxAspectRatio >= 0f ? maxAspectRatio : 0f;
     }
 
     /** @hide */
@@ -1378,8 +1379,8 @@
     }
 
     /** @hide */
-    public void setMinAspectRatio(float minAspectRatio) {
-        this.mMinAspectRatio = minAspectRatio;
+    public void setMinAspectRatio(@FloatRange(from = 0f) float minAspectRatio) {
+        this.mMinAspectRatio = minAspectRatio >= 0f ? minAspectRatio : 0f;
     }
 
     /**
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index f2a6a5c..dfc1886a 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -531,10 +531,8 @@
         ai.lockTaskLaunchMode = a.getLockTaskLaunchMode();
         ai.screenOrientation = a.getScreenOrientation();
         ai.resizeMode = a.getResizeMode();
-        Float maxAspectRatio = a.getMaxAspectRatio();
-        ai.setMaxAspectRatio(maxAspectRatio != null ? maxAspectRatio : 0f);
-        Float minAspectRatio = a.getMinAspectRatio();
-        ai.setMinAspectRatio(minAspectRatio != null ? minAspectRatio : 0f);
+        ai.setMaxAspectRatio(a.getMaxAspectRatio());
+        ai.setMinAspectRatio(a.getMinAspectRatio());
         ai.supportsSizeChanges = a.isSupportsSizeChanges();
         ai.requestedVrComponent = a.getRequestedVrComponent();
         ai.rotationAnimation = a.getRotationAnimation();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index b9348a1..115ba50 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -150,6 +150,7 @@
     public static final boolean DEBUG_JAR = false;
     public static final boolean DEBUG_BACKUP = false;
     public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
+    public static final float ASPECT_RATIO_NOT_SET = -1f;
 
     /** File name in an APK for the Android manifest. */
     public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
@@ -2096,7 +2097,7 @@
             pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
             pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1));
             if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized)) {
-                Boolean v = sa.getBoolean(
+                final boolean v = sa.getBoolean(
                         R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized, false);
                 pkg.setNativeHeapZeroInitialized(
                         v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
@@ -2677,7 +2678,7 @@
         for (int index = 0; index < activitiesSize; index++) {
             ParsedActivity activity = activities.get(index);
             // If the max aspect ratio for the activity has already been set, skip.
-            if (activity.getMaxAspectRatio() != null) {
+            if (activity.getMaxAspectRatio() != ASPECT_RATIO_NOT_SET) {
                 continue;
             }
 
@@ -2706,7 +2707,7 @@
         int activitiesSize = activities.size();
         for (int index = 0; index < activitiesSize; index++) {
             ParsedActivity activity = activities.get(index);
-            if (activity.getMinAspectRatio() == null) {
+            if (activity.getMinAspectRatio() == ASPECT_RATIO_NOT_SET) {
                 activity.setMinAspectRatio(activity.getResizeMode(), minAspectRatio);
             }
         }
diff --git a/core/java/android/content/pm/parsing/ParsingUtils.java b/core/java/android/content/pm/parsing/ParsingUtils.java
index 289716a..f3a1740 100644
--- a/core/java/android/content/pm/parsing/ParsingUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingUtils.java
@@ -41,6 +41,8 @@
     public static final int DEFAULT_MIN_SDK_VERSION = 1;
     public static final int DEFAULT_TARGET_SDK_VERSION = 0;
 
+    public static final int NOT_SET = -1;
+
     @Nullable
     public static String buildClassName(String pkg, CharSequence clsSeq) {
         if (clsSeq == null || clsSeq.length() <= 0) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index adb6b76..8ca86f1 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -21,6 +21,7 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+import static android.content.pm.parsing.ParsingPackageUtils.ASPECT_RATIO_NOT_SET;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
 
 import android.annotation.NonNull;
@@ -67,11 +68,8 @@
     private int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     private int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
 
-    @Nullable
-    private Float maxAspectRatio;
-
-    @Nullable
-    private Float minAspectRatio;
+    private float maxAspectRatio = ASPECT_RATIO_NOT_SET;
+    private float minAspectRatio = ASPECT_RATIO_NOT_SET;
 
     private boolean supportsSizeChanges;
 
@@ -234,7 +232,7 @@
         return this;
     }
 
-    public ParsedActivity setMaxAspectRatio(Float maxAspectRatio) {
+    public ParsedActivity setMaxAspectRatio(float maxAspectRatio) {
         this.maxAspectRatio = maxAspectRatio;
         return this;
     }
@@ -260,7 +258,7 @@
         return this;
     }
 
-    public ParsedActivity setMinAspectRatio(Float minAspectRatio) {
+    public ParsedActivity setMinAspectRatio(float minAspectRatio) {
         this.minAspectRatio = minAspectRatio;
         return this;
     }
@@ -375,8 +373,8 @@
         dest.writeInt(this.lockTaskLaunchMode);
         dest.writeInt(this.screenOrientation);
         dest.writeInt(this.resizeMode);
-        dest.writeValue(this.maxAspectRatio);
-        dest.writeValue(this.minAspectRatio);
+        dest.writeFloat(this.maxAspectRatio);
+        dest.writeFloat(this.minAspectRatio);
         dest.writeBoolean(this.supportsSizeChanges);
         dest.writeString(this.requestedVrComponent);
         dest.writeInt(this.rotationAnimation);
@@ -412,8 +410,8 @@
         this.lockTaskLaunchMode = in.readInt();
         this.screenOrientation = in.readInt();
         this.resizeMode = in.readInt();
-        this.maxAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
-        this.minAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
+        this.maxAspectRatio = in.readFloat();
+        this.minAspectRatio = in.readFloat();
         this.supportsSizeChanges = in.readBoolean();
         this.requestedVrComponent = in.readString();
         this.rotationAnimation = in.readInt();
@@ -505,13 +503,11 @@
         return resizeMode;
     }
 
-    @Nullable
-    public Float getMaxAspectRatio() {
+    public float getMaxAspectRatio() {
         return maxAspectRatio;
     }
 
-    @Nullable
-    public Float getMinAspectRatio() {
+    public float getMinAspectRatio() {
         return minAspectRatio;
     }
 
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index ac6bcd0..dc7eb80 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
 import static android.content.pm.parsing.component.ComponentParseUtils.flag;
 
 import android.annotation.NonNull;
@@ -269,15 +270,15 @@
                     activity, tag, null, pkg, sa, 0, useRoundIcon, input,
                     R.styleable.AndroidManifestActivityAlias_banner,
                     R.styleable.AndroidManifestActivityAlias_description,
-                    null /*directBootAwareAttr*/,
+                    NOT_SET /*directBootAwareAttr*/,
                     R.styleable.AndroidManifestActivityAlias_enabled,
                     R.styleable.AndroidManifestActivityAlias_icon,
                     R.styleable.AndroidManifestActivityAlias_label,
                     R.styleable.AndroidManifestActivityAlias_logo,
                     R.styleable.AndroidManifestActivityAlias_name,
-                    null /*processAttr*/,
+                    NOT_SET /*processAttr*/,
                     R.styleable.AndroidManifestActivityAlias_roundIcon,
-                    null /*splitNameAttr*/,
+                    NOT_SET /*splitNameAttr*/,
                     R.styleable.AndroidManifestActivityAlias_attributionTags);
             if (result.isError()) {
                 return result;
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
index ab596d3..6d798fd 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -16,8 +16,9 @@
 
 package android.content.pm.parsing.component;
 
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
+
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.Property;
 import android.content.pm.parsing.ParsingPackage;
@@ -41,9 +42,8 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     static <Component extends ParsedComponent> ParseResult<Component> parseComponent(
             Component component, String tag, ParsingPackage pkg, TypedArray array,
-            boolean useRoundIcon, ParseInput input, int bannerAttr,
-            @Nullable Integer descriptionAttr, int iconAttr, int labelAttr, int logoAttr,
-            int nameAttr, int roundIconAttr) {
+            boolean useRoundIcon, ParseInput input, int bannerAttr, int descriptionAttr,
+            int iconAttr, int labelAttr, int logoAttr, int nameAttr, int roundIconAttr) {
         String name = array.getNonConfigurationString(nameAttr, 0);
         if (TextUtils.isEmpty(name)) {
             return input.error(tag + " does not specify android:name");
@@ -81,7 +81,7 @@
             component.setBanner(bannerVal);
         }
 
-        if (descriptionAttr != null) {
+        if (descriptionAttr != NOT_SET) {
             component.setDescriptionRes(array.getResourceId(descriptionAttr, 0));
         }
 
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
index 5977c83..f122fd6 100644
--- a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
@@ -16,15 +16,17 @@
 
 package android.content.pm.parsing.component;
 
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
+
 import android.annotation.NonNull;
 import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 
 import com.android.internal.R;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -46,7 +48,7 @@
             ParseResult<ParsedInstrumentation> result = ParsedComponentUtils.parseComponent(
                     instrumentation, tag, pkg, sa, useRoundIcon, input,
                     R.styleable.AndroidManifestInstrumentation_banner,
-                    null /*descriptionAttr*/,
+                    NOT_SET /*descriptionAttr*/,
                     R.styleable.AndroidManifestInstrumentation_icon,
                     R.styleable.AndroidManifestInstrumentation_label,
                     R.styleable.AndroidManifestInstrumentation_logo,
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
index 7ccca93..869e81c 100644
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
@@ -16,8 +16,9 @@
 
 package android.content.pm.parsing.component;
 
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
+
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.IntentFilter;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingUtils;
@@ -45,11 +46,10 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     static <Component extends ParsedMainComponent> ParseResult<Component> parseMainComponent(
             Component component, String tag, String[] separateProcesses, ParsingPackage pkg,
-            TypedArray array, int flags, boolean useRoundIcon, ParseInput input,
-            int bannerAttr, int descriptionAttr, @Nullable Integer directBootAwareAttr,
-            @Nullable Integer enabledAttr, int iconAttr, int labelAttr, int logoAttr, int nameAttr,
-            @Nullable Integer processAttr, int roundIconAttr, @Nullable Integer splitNameAttr,
-            @Nullable Integer attributionTagsAttr) {
+            TypedArray array, int flags, boolean useRoundIcon, ParseInput input, int bannerAttr,
+            int descriptionAttr, int directBootAwareAttr, int enabledAttr, int iconAttr,
+            int labelAttr, int logoAttr, int nameAttr, int processAttr, int roundIconAttr,
+            int splitNameAttr, int attributionTagsAttr) {
         ParseResult<Component> result = ParsedComponentUtils.parseComponent(component, tag, pkg,
                 array, useRoundIcon, input, bannerAttr, descriptionAttr, iconAttr, labelAttr,
                 logoAttr, nameAttr, roundIconAttr);
@@ -57,18 +57,18 @@
             return result;
         }
 
-        if (directBootAwareAttr != null) {
+        if (directBootAwareAttr != NOT_SET) {
             component.setDirectBootAware(array.getBoolean(directBootAwareAttr, false));
             if (component.isDirectBootAware()) {
                 pkg.setPartiallyDirectBootAware(true);
             }
         }
 
-        if (enabledAttr != null) {
+        if (enabledAttr != NOT_SET) {
             component.setEnabled(array.getBoolean(enabledAttr, true));
         }
 
-        if (processAttr != null) {
+        if (processAttr != NOT_SET) {
             CharSequence processName;
             if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
                 processName = array.getNonConfigurationString(processAttr,
@@ -91,11 +91,11 @@
             component.setProcessName(processNameResult.getResult());
         }
 
-        if (splitNameAttr != null) {
+        if (splitNameAttr != NOT_SET) {
             component.setSplitName(array.getNonConfigurationString(splitNameAttr, 0));
         }
 
-        if (attributionTagsAttr != null) {
+        if (attributionTagsAttr != NOT_SET) {
             final String attributionTags = array.getNonConfigurationString(attributionTagsAttr, 0);
             if (attributionTags != null) {
                 component.setAttributionTags(attributionTags.split("\\|"));
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index eec333c..5a7a5ef 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -16,6 +16,8 @@
 
 package android.content.pm.parsing.component;
 
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
+
 import android.annotation.NonNull;
 import android.content.pm.PermissionInfo;
 import android.content.pm.parsing.ParsingPackage;
@@ -163,7 +165,7 @@
             result = ParsedComponentUtils.parseComponent(
                     permission, tag, pkg, sa, useRoundIcon, input,
                     R.styleable.AndroidManifestPermissionTree_banner,
-                    null /*descriptionAttr*/,
+                    NOT_SET /*descriptionAttr*/,
                     R.styleable.AndroidManifestPermissionTree_icon,
                     R.styleable.AndroidManifestPermissionTree_label,
                     R.styleable.AndroidManifestPermissionTree_logo,
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index d4e19af..54dd295 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -101,7 +101,7 @@
             proc.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1));
             proc.setMemtagMode(sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1));
             if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
-                Boolean v = sa.getBoolean(
+                final boolean v = sa.getBoolean(
                         R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized, false);
                 proc.setNativeHeapZeroInitialized(
                         v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
index 324612d..1a3fc85 100644
--- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -16,6 +16,8 @@
 
 package android.content.pm.parsing.result;
 
+import static android.content.pm.parsing.ParsingUtils.NOT_SET;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -62,7 +64,7 @@
     private ArrayMap<Long, String> mDeferredErrors = null;
 
     private String mPackageName;
-    private Integer mTargetSdkVersion;
+    private int mTargetSdkVersion = NOT_SET;
 
     /**
      * Specifically for {@link PackageManager#getPackageArchiveInfo(String, int)} where
@@ -119,7 +121,7 @@
             // how many APKs they're going through.
             mDeferredErrors.erase();
         }
-        mTargetSdkVersion = null;
+        mTargetSdkVersion = NOT_SET;
         return this;
     }
 
@@ -139,7 +141,7 @@
         if (DEBUG_THROW_ALL_ERRORS) {
             return error(parseError);
         }
-        if (mTargetSdkVersion != null) {
+        if (mTargetSdkVersion != NOT_SET) {
             if (mDeferredErrors != null && mDeferredErrors.containsKey(deferredError)) {
                 // If the map already contains the key, that means it's already been checked and
                 // found to be disabled. Otherwise it would've failed when mTargetSdkVersion was
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 3f3db29..c8c122d 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -438,9 +438,16 @@
     }
 
     private class OnAuthenticationCancelListener implements CancellationSignal.OnCancelListener {
+        private final long mAuthRequestId;
+
+        OnAuthenticationCancelListener(long id) {
+            mAuthRequestId = id;
+        }
+
         @Override
         public void onCancel() {
-            cancelAuthentication();
+            Log.d(TAG, "Cancel BP authentication requested for: " + mAuthRequestId);
+            cancelAuthentication(mAuthRequestId);
         }
     }
 
@@ -853,10 +860,12 @@
      * @param userId The user to authenticate
      * @param operationId The keystore operation associated with authentication
      *
+     * @return A requestId that can be used to cancel this operation.
+     *
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
-    public void authenticateUserForOperation(
+    public long authenticateUserForOperation(
             @NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull AuthenticationCallback callback,
@@ -871,7 +880,8 @@
         if (callback == null) {
             throw new IllegalArgumentException("Must supply a callback");
         }
-        authenticateInternal(operationId, cancel, executor, callback, userId);
+
+        return authenticateInternal(operationId, cancel, executor, callback, userId);
     }
 
     /**
@@ -1002,10 +1012,10 @@
         authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId());
     }
 
-    private void cancelAuthentication() {
+    private void cancelAuthentication(long requestId) {
         if (mService != null) {
             try {
-                mService.cancelAuthentication(mToken, mContext.getOpPackageName());
+                mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId);
             } catch (RemoteException e) {
                 Log.e(TAG, "Unable to cancel authentication", e);
             }
@@ -1024,7 +1034,7 @@
         authenticateInternal(operationId, cancel, executor, callback, userId);
     }
 
-    private void authenticateInternal(
+    private long authenticateInternal(
             long operationId,
             @NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
@@ -1040,9 +1050,7 @@
         try {
             if (cancel.isCanceled()) {
                 Log.w(TAG, "Authentication already canceled");
-                return;
-            } else {
-                cancel.setOnCancelListener(new OnAuthenticationCancelListener());
+                return -1;
             }
 
             mExecutor = executor;
@@ -1065,14 +1073,16 @@
                 promptInfo = mPromptInfo;
             }
 
-            mService.authenticate(mToken, operationId, userId, mBiometricServiceReceiver,
-                    mContext.getOpPackageName(), promptInfo);
-
+            final long authId = mService.authenticate(mToken, operationId, userId,
+                    mBiometricServiceReceiver, mContext.getOpPackageName(), promptInfo);
+            cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
+            return authId;
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception while authenticating", e);
             mExecutor.execute(() -> callback.onAuthenticationError(
                     BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                     mContext.getString(R.string.biometric_error_hw_unavailable)));
+            return -1;
         }
     }
 
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index 4c2a9ae..91f794c 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -41,13 +41,14 @@
     // Retrieve the package where BIometricOrompt's UI is implemented
     String getUiPackage();
 
-    // Requests authentication. The service choose the appropriate biometric to use, and show
-    // the corresponding BiometricDialog.
-    void authenticate(IBinder token, long sessionId, int userId,
+    // Requests authentication. The service chooses the appropriate biometric to use, and shows
+    // the corresponding BiometricDialog. A requestId is returned that can be used to cancel
+    // this operation.
+    long authenticate(IBinder token, long sessionId, int userId,
             IBiometricServiceReceiver receiver, String opPackageName, in PromptInfo promptInfo);
 
-    // Cancel authentication for the given sessionId
-    void cancelAuthentication(IBinder token, String opPackageName);
+    // Cancel authentication for the given requestId.
+    void cancelAuthentication(IBinder token, String opPackageName, long requestId);
 
     // TODO(b/141025588): Make userId the first arg to be consistent with hasEnrolledBiometrics.
     // Checks if biometrics can be used.
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index 876513f..addd622 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -48,13 +48,13 @@
     // startPreparedClient().
     void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId,
             int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
-            int cookie, boolean allowBackgroundAuthentication);
+            long requestId, int cookie, boolean allowBackgroundAuthentication);
 
     // Starts authentication with the previously prepared client.
     void startPreparedClient(int cookie);
 
-    // Cancels authentication.
-    void cancelAuthenticationFromService(IBinder token, String opPackageName);
+    // Cancels authentication for the given requestId.
+    void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId);
 
     // Determine if HAL is loaded and ready
     boolean isHardwareDetected(String opPackageName);
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 64b5118..2c3c8c3 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -36,13 +36,14 @@
     // Retrieve static sensor properties for all biometric sensors
     List<SensorPropertiesInternal> getSensorProperties(String opPackageName);
 
-    // Requests authentication. The service choose the appropriate biometric to use, and show
-    // the corresponding BiometricDialog.
-    void authenticate(IBinder token, long operationId, int userId,
+    // Requests authentication. The service chooses the appropriate biometric to use, and shows
+    // the corresponding BiometricDialog. A requestId is returned that can be used to cancel
+    // this operation.
+    long authenticate(IBinder token, long operationId, int userId,
             IBiometricServiceReceiver receiver, String opPackageName, in PromptInfo promptInfo);
 
-    // Cancel authentication for the given session.
-    void cancelAuthentication(IBinder token, String opPackageName);
+    // Cancel authentication for the given requestId.
+    void cancelAuthentication(IBinder token, String opPackageName, long requestId);
 
     // Checks if biometrics can be used.
     int canAuthenticate(String opPackageName, int userId, int callingUserId, int authenticators);
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 385ad2d..56f8142 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -58,7 +58,7 @@
 public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants {
 
     private static final String TAG = "FaceManager";
-    private static final boolean DEBUG = true;
+
     private static final int MSG_ENROLL_RESULT = 100;
     private static final int MSG_ACQUIRED = 101;
     private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
@@ -207,13 +207,9 @@
             throw new IllegalArgumentException("Must supply an authentication callback");
         }
 
-        if (cancel != null) {
-            if (cancel.isCanceled()) {
-                Slog.w(TAG, "authentication already canceled");
-                return;
-            } else {
-                cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
-            }
+        if (cancel != null && cancel.isCanceled()) {
+            Slog.w(TAG, "authentication already canceled");
+            return;
         }
 
         if (mService != null) {
@@ -223,17 +219,18 @@
                 mCryptoObject = crypto;
                 final long operationId = crypto != null ? crypto.getOpId() : 0;
                 Trace.beginSection("FaceManager#authenticate");
-                mService.authenticate(mToken, operationId, userId, mServiceReceiver,
-                        mContext.getOpPackageName(), isKeyguardBypassEnabled);
+                final long authId = mService.authenticate(mToken, operationId, userId,
+                        mServiceReceiver, mContext.getOpPackageName(), isKeyguardBypassEnabled);
+                if (cancel != null) {
+                    cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
+                }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception while authenticating: ", e);
-                if (callback != null) {
-                    // Though this may not be a hardware issue, it will cause apps to give up or
-                    // try again later.
-                    callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
-                            getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
-                                    0 /* vendorCode */));
-                }
+                // Though this may not be a hardware issue, it will cause apps to give up or
+                // try again later.
+                callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
+                        getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+                                0 /* vendorCode */));
             } finally {
                 Trace.endSection();
             }
@@ -255,14 +252,14 @@
         if (cancel.isCanceled()) {
             Slog.w(TAG, "Detection already cancelled");
             return;
-        } else {
-            cancel.setOnCancelListener(new OnFaceDetectionCancelListener());
         }
 
         mFaceDetectionCallback = callback;
 
         try {
-            mService.detectFace(mToken, userId, mServiceReceiver, mContext.getOpPackageName());
+            final long authId = mService.detectFace(
+                    mToken, userId, mServiceReceiver, mContext.getOpPackageName());
+            cancel.setOnCancelListener(new OnFaceDetectionCancelListener(authId));
         } catch (RemoteException e) {
             Slog.w(TAG, "Remote exception when requesting finger detect", e);
         }
@@ -726,23 +723,23 @@
         }
     }
 
-    private void cancelAuthentication(CryptoObject cryptoObject) {
+    private void cancelAuthentication(long requestId) {
         if (mService != null) {
             try {
-                mService.cancelAuthentication(mToken, mContext.getOpPackageName());
+                mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         }
     }
 
-    private void cancelFaceDetect() {
+    private void cancelFaceDetect(long requestId) {
         if (mService == null) {
             return;
         }
 
         try {
-            mService.cancelFaceDetect(mToken, mContext.getOpPackageName());
+            mService.cancelFaceDetect(mToken, mContext.getOpPackageName(), requestId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -794,9 +791,9 @@
         // This is used as a last resort in case a vendor string is missing
         // It should not happen for anything other than FACE_ERROR_VENDOR, but
         // warn and use the default if all else fails.
-        // TODO(b/196639965): update string
         Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
-        return "";
+        return context.getString(
+                com.android.internal.R.string.face_error_vendor_unknown);
     }
 
     /**
@@ -1110,22 +1107,30 @@
     }
 
     private class OnAuthenticationCancelListener implements OnCancelListener {
-        private final CryptoObject mCrypto;
+        private final long mAuthRequestId;
 
-        OnAuthenticationCancelListener(CryptoObject crypto) {
-            mCrypto = crypto;
+        OnAuthenticationCancelListener(long id) {
+            mAuthRequestId = id;
         }
 
         @Override
         public void onCancel() {
-            cancelAuthentication(mCrypto);
+            Slog.d(TAG, "Cancel face authentication requested for: " + mAuthRequestId);
+            cancelAuthentication(mAuthRequestId);
         }
     }
 
     private class OnFaceDetectionCancelListener implements OnCancelListener {
+        private final long mAuthRequestId;
+
+        OnFaceDetectionCancelListener(long id) {
+            mAuthRequestId = id;
+        }
+
         @Override
         public void onCancel() {
-            cancelFaceDetect();
+            Slog.d(TAG, "Cancel face detect requested for: " + mAuthRequestId);
+            cancelFaceDetect(mAuthRequestId);
         }
     }
 
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index db02a0ef..e919824 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -44,34 +44,36 @@
     // Retrieve static sensor properties for the specified sensor
     FaceSensorPropertiesInternal getSensorProperties(int sensorId, String opPackageName);
 
-    // Authenticate the given sessionId with a face
-    void authenticate(IBinder token, long operationId, int userId, IFaceServiceReceiver receiver,
+    // Authenticate with a face. A requestId is returned that can be used to cancel this operation.
+    long authenticate(IBinder token, long operationId, int userId, IFaceServiceReceiver receiver,
             String opPackageName, boolean isKeyguardBypassEnabled);
 
     // Uses the face hardware to detect for the presence of a face, without giving details
-    // about accept/reject/lockout.
-    void detectFace(IBinder token, int userId, IFaceServiceReceiver receiver, String opPackageName);
+    // about accept/reject/lockout. A requestId is returned that can be used to cancel this
+    // operation.
+    long detectFace(IBinder token, int userId, IFaceServiceReceiver receiver, String opPackageName);
 
     // This method prepares the service to start authenticating, but doesn't start authentication.
     // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
     // called from BiometricService. The additional uid, pid, userId arguments should be determined
     // by BiometricService. To start authentication after the clients are ready, use
     // startPreparedClient().
-    void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token, long operationId,
-            int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
-            int cookie, boolean allowBackgroundAuthentication);
+    void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token,
+            long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
+            String opPackageName, long requestId, int cookie,
+            boolean allowBackgroundAuthentication);
 
     // Starts authentication with the previously prepared client.
     void startPreparedClient(int sensorId, int cookie);
 
-    // Cancel authentication for the given sessionId
-    void cancelAuthentication(IBinder token, String opPackageName);
+    // Cancel authentication for the given requestId.
+    void cancelAuthentication(IBinder token, String opPackageName, long requestId);
 
-    // Cancel face detection
-    void cancelFaceDetect(IBinder token, String opPackageName);
+    // Cancel face detection for the given requestId.
+    void cancelFaceDetect(IBinder token, String opPackageName, long requestId);
 
     // Same as above, with extra arguments.
-    void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName);
+    void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
 
     // Start face enrollment
     void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 87d45b9..7c42dc0 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -189,22 +189,30 @@
     }
 
     private class OnAuthenticationCancelListener implements OnCancelListener {
-        private android.hardware.biometrics.CryptoObject mCrypto;
+        private final long mAuthRequestId;
 
-        public OnAuthenticationCancelListener(android.hardware.biometrics.CryptoObject crypto) {
-            mCrypto = crypto;
+        OnAuthenticationCancelListener(long id) {
+            mAuthRequestId = id;
         }
 
         @Override
         public void onCancel() {
-            cancelAuthentication(mCrypto);
+            Slog.d(TAG, "Cancel fingerprint authentication requested for: " + mAuthRequestId);
+            cancelAuthentication(mAuthRequestId);
         }
     }
 
     private class OnFingerprintDetectionCancelListener implements OnCancelListener {
+        private final long mAuthRequestId;
+
+        OnFingerprintDetectionCancelListener(long id) {
+            mAuthRequestId = id;
+        }
+
         @Override
         public void onCancel() {
-            cancelFingerprintDetect();
+            Slog.d(TAG, "Cancel fingerprint detect requested for: " + mAuthRequestId);
+            cancelFingerprintDetect(mAuthRequestId);
         }
     }
 
@@ -552,13 +560,9 @@
             throw new IllegalArgumentException("Must supply an authentication callback");
         }
 
-        if (cancel != null) {
-            if (cancel.isCanceled()) {
-                Slog.w(TAG, "authentication already canceled");
-                return;
-            } else {
-                cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
-            }
+        if (cancel != null && cancel.isCanceled()) {
+            Slog.w(TAG, "authentication already canceled");
+            return;
         }
 
         if (mService != null) {
@@ -567,8 +571,11 @@
                 mAuthenticationCallback = callback;
                 mCryptoObject = crypto;
                 final long operationId = crypto != null ? crypto.getOpId() : 0;
-                mService.authenticate(mToken, operationId, sensorId, userId, mServiceReceiver,
-                        mContext.getOpPackageName());
+                final long authId = mService.authenticate(mToken, operationId, sensorId, userId,
+                        mServiceReceiver, mContext.getOpPackageName());
+                if (cancel != null) {
+                    cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
+                }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception while authenticating: ", e);
                 // Though this may not be a hardware issue, it will cause apps to give up or try
@@ -595,15 +602,14 @@
         if (cancel.isCanceled()) {
             Slog.w(TAG, "Detection already cancelled");
             return;
-        } else {
-            cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener());
         }
 
         mFingerprintDetectionCallback = callback;
 
         try {
-            mService.detectFingerprint(mToken, userId, mServiceReceiver,
+            final long authId = mService.detectFingerprint(mToken, userId, mServiceReceiver,
                     mContext.getOpPackageName());
+            cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener(authId));
         } catch (RemoteException e) {
             Slog.w(TAG, "Remote exception when requesting finger detect", e);
         }
@@ -1320,21 +1326,21 @@
         }
     }
 
-    private void cancelAuthentication(android.hardware.biometrics.CryptoObject cryptoObject) {
+    private void cancelAuthentication(long requestId) {
         if (mService != null) try {
-            mService.cancelAuthentication(mToken, mContext.getOpPackageName());
+            mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
-    private void cancelFingerprintDetect() {
+    private void cancelFingerprintDetect(long requestId) {
         if (mService == null) {
             return;
         }
 
         try {
-            mService.cancelFingerprintDetect(mToken, mContext.getOpPackageName());
+            mService.cancelFingerprintDetect(mToken, mContext.getOpPackageName(), requestId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1390,9 +1396,9 @@
         // This is used as a last resort in case a vendor string is missing
         // It should not happen for anything other than FINGERPRINT_ERROR_VENDOR, but
         // warn and use the default if all else fails.
-        // TODO(b/196639965): update string
         Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
-        return "";
+        return context.getString(
+                com.android.internal.R.string.fingerprint_error_vendor_unknown);
     }
 
     /**
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 3979afe..4774827 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -48,15 +48,16 @@
     // Retrieve static sensor properties for the specified sensor
     FingerprintSensorPropertiesInternal getSensorProperties(int sensorId, String opPackageName);
 
-    // Authenticate the given sessionId with a fingerprint. This is protected by
-    // USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes
-    // through FingerprintManager now.
-    void authenticate(IBinder token, long operationId, int sensorId, int userId,
+    // Authenticate with a fingerprint. This is protected by USE_FINGERPRINT/USE_BIOMETRIC
+    // permission. This is effectively deprecated, since it only comes through FingerprintManager
+    // now. A requestId is returned that can be used to cancel this operation.
+    long authenticate(IBinder token, long operationId, int sensorId, int userId,
             IFingerprintServiceReceiver receiver, String opPackageName);
 
     // Uses the fingerprint hardware to detect for the presence of a finger, without giving details
-    // about accept/reject/lockout.
-    void detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver,
+    // about accept/reject/lockout. A requestId is returned that can be used to cancel this
+    // operation.
+    long detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver,
             String opPackageName);
 
     // This method prepares the service to start authenticating, but doesn't start authentication.
@@ -65,21 +66,21 @@
     // by BiometricService. To start authentication after the clients are ready, use
     // startPreparedClient().
     void prepareForAuthentication(int sensorId, IBinder token, long operationId, int userId,
-            IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie,
-            boolean allowBackgroundAuthentication);
+            IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId,
+            int cookie, boolean allowBackgroundAuthentication);
 
     // Starts authentication with the previously prepared client.
     void startPreparedClient(int sensorId, int cookie);
 
-    // Cancel authentication for the given sessionId
-    void cancelAuthentication(IBinder token, String opPackageName);
+    // Cancel authentication for the given requestId.
+    void cancelAuthentication(IBinder token, String opPackageName, long requestId);
 
-    // Cancel finger detection
-    void cancelFingerprintDetect(IBinder token, String opPackageName);
+    // Cancel finger detection for the given requestId.
+    void cancelFingerprintDetect(IBinder token, String opPackageName, long requestId);
 
     // Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
     // an additional uid, pid, userid.
-    void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName);
+    void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
 
     // Start fingerprint enrollment
     void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
diff --git a/core/java/android/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java
index 7fd02116..0686dd6 100644
--- a/core/java/android/os/ExternalVibration.java
+++ b/core/java/android/os/ExternalVibration.java
@@ -42,25 +42,38 @@
     // boundaries.
     @NonNull
     private IBinder mToken;
-
     public ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
             @NonNull IExternalVibrationController controller) {
+        this(uid, pkg, attrs, controller, new Binder());
+    }
+
+    private ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
+            @NonNull IExternalVibrationController controller, @NonNull IBinder token) {
         mUid = uid;
         mPkg = Preconditions.checkNotNull(pkg);
         mAttrs = Preconditions.checkNotNull(attrs);
         mController = Preconditions.checkNotNull(controller);
-        mToken = new Binder();
+        mToken = Preconditions.checkNotNull(token);
+
+        // IExternalVibrationController is a hidden AIDL interface with implementation provided by
+        // the audio framework to allow mute/unmute control over the external vibration.
+        //
+        // Transactions are locked in audioflinger, and should be blocking to avoid racing
+        // conditions on multiple audio playback.
+        //
+        // They can also be triggered before starting a new external vibration in
+        // IExternalVibratorService, as the ongoing external vibration needs to be muted before the
+        // new one can start, which also requires blocking calls to mute.
+        Binder.allowBlocking(mController.asBinder());
     }
 
     private ExternalVibration(Parcel in) {
-        mUid = in.readInt();
-        mPkg = in.readString();
-        mAttrs = readAudioAttributes(in);
-        mController = IExternalVibrationController.Stub.asInterface(in.readStrongBinder());
-        mToken = in.readStrongBinder();
+        this(in.readInt(), in.readString(), readAudioAttributes(in),
+                IExternalVibrationController.Stub.asInterface(in.readStrongBinder()),
+                in.readStrongBinder());
     }
 
-    private AudioAttributes readAudioAttributes(Parcel in) {
+    private static AudioAttributes readAudioAttributes(Parcel in) {
         int usage = in.readInt();
         int contentType = in.readInt();
         int capturePreset = in.readInt();
diff --git a/services/core/java/com/android/server/utils/DeviceConfigInterface.java b/core/java/android/provider/DeviceConfigInterface.java
similarity index 63%
rename from services/core/java/com/android/server/utils/DeviceConfigInterface.java
rename to core/java/android/provider/DeviceConfigInterface.java
index ff60903..0a888f4 100644
--- a/services/core/java/com/android/server/utils/DeviceConfigInterface.java
+++ b/core/java/android/provider/DeviceConfigInterface.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,57 +14,103 @@
  * limitations under the License.
  */
 
-package com.android.server.utils;
+package android.provider;
+
+import static android.provider.Settings.ResetMode;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.BadConfigException;
+import android.provider.DeviceConfig.Properties;
 
 import java.util.concurrent.Executor;
 
 /**
  * Abstraction around {@link DeviceConfig} to allow faking device configuration in tests.
+ *
+ * @hide
  */
 public interface DeviceConfigInterface {
+
     /**
+     * @hide
      * @see DeviceConfig#getProperty
      */
     @Nullable
     String getProperty(@NonNull String namespace, @NonNull String name);
 
     /**
+     * @hide
+     * @see DeviceConfig#getProperties
+     */
+    @NonNull
+    Properties getProperties(@NonNull String namespace, @NonNull String... names);
+
+    /**
+     * @hide
+     * @see DeviceConfig#setProperty
+     */
+    boolean setProperty(@NonNull String namespace, @NonNull String name, @Nullable String value,
+            boolean makeDefault);
+
+    /**
+     * @hide
+     * @see DeviceConfig#setProperties
+     */
+    boolean setProperties(@NonNull Properties properties) throws BadConfigException;
+
+    /**
+     * @hide
+     * @see DeviceConfig#deleteProperty
+     */
+    boolean deleteProperty(@NonNull String namespace, @NonNull String name);
+
+    /**
+     * @hide
+     * @see DeviceConfig#resetToDefaults
+     */
+    void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace);
+
+    /**
+     * @hide
      * @see DeviceConfig#getString
      */
     @NonNull
     String getString(@NonNull String namespace, @NonNull String name, @NonNull String defaultValue);
 
     /**
+     * @hide
      * @see DeviceConfig#getInt
      */
     int getInt(@NonNull String namespace, @NonNull String name, int defaultValue);
 
     /**
+     * @hide
      * @see DeviceConfig#getLong
      */
     long getLong(@NonNull String namespace, @NonNull String name, long defaultValue);
 
     /**
+     * @hide
      * @see DeviceConfig#getBoolean
      */
     boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue);
 
     /**
+     * @hide
      * @see DeviceConfig#getFloat
      */
     float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue);
 
     /**
+     * @hide
      * @see DeviceConfig#addOnPropertiesChangedListener
      */
     void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor,
             @NonNull DeviceConfig.OnPropertiesChangedListener listener);
 
     /**
+     * @hide
      * @see DeviceConfig#removeOnPropertiesChangedListener
      */
     void removeOnPropertiesChangedListener(
@@ -72,7 +118,10 @@
 
     /**
      * Calls through to the real {@link DeviceConfig}.
+     *
+     * @hide
      */
+    @NonNull
     DeviceConfigInterface REAL = new DeviceConfigInterface() {
         @Override
         public String getProperty(String namespace, String name) {
@@ -80,6 +129,36 @@
         }
 
         @Override
+        public DeviceConfig.Properties getProperties(@NonNull String namespace,
+                @NonNull String... names) {
+            return DeviceConfig.getProperties(namespace, names);
+        }
+
+        @Override
+        public boolean setProperty(@NonNull String namespace,
+                @NonNull String name,
+                @Nullable String value, boolean makeDefault) {
+            return DeviceConfig.setProperty(namespace, name, value, makeDefault);
+        }
+
+        @Override
+        public boolean setProperties(@NonNull Properties properties)
+                throws BadConfigException {
+            return DeviceConfig.setProperties(properties);
+        }
+
+        @Override
+        public boolean deleteProperty(@NonNull String namespace,
+                @NonNull String name) {
+            return DeviceConfig.deleteProperty(namespace, name);
+        }
+
+        @Override
+        public void resetToDefaults(int resetMode, @Nullable String namespace) {
+            DeviceConfig.resetToDefaults(resetMode, namespace);
+        }
+
+        @Override
         public String getString(String namespace, String name, String defaultValue) {
             return DeviceConfig.getString(namespace, name, defaultValue);
         }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a4e7a43..643c1bc 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1889,10 +1889,12 @@
     public void setChildSurfacePackage(@NonNull SurfaceControlViewHost.SurfacePackage p) {
         final SurfaceControl lastSc = mSurfacePackage != null ?
                 mSurfacePackage.getSurfaceControl() : null;
-        if (mSurfaceControl != null && lastSc != null) {
-            mTmpTransaction.reparent(lastSc, null).apply();
-            mSurfacePackage.release();
-        } else if (mSurfaceControl != null) {
+        if (mSurfaceControl != null) {
+            if (lastSc != null) {
+                mTmpTransaction.reparent(lastSc, null);
+                mSurfacePackage.release();
+            }
+
             reparentSurfacePackage(mTmpTransaction, p);
             mTmpTransaction.apply();
         }
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index 0ca8a86..1ad0452 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -16,6 +16,7 @@
 
 package android.window;
 
+import android.view.RemoteAnimationDefinition;
 import android.window.ITaskFragmentOrganizer;
 
 /** @hide */
@@ -30,4 +31,17 @@
      * Unregisters a previously registered TaskFragmentOrganizer.
      */
     void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
+
+    /**
+     * Registers remote animations per transition type for the organizer. It will override the
+     * animations if the transition only contains windows that belong to the organized
+     * TaskFragments.
+     */
+    void registerRemoteAnimations(in ITaskFragmentOrganizer organizer,
+        in RemoteAnimationDefinition definition);
+
+    /**
+     * Unregisters remote animations per transition type for the organizer.
+     */
+    void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer);
 }
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index f22f0b2..337c5a1 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -23,6 +23,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.view.RemoteAnimationDefinition;
 
 import java.util.concurrent.Executor;
 
@@ -90,6 +91,34 @@
         }
     }
 
+    /**
+     * Registers remote animations per transition type for the organizer. It will override the
+     * animations if the transition only contains windows that belong to the organized
+     * TaskFragments.
+     * @hide
+     */
+    @CallSuper
+    public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) {
+        try {
+            getController().registerRemoteAnimations(mInterface, definition);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters remote animations per transition type for the organizer.
+     * @hide
+     */
+    @CallSuper
+    public void unregisterRemoteAnimations() {
+        try {
+            getController().unregisterRemoteAnimations(mInterface);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** Called when a TaskFragment is created and organized by this organizer. */
     public void onTaskFragmentAppeared(
             @NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {}
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 179ac8b..a611d65d 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -39,6 +39,9 @@
 import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_FULL_SCREEN;
 import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_UNKNOWN_MODE;
 import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW;
+import static com.android.internal.util.FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_CLICKED;
+import static com.android.internal.util.FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SERVICE_DISABLED;
+import static com.android.internal.util.FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SHOWN;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -50,6 +53,16 @@
 
 /** Methods for logging accessibility states. */
 public final class AccessibilityStatsLogUtils {
+    /** The status represents an accessibility privacy warning has been shown. */
+    public static int ACCESSIBILITY_PRIVACY_WARNING_STATUS_SHOWN =
+            NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SHOWN;
+    /** The status represents an accessibility privacy warning has been clicked to review. */
+    public static int ACCESSIBILITY_PRIVACY_WARNING_STATUS_CLICKED =
+            NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_CLICKED;
+    /** The status represents an accessibility privacy warning service has been disabled. */
+    public static int ACCESSIBILITY_PRIVACY_WARNING_STATUS_SERVICE_DISABLED =
+            NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SERVICE_DISABLED;
+
     private static final int UNKNOWN_STATUS =
             ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__UNKNOWN;
 
@@ -154,6 +167,23 @@
                 convertToLoggingMagnificationMode(mode));
     }
 
+    /**
+     * Logs the warning status of the non-a11yTool service. Calls this when the warning status is
+     * changed.
+     *
+     * @param packageName    The package name of the non-a11yTool service
+     * @param status         The warning status of the non-a11yTool service, it should be one of
+     *                       {@code ACCESSIBILITY_PRIVACY_WARNING_STATUS_SHOWN},{@code
+     *                       ACCESSIBILITY_PRIVACY_WARNING_STATUS_CLICKED} and {@code
+     *                       ACCESSIBILITY_PRIVACY_WARNING_STATUS_SERVICE_DISABLED}
+     * @param durationMillis The duration in milliseconds between current and previous status
+     */
+    public static void logNonA11yToolServiceWarningReported(String packageName, int status,
+            long durationMillis) {
+        FrameworkStatsLog.write(FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORT,
+                packageName, status, durationMillis);
+    }
+
     private static boolean isAccessibilityFloatingMenuEnabled(Context context) {
         return Settings.Secure.getInt(context.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 84354d9..ec224e5 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -72,17 +72,19 @@
     private Resources mSuspendingAppResources;
     private SuspendDialogInfo mSuppliedDialogInfo;
     private Bundle mOptions;
-    private BroadcastReceiver mUnsuspendReceiver = new BroadcastReceiver() {
+    private BroadcastReceiver mSuspendModifiedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(intent.getAction())) {
-                final String[] unsuspended = intent.getStringArrayExtra(
+            if (Intent.ACTION_PACKAGES_SUSPENSION_CHANGED.equals(intent.getAction())) {
+                // Suspension conditions were modified, dismiss any related visible dialogs.
+                final String[] modified = intent.getStringArrayExtra(
                         Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                if (ArrayUtils.contains(unsuspended, mSuspendedPackage)) {
+                if (ArrayUtils.contains(modified, mSuspendedPackage)) {
                     if (!isFinishing()) {
-                        Slog.w(TAG, "Package " + mSuspendedPackage
-                                + " got unsuspended while the dialog was visible. Finishing.");
+                        Slog.w(TAG, "Package " + mSuspendedPackage + " has modified"
+                                + " suspension conditions while dialog was visible. Finishing.");
                         SuspendedAppActivity.this.finish();
+                        // TODO (b/198201994): reload the suspend dialog to show most relevant info
                     }
                 }
             }
@@ -245,15 +247,16 @@
 
         setupAlert();
 
-        final IntentFilter unsuspendFilter = new IntentFilter(Intent.ACTION_PACKAGES_UNSUSPENDED);
-        registerReceiverAsUser(mUnsuspendReceiver, UserHandle.of(mUserId), unsuspendFilter, null,
-                null);
+        final IntentFilter suspendModifiedFilter =
+                new IntentFilter(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED);
+        registerReceiverAsUser(mSuspendModifiedReceiver, UserHandle.of(mUserId),
+                suspendModifiedFilter, null, null);
     }
 
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        unregisterReceiver(mUnsuspendReceiver);
+        unregisterReceiver(mSuspendModifiedReceiver);
     }
 
     private void requestDismissKeyguardIfNeeded(CharSequence dismissMessage) {
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 2d0b3f9..74211fb 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -43,6 +43,7 @@
 import com.android.internal.view.IInputContext;
 
 import java.lang.ref.WeakReference;
+import java.util.function.Supplier;
 
 /**
  * Takes care of remote method invocations of {@link InputConnection} in the IME client side.
@@ -222,9 +223,7 @@
     @Override
     public void getTextAfterCursor(int length, int flags,
             AndroidFuture future /* T=CharSequence */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<CharSequence> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor");
             try {
                 final InputConnection ic = getInputConnection();
@@ -241,7 +240,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -251,9 +250,7 @@
     @Override
     public void getTextBeforeCursor(int length, int flags,
             AndroidFuture future /* T=CharSequence */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<CharSequence> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor");
             try {
                 final InputConnection ic = getInputConnection();
@@ -270,7 +267,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -279,9 +276,7 @@
 
     @Override
     public void getSelectedText(int flags, AndroidFuture future /* T=CharSequence */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<CharSequence> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText");
             try {
                 final InputConnection ic = getInputConnection();
@@ -298,7 +293,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getSelectedText", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -308,9 +303,7 @@
     @Override
     public void getSurroundingText(int beforeLength, int afterLength, int flags,
             AndroidFuture future /* T=SurroundingText */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<SurroundingText> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText");
             try {
                 final InputConnection ic = getInputConnection();
@@ -327,7 +320,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -336,9 +329,7 @@
 
     @Override
     public void getCursorCapsMode(int reqModes, AndroidFuture future /* T=Integer */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<Integer> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode");
             try {
                 final InputConnection ic = getInputConnection();
@@ -355,7 +346,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -365,9 +356,7 @@
     @Override
     public void getExtractedText(ExtractedTextRequest request, int flags,
             AndroidFuture future /* T=ExtractedText */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<ExtractedText> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText");
             try {
                 final InputConnection ic = getInputConnection();
@@ -384,7 +373,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getExtractedText", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -695,20 +684,15 @@
 
     @Override
     public void requestCursorUpdates(int cursorUpdateMode, AndroidFuture future /* T=Boolean */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<Boolean> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates");
             try {
                 final InputConnection ic = getInputConnection();
-                final boolean result;
                 if (ic == null || !isActive()) {
                     Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
-                    result = false;
-                } else {
-                    result = ic.requestCursorUpdates(cursorUpdateMode);
+                    return false;
                 }
-                typedFuture.complete(result);
+                return ic.requestCursorUpdates(cursorUpdateMode);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -718,26 +702,19 @@
     @Override
     public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
             AndroidFuture future /* T=Boolean */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<Boolean> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent");
             try {
                 final InputConnection ic = getInputConnection();
-                final boolean result;
                 if (ic == null || !isActive()) {
                     Log.w(TAG, "commitContent on inactive InputConnection");
-                    result = false;
-                } else {
-                    if (inputContentInfo == null || !inputContentInfo.validate()) {
-                        Log.w(TAG, "commitContent with invalid inputContentInfo="
-                                + inputContentInfo);
-                        result = false;
-                    } else {
-                        result = ic.commitContent(inputContentInfo, flags, opts);
-                    }
+                    return false;
                 }
-                typedFuture.complete(result);
+                if (inputContentInfo == null || !inputContentInfo.validate()) {
+                    Log.w(TAG, "commitContent with invalid inputContentInfo=" + inputContentInfo);
+                    return false;
+                }
+                return ic.commitContent(inputContentInfo, flags, opts);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -771,4 +748,10 @@
 
         mH.post(runnable);
     }
+
+    private <T> void dispatch(@NonNull AndroidFuture untypedFuture, @NonNull Supplier<T> supplier) {
+        @SuppressWarnings("unchecked")
+        final AndroidFuture<T> future = untypedFuture;
+        dispatch(() -> future.complete(supplier.get()));
+    }
 }
diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java
index 5212265..2f4a14f 100644
--- a/core/java/com/android/internal/logging/UiEventLogger.java
+++ b/core/java/com/android/internal/logging/UiEventLogger.java
@@ -32,6 +32,22 @@
      * OEMs should use event IDs above 100000 and below 1000000 (1 million).
      */
     interface UiEventEnum {
+
+        /**
+         * Tag used to request new UI Event IDs via presubmit analysis.
+         *
+         * <p>Use RESERVE_NEW_UI_EVENT_ID as the constructor parameter for a new {@link EventEnum}
+         * to signal the presubmit analyzer to reserve a new ID for the event. The new ID will be
+         * returned as a Gerrit presubmit finding.  Do not submit {@code RESERVE_NEW_UI_EVENT_ID} as
+         * the constructor parameter for any event.
+         *
+         * <pre>
+         * &#064;UiEvent(doc = "Briefly describe the interaction when this event will be logged")
+         * UNIQUE_EVENT_NAME(RESERVE_NEW_UI_EVENT_ID);
+         * </pre>
+         */
+        int RESERVE_NEW_UI_EVENT_ID = Integer.MIN_VALUE; // Negative IDs are ignored by the logger.
+
         int getId();
     }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4043060..719dc53 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -661,6 +661,10 @@
      * Mapping isolated uids to the actual owning app uid.
      */
     final SparseIntArray mIsolatedUids = new SparseIntArray();
+    /**
+     * Internal reference count of isolated uids.
+     */
+    final SparseIntArray mIsolatedUidRefCounts = new SparseIntArray();
 
     /**
      * The statistics we have collected organized by uids.
@@ -3855,6 +3859,7 @@
     public void addIsolatedUidLocked(int isolatedUid, int appUid,
             long elapsedRealtimeMs, long uptimeMs) {
         mIsolatedUids.put(isolatedUid, appUid);
+        mIsolatedUidRefCounts.put(isolatedUid, 1);
         final Uid u = getUidStatsLocked(appUid, elapsedRealtimeMs, uptimeMs);
         u.addIsolatedUid(isolatedUid);
     }
@@ -3873,19 +3878,51 @@
     }
 
     /**
-     * This should only be called after the cpu times have been read.
+     * Isolated uid should only be removed after all wakelocks associated with the uid are stopped
+     * and the cpu time-in-state has been read one last time for the uid.
+     *
      * @see #scheduleRemoveIsolatedUidLocked(int, int)
+     *
+     * @return true if the isolated uid is actually removed.
      */
     @GuardedBy("this")
-    public void removeIsolatedUidLocked(int isolatedUid, long elapsedRealtimeMs, long uptimeMs) {
+    public boolean maybeRemoveIsolatedUidLocked(int isolatedUid, long elapsedRealtimeMs,
+            long uptimeMs) {
+        final int refCount = mIsolatedUidRefCounts.get(isolatedUid, 0) - 1;
+        if (refCount > 0) {
+            // Isolated uid is still being tracked
+            mIsolatedUidRefCounts.put(isolatedUid, refCount);
+            return false;
+        }
+
         final int idx = mIsolatedUids.indexOfKey(isolatedUid);
         if (idx >= 0) {
             final int ownerUid = mIsolatedUids.valueAt(idx);
             final Uid u = getUidStatsLocked(ownerUid, elapsedRealtimeMs, uptimeMs);
             u.removeIsolatedUid(isolatedUid);
             mIsolatedUids.removeAt(idx);
+            mIsolatedUidRefCounts.delete(isolatedUid);
+        } else {
+            Slog.w(TAG, "Attempted to remove untracked isolated uid (" + isolatedUid + ")");
         }
         mPendingRemovedUids.add(new UidToRemove(isolatedUid, elapsedRealtimeMs));
+
+        return true;
+    }
+
+    /**
+     * Increment the ref count for an isolated uid.
+     * call #maybeRemoveIsolatedUidLocked to decrement.
+     */
+    public void incrementIsolatedUidRefCount(int uid) {
+        final int refCount = mIsolatedUidRefCounts.get(uid, 0);
+        if (refCount <= 0) {
+            // Uid is not mapped or referenced
+            Slog.w(TAG,
+                    "Attempted to increment ref counted of untracked isolated uid (" + uid + ")");
+            return;
+        }
+        mIsolatedUidRefCounts.put(uid, refCount + 1);
     }
 
     public int mapUid(int uid) {
@@ -4245,7 +4282,7 @@
 
     public void noteStartWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
             int type, boolean unimportantForLogging, long elapsedRealtimeMs, long uptimeMs) {
-        uid = mapUid(uid);
+        final int mappedUid = mapUid(uid);
         if (type == WAKE_TYPE_PARTIAL) {
             // Only care about partial wake locks, since full wake locks
             // will be canceled when the user puts the screen to sleep.
@@ -4255,9 +4292,9 @@
             }
             if (mRecordAllHistory) {
                 if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName,
-                        uid, 0)) {
+                        mappedUid, 0)) {
                     addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
-                            HistoryItem.EVENT_WAKE_LOCK_START, historyName, uid);
+                            HistoryItem.EVENT_WAKE_LOCK_START, historyName, mappedUid);
                 }
             }
             if (mWakeLockNesting == 0) {
@@ -4266,7 +4303,7 @@
                         + Integer.toHexString(mHistoryCur.states));
                 mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
                 mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName;
-                mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
+                mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = mappedUid;
                 mWakeLockImportant = !unimportantForLogging;
                 addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             } else if (!mWakeLockImportant && !unimportantForLogging
@@ -4276,14 +4313,19 @@
                     mHistoryLastWritten.wakelockTag = null;
                     mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
                     mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName;
-                    mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
+                    mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = mappedUid;
                     addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
                 }
                 mWakeLockImportant = true;
             }
             mWakeLockNesting++;
         }
-        if (uid >= 0) {
+        if (mappedUid >= 0) {
+            if (mappedUid != uid) {
+                // Prevent the isolated uid mapping from being removed while the wakelock is
+                // being held.
+                incrementIsolatedUidRefCount(uid);
+            }
             if (mOnBatteryScreenOffTimeBase.isRunning()) {
                 // We only update the cpu time when a wake lock is acquired if the screen is off.
                 // If the screen is on, we don't distribute the power amongst partial wakelocks.
@@ -4293,7 +4335,7 @@
                 requestWakelockCpuUpdate();
             }
 
-            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+            getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs)
                     .noteStartWakeLocked(pid, name, type, elapsedRealtimeMs);
 
             if (wc != null) {
@@ -4301,8 +4343,8 @@
                         wc.getTags(), getPowerManagerWakeLockLevel(type), name,
                         FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
             } else {
-                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, uid,
-                        null, getPowerManagerWakeLockLevel(type), name,
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED,
+                        mappedUid, null, getPowerManagerWakeLockLevel(type), name,
                         FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
             }
         }
@@ -4316,7 +4358,7 @@
 
     public void noteStopWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
             int type, long elapsedRealtimeMs, long uptimeMs) {
-        uid = mapUid(uid);
+        final int mappedUid = mapUid(uid);
         if (type == WAKE_TYPE_PARTIAL) {
             mWakeLockNesting--;
             if (mRecordAllHistory) {
@@ -4324,9 +4366,9 @@
                     historyName = name;
                 }
                 if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName,
-                        uid, 0)) {
+                        mappedUid, 0)) {
                     addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
-                            HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, uid);
+                            HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, mappedUid);
                 }
             }
             if (mWakeLockNesting == 0) {
@@ -4338,7 +4380,7 @@
                 addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             }
         }
-        if (uid >= 0) {
+        if (mappedUid >= 0) {
             if (mOnBatteryScreenOffTimeBase.isRunning()) {
                 if (DEBUG_ENERGY_CPU) {
                     Slog.d(TAG, "Updating cpu time because of -wake_lock");
@@ -4346,17 +4388,22 @@
                 requestWakelockCpuUpdate();
             }
 
-            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+            getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs)
                     .noteStopWakeLocked(pid, name, type, elapsedRealtimeMs);
             if (wc != null) {
                 FrameworkStatsLog.write(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(),
                         wc.getTags(), getPowerManagerWakeLockLevel(type), name,
                         FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
             } else {
-                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, uid,
-                        null, getPowerManagerWakeLockLevel(type), name,
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED,
+                        mappedUid, null, getPowerManagerWakeLockLevel(type), name,
                         FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
             }
+
+            if (mappedUid != uid) {
+                // Decrement the ref count for the isolated uid and delete the mapping if uneeded.
+                maybeRemoveIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
+            }
         }
     }
 
@@ -16769,6 +16816,15 @@
         pw.print("UIDs removed since the later of device start or stats reset: ");
         pw.println(mNumUidsRemoved);
 
+        pw.println("Currently mapped isolated uids:");
+        final int numIsolatedUids = mIsolatedUids.size();
+        for (int i = 0; i < numIsolatedUids; i++) {
+            final int isolatedUid = mIsolatedUids.keyAt(i);
+            final int ownerUid = mIsolatedUids.valueAt(i);
+            final int refCount = mIsolatedUidRefCounts.get(isolatedUid);
+            pw.println("  " + isolatedUid + "->" + ownerUid + " (ref count = " + refCount + ")");
+        }
+
         pw.println();
         dumpConstantsLocked(pw);
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 84a7f2f..b427e8b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -149,7 +149,7 @@
     */
     void showAuthenticationDialog(in PromptInfo promptInfo, IBiometricSysuiReceiver sysuiReceiver,
             in int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, int userId,
-            String opPackageName, long operationId, int multiSensorConfig);
+            long operationId, String opPackageName, long requestId, int multiSensorConfig);
     /**
     * Used to notify the authentication dialog that a biometric has been authenticated.
     */
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index e7d6d6c..b3499db 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -110,7 +110,8 @@
     // Used to show the authentication dialog (Biometrics, Device Credential)
     void showAuthenticationDialog(in PromptInfo promptInfo, IBiometricSysuiReceiver sysuiReceiver,
             in int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
-            int userId, String opPackageName, long operationId, int multiSensorConfig);
+            int userId, long operationId, String opPackageName, long requestId,
+            int multiSensorConfig);
 
     // Used to notify the authentication dialog that a biometric has been authenticated
     void onBiometricAuthenticated();
diff --git a/core/jni/android_media_audio_common_AidlConversion.cpp b/core/jni/android_media_audio_common_AidlConversion.cpp
index 6dcaf76..2ef817c 100644
--- a/core/jni/android_media_audio_common_AidlConversion.cpp
+++ b/core/jni/android_media_audio_common_AidlConversion.cpp
@@ -16,6 +16,9 @@
 
 #define LOG_TAG "AidlConversion"
 
+#include <sstream>
+#include <type_traits>
+
 #include <android_os_Parcel.h>
 #include <binder/Parcel.h>
 #include <jni.h>
@@ -28,13 +31,64 @@
 namespace {
 
 using namespace android;
+using media::audio::common::AudioChannelLayout;
+using media::audio::common::AudioEncapsulationMode;
+using media::audio::common::AudioFormatDescription;
+using media::audio::common::AudioStreamType;
+using media::audio::common::AudioUsage;
 
 #define PACKAGE "android/media/audio/common"
 #define CLASSNAME PACKAGE "/AidlConversion"
 
+// Used for creating messages.
+template <typename T>
+struct type_info {
+    static constexpr const char* name = "";
+};
+#define TYPE_NAME_QUOTE(x) #x
+#define TYPE_NAME_STRINGIFY(x) TYPE_NAME_QUOTE(x)
+#define TYPE_NAME(n)                                                \
+    template <>                                                     \
+    struct type_info<n> {                                           \
+        static constexpr const char* name = TYPE_NAME_STRINGIFY(n); \
+    }
+
+TYPE_NAME(AudioChannelLayout);
+TYPE_NAME(AudioEncapsulationMode);
+TYPE_NAME(AudioFormatDescription);
+TYPE_NAME(AudioStreamType);
+TYPE_NAME(AudioUsage);
+TYPE_NAME(audio_encapsulation_mode_t);
+TYPE_NAME(audio_stream_type_t);
+TYPE_NAME(audio_usage_t);
+
+template <typename AidlType, typename LegacyType, typename ConvFunc>
+int aidl2legacy(JNIEnv* env, AidlType aidl, const ConvFunc& conv, LegacyType fallbackValue) {
+    const auto result = conv(aidl);
+    if (result.ok()) {
+        return result.value();
+    }
+    std::ostringstream msg;
+    msg << "Failed to convert " << type_info<AidlType>::name << " value "
+        << static_cast<std::underlying_type_t<AidlType>>(aidl);
+    jniThrowException(env, "java/lang/IllegalArgumentException", msg.str().c_str());
+    return fallbackValue;
+}
+
+template <typename LegacyType, typename AidlType, typename ConvFunc>
+int legacy2aidl(JNIEnv* env, LegacyType legacy, const ConvFunc& conv, AidlType fallbackValue) {
+    const auto result = conv(legacy);
+    if (result.ok()) {
+        return static_cast<std::underlying_type_t<AidlType>>(result.value());
+    }
+    std::ostringstream msg;
+    msg << "Failed to convert legacy " << type_info<LegacyType>::name << " value " << legacy;
+    jniThrowException(env, "java/lang/IllegalArgumentException", msg.str().c_str());
+    return static_cast<std::underlying_type_t<AidlType>>(fallbackValue);
+}
+
 template <typename AidlType, typename ConvFunc>
-int aidl2legacy(JNIEnv* env, jobject clazz, jobject jParcel, const ConvFunc& conv,
-                int fallbackValue) {
+int aidlParcel2legacy(JNIEnv* env, jobject jParcel, const ConvFunc& conv, int fallbackValue) {
     if (Parcel* parcel = parcelForJavaObject(env, jParcel); parcel != nullptr) {
         AidlType aidl{};
         if (status_t status = aidl.readFromParcel(parcel); status == OK) {
@@ -43,8 +97,11 @@
                 return legacy.value();
             }
         } else {
-            ALOGE("aidl2legacy: Failed to read from parcel: %d", status);
+            ALOGE("aidl2legacy: Failed to read from parcel: %s", statusToString(status).c_str());
         }
+        std::ostringstream msg;
+        msg << "Failed to convert " << type_info<AidlType>::name << " value " << aidl.toString();
+        jniThrowException(env, "java/lang/IllegalArgumentException", msg.str().c_str());
     } else {
         ALOGE("aidl2legacy: Failed to retrieve the native parcel from Java parcel");
     }
@@ -52,9 +109,12 @@
 }
 
 template <typename LegacyType, typename ConvFunc>
-jobject legacy2aidl(JNIEnv* env, jobject clazz, LegacyType legacy, const ConvFunc& conv) {
+jobject legacy2aidlParcel(JNIEnv* env, LegacyType legacy, const ConvFunc& conv) {
     auto aidl = conv(legacy);
     if (!aidl.ok()) {
+        std::ostringstream msg;
+        msg << "Failed to convert legacy " << type_info<LegacyType>::name << " value " << legacy;
+        jniThrowException(env, "java/lang/IllegalArgumentException", msg.str().c_str());
         return 0;
     }
     if (jobject jParcel = createJavaParcelObject(env); jParcel != 0) {
@@ -62,6 +122,9 @@
             if (status_t status = aidl.value().writeToParcel(parcel); status == OK) {
                 parcel->setDataPosition(0);
                 return jParcel;
+            } else {
+                ALOGE("legacy2aidl: Failed to write to parcel: %s, aidl value: %s",
+                      statusToString(status).c_str(), aidl.value().toString().c_str());
             }
         } else {
             ALOGE("legacy2aidl: Failed to retrieve the native parcel from Java parcel");
@@ -73,38 +136,71 @@
     return 0;
 }
 
-int aidl2legacy_AudioChannelLayout_Parcel_audio_channel_mask_t(JNIEnv* env, jobject clazz,
+int aidl2legacy_AudioChannelLayout_Parcel_audio_channel_mask_t(JNIEnv* env, jobject,
                                                                jobject jParcel, jboolean isInput) {
-    return aidl2legacy<media::audio::common::AudioChannelLayout>(
-            env, clazz, jParcel,
-            [isInput](const media::audio::common::AudioChannelLayout& l) {
+    return aidlParcel2legacy<AudioChannelLayout>(
+            env, jParcel,
+            [isInput](const AudioChannelLayout& l) {
                 return aidl2legacy_AudioChannelLayout_audio_channel_mask_t(l, isInput);
             },
             AUDIO_CHANNEL_INVALID);
 }
 
 jobject legacy2aidl_audio_channel_mask_t_AudioChannelLayout_Parcel(
-        JNIEnv* env, jobject clazz, int /*audio_channel_mask_t*/ legacy, jboolean isInput) {
-    return legacy2aidl<audio_channel_mask_t>(
-            env, clazz, static_cast<audio_channel_mask_t>(legacy),
-            [isInput](audio_channel_mask_t m) {
+        JNIEnv* env, jobject, int /*audio_channel_mask_t*/ legacy, jboolean isInput) {
+    return legacy2aidlParcel(
+            env, static_cast<audio_channel_mask_t>(legacy), [isInput](audio_channel_mask_t m) {
                 return legacy2aidl_audio_channel_mask_t_AudioChannelLayout(m, isInput);
             });
 }
 
-int aidl2legacy_AudioFormatDescription_Parcel_audio_format_t(JNIEnv* env, jobject clazz,
+int aidl2legacy_AudioFormatDescription_Parcel_audio_format_t(JNIEnv* env, jobject,
                                                              jobject jParcel) {
-    return aidl2legacy<
-            media::audio::common::
-                    AudioFormatDescription>(env, clazz, jParcel,
-                                            aidl2legacy_AudioFormatDescription_audio_format_t,
-                                            AUDIO_FORMAT_INVALID);
+    return aidlParcel2legacy<
+            AudioFormatDescription>(env, jParcel, aidl2legacy_AudioFormatDescription_audio_format_t,
+                                    AUDIO_FORMAT_INVALID);
 }
 
-jobject legacy2aidl_audio_format_t_AudioFormatDescription_Parcel(JNIEnv* env, jobject clazz,
+jobject legacy2aidl_audio_format_t_AudioFormatDescription_Parcel(JNIEnv* env, jobject,
                                                                  int /*audio_format_t*/ legacy) {
-    return legacy2aidl<audio_format_t>(env, clazz, static_cast<audio_format_t>(legacy),
-                                       legacy2aidl_audio_format_t_AudioFormatDescription);
+    return legacy2aidlParcel(env, static_cast<audio_format_t>(legacy),
+                             legacy2aidl_audio_format_t_AudioFormatDescription);
+}
+
+jint aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(JNIEnv* env, jobject,
+                                                                   jint aidl) {
+    return aidl2legacy(env, AudioEncapsulationMode(static_cast<int32_t>(aidl)),
+                       android::aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t,
+                       AUDIO_ENCAPSULATION_MODE_NONE);
+}
+
+jint legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(JNIEnv* env, jobject,
+                                                                   jint legacy) {
+    return legacy2aidl(env, static_cast<audio_encapsulation_mode_t>(legacy),
+                       android::legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode,
+                       AudioEncapsulationMode::INVALID);
+}
+
+jint aidl2legacy_AudioStreamType_audio_stream_type_t(JNIEnv* env, jobject, jint aidl) {
+    return aidl2legacy(env, AudioStreamType(static_cast<int32_t>(aidl)),
+                       android::aidl2legacy_AudioStreamType_audio_stream_type_t,
+                       AUDIO_STREAM_DEFAULT);
+}
+
+jint legacy2aidl_audio_stream_type_t_AudioStreamType(JNIEnv* env, jobject, jint legacy) {
+    return legacy2aidl(env, static_cast<audio_stream_type_t>(legacy),
+                       android::legacy2aidl_audio_stream_type_t_AudioStreamType,
+                       AudioStreamType::INVALID);
+}
+
+jint aidl2legacy_AudioUsage_audio_usage_t(JNIEnv* env, jobject, jint aidl) {
+    return aidl2legacy(env, AudioUsage(static_cast<int32_t>(aidl)),
+                       android::aidl2legacy_AudioUsage_audio_usage_t, AUDIO_USAGE_UNKNOWN);
+}
+
+jint legacy2aidl_audio_usage_t_AudioUsage(JNIEnv* env, jobject, jint legacy) {
+    return legacy2aidl(env, static_cast<audio_usage_t>(legacy),
+                       android::legacy2aidl_audio_usage_t_AudioUsage, AudioUsage::INVALID);
 }
 
 const JNINativeMethod gMethods[] = {
@@ -116,6 +212,18 @@
          reinterpret_cast<void*>(aidl2legacy_AudioFormatDescription_Parcel_audio_format_t)},
         {"legacy2aidl_audio_format_t_AudioFormatDescription_Parcel", "(I)Landroid/os/Parcel;",
          reinterpret_cast<void*>(legacy2aidl_audio_format_t_AudioFormatDescription_Parcel)},
+        {"aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t", "(I)I",
+         reinterpret_cast<void*>(aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t)},
+        {"legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode", "(I)I",
+         reinterpret_cast<void*>(legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode)},
+        {"aidl2legacy_AudioStreamType_audio_stream_type_t", "(I)I",
+         reinterpret_cast<void*>(aidl2legacy_AudioStreamType_audio_stream_type_t)},
+        {"legacy2aidl_audio_stream_type_t_AudioStreamType", "(I)I",
+         reinterpret_cast<void*>(legacy2aidl_audio_stream_type_t_AudioStreamType)},
+        {"aidl2legacy_AudioUsage_audio_usage_t", "(I)I",
+         reinterpret_cast<void*>(aidl2legacy_AudioUsage_audio_usage_t)},
+        {"legacy2aidl_audio_usage_t_AudioUsage", "(I)I",
+         reinterpret_cast<void*>(legacy2aidl_audio_usage_t_AudioUsage)},
 };
 
 } // namespace
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7fe4b65..8813c43 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -660,6 +660,9 @@
     <!-- Indicates whether to enable an animation when unfolding a device or not -->
     <bool name="config_unfoldTransitionEnabled">false</bool>
 
+    <!-- Indicates whether to enable hinge angle sensor when using unfold animation -->
+    <bool name="config_unfoldTransitionHingeAngle">false</bool>
+
     <!-- Indicates that the device supports having more than one internal display on at the same
          time. Only applicable to devices with more than one internal display. If this option is
          set to false, DisplayManager will make additional effort to ensure no more than 1 internal
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d2c6f78..4c1cc87 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1650,6 +1650,8 @@
     <!-- Array containing custom error messages from vendor.  Vendor is expected to add and translate these strings -->
     <string-array name="fingerprint_error_vendor">
     </string-array>
+    <!-- Default error message to use when fingerprint_error_vendor does not contain a message. [CHAR LIMIT=NONE] -->
+    <string name="fingerprint_error_vendor_unknown">Something went wrong. Try again.</string>
 
     <!-- Content description which should be used for the fingerprint icon. -->
     <string name="fingerprint_icon_content_description">Fingerprint icon</string>
@@ -1760,6 +1762,8 @@
     <!-- Array containing custom error messages from vendor.  Vendor is expected to add and translate these strings -->
     <string-array name="face_error_vendor">
     </string-array>
+    <!-- Default error message to use when face_error_vendor does not contain a message. [CHAR LIMIT=NONE] -->
+    <string name="face_error_vendor_unknown">Something went wrong. Try again.</string>
 
     <!-- Content description which should be used for the face icon. [CHAR LIMIT=10] -->
     <string name="face_icon_content_description">Face icon</string>
@@ -4557,7 +4561,7 @@
     <string name="accessibility_shortcut_multiple_service_warning">Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="service" example="TalkBack">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility.</string>
 
     <!-- Used in multiple service warning to list current features. [CHAR LIMIT=none] -->
-    <string name="accessibility_shortcut_multiple_service_list">\t• <xliff:g id="service" example="TalkBack">%1$s</xliff:g>\n</string>
+    <string name="accessibility_shortcut_multiple_service_list">\u0020• <xliff:g id="service" example="TalkBack">%1$s</xliff:g>\n</string>
 
     <!-- Dialog title for dialog shown when this accessibility shortcut is activated, and we want to confirm that the user understands what's going to happen. [CHAR LIMIT=none] -->
     <string name="accessibility_shortcut_single_service_warning_title">Turn on <xliff:g id="service" example="TalkBack">%1$s</xliff:g> shortcut?</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 527b928..6a1152b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2528,6 +2528,7 @@
   <java-symbol type="string" name="fingerprint_error_no_space" />
   <java-symbol type="string" name="fingerprint_error_timeout" />
   <java-symbol type="array" name="fingerprint_error_vendor" />
+  <java-symbol type="string" name="fingerprint_error_vendor_unknown" />
   <java-symbol type="string" name="fingerprint_acquired_partial" />
   <java-symbol type="string" name="fingerprint_acquired_insufficient" />
   <java-symbol type="string" name="fingerprint_acquired_imager_dirty" />
@@ -2567,6 +2568,7 @@
   <java-symbol type="string" name="face_error_no_space" />
   <java-symbol type="string" name="face_error_timeout" />
   <java-symbol type="array" name="face_error_vendor" />
+  <java-symbol type="string" name="face_error_vendor_unknown" />
   <java-symbol type="string" name="face_error_canceled" />
   <java-symbol type="string" name="face_error_user_canceled" />
   <java-symbol type="string" name="face_error_lockout" />
@@ -3844,6 +3846,7 @@
   <java-symbol type="string" name="config_foldedArea" />
   <java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" />
   <java-symbol type="bool" name="config_unfoldTransitionEnabled" />
+  <java-symbol type="bool" name="config_unfoldTransitionHingeAngle" />
   <java-symbol type="array" name="config_perDeviceStateRotationLockDefaults" />
 
 
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 37d059a..69d2c74 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -274,6 +274,6 @@
     <shortcode country="yt" pattern="\\d{1,5}" free="38600,36300,36303,959" />
 
     <!-- South Africa -->
-    <shortcode country="za" pattern="\d{1,5}" free="44136|30791|36056" />
+    <shortcode country="za" pattern="\\d{1,5}" free="44136|30791|36056" />
 
 </shortcodes>
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
index 3d820ac..6884f13d 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
@@ -62,18 +62,4 @@
         assertThat(outDoc.getPropertyDocument("propDocument").getPropertyBytesArray("propBytes"))
                 .isEqualTo(new byte[][] {{3, 4}});
     }
-
-    @Test
-    public void testPutLargeDocument_exceedLimit() throws Exception {
-        // Create a String property that has a very large property.
-        char[] chars = new char[10_000_000];
-        String property = new StringBuilder().append(chars).append("the end.").toString();
-
-        GenericDocument doc =
-                new GenericDocument.Builder<>("namespace", "id1", "schema1")
-                        .setPropertyString("propString", property)
-                        .build();
-
-        assertThat(doc.getPropertyString("propString")).isEqualTo(property);
-    }
 }
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index fd7753b..5b3be58 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -22,6 +22,7 @@
 
 import static org.testng.Assert.assertThrows;
 
+import android.app.ActivityThread;
 import android.content.ContentResolver;
 import android.os.Bundle;
 import android.platform.test.annotations.Presubmit;
@@ -35,6 +36,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /** Tests that ensure appropriate settings are backed up. */
 @Presubmit
 @RunWith(AndroidJUnit4.class)
@@ -701,66 +708,67 @@
         assertThat(result.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
     }
 
-    // TODO(mpape): resolve b/142727848 and re-enable listener tests
-//    @Test
-//    public void onPropertiesChangedListener_setPropertyCallback() throws InterruptedException {
-//        final CountDownLatch countDownLatch = new CountDownLatch(1);
-//
-//        DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
-//            assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
-//            assertThat(properties.getKeyset()).contains(KEY);
-//            assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE);
-//            countDownLatch.countDown();
-//        };
-//
-//        try {
-//            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE,
-//                    ActivityThread.currentApplication().getMainExecutor(), changeListener);
-//            DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
-//            assertThat(countDownLatch.await(
-//                    WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
-//        } catch (InterruptedException e) {
-//            Assert.fail(e.getMessage());
-//        } finally {
-//            DeviceConfig.removeOnPropertiesChangedListener(changeListener);
-//        }
-//    }
-//
-//    @Test
-//    public void onPropertiesChangedListener_setPropertiesCallback() throws InterruptedException {
-//        final CountDownLatch countDownLatch = new CountDownLatch(1);
-//        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
-//        DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false);
-//
-//        Map<String, String> keyValues = new HashMap<>(2);
-//        keyValues.put(KEY, VALUE2);
-//        keyValues.put(KEY3, VALUE3);
-//        Properties setProperties = new Properties(NAMESPACE, keyValues);
-//
-//        DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
-//            assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
-//            assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3);
-//            // KEY updated from VALUE to VALUE2
-//            assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE2);
-//            // KEY2 deleted (returns default_value)
-//            assertThat(properties.getString(KEY2, "default_value")).isEqualTo("default_value");
-//            //KEY3 added with VALUE3
-//            assertThat(properties.getString(KEY3, "default_value")).isEqualTo(VALUE3);
-//            countDownLatch.countDown();
-//        };
-//
-//        try {
-//            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE,
-//                    ActivityThread.currentApplication().getMainExecutor(), changeListener);
-//            DeviceConfig.setProperties(setProperties);
-//            assertThat(countDownLatch.await(
-//                    WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
-//        } catch (InterruptedException e) {
-//            Assert.fail(e.getMessage());
-//        } finally {
-//            DeviceConfig.removeOnPropertiesChangedListener(changeListener);
-//        }
-//    }
+    @Test
+    public void onPropertiesChangedListener_setPropertyCallback() throws InterruptedException {
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+
+        DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
+            assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
+            assertThat(properties.getKeyset()).contains(KEY);
+            assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE);
+            countDownLatch.countDown();
+        };
+
+        try {
+            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE,
+                    Objects.requireNonNull(ActivityThread.currentApplication()).getMainExecutor(),
+                    changeListener);
+            DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+            assertThat(countDownLatch.await(
+                    WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+        } catch (InterruptedException e) {
+            Assert.fail(e.getMessage());
+        } finally {
+            DeviceConfig.removeOnPropertiesChangedListener(changeListener);
+        }
+    }
+
+    @Test
+    public void onPropertiesChangedListener_setPropertiesCallback() {
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+        DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false);
+
+        Map<String, String> keyValues = new HashMap<>(2);
+        keyValues.put(KEY, VALUE2);
+        keyValues.put(KEY3, VALUE3);
+        Properties setProperties = new Properties(NAMESPACE, keyValues);
+
+        DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> {
+            assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
+            assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3);
+            // KEY updated from VALUE to VALUE2
+            assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE2);
+            // KEY2 deleted (returns default_value)
+            assertThat(properties.getString(KEY2, "default_value")).isEqualTo("default_value");
+            //KEY3 added with VALUE3
+            assertThat(properties.getString(KEY3, "default_value")).isEqualTo(VALUE3);
+            countDownLatch.countDown();
+        };
+
+        try {
+            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE,
+                    Objects.requireNonNull(ActivityThread.currentApplication()).getMainExecutor(),
+                    changeListener);
+            DeviceConfig.setProperties(setProperties);
+            assertThat(countDownLatch.await(
+                    WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+        } catch (InterruptedException | DeviceConfig.BadConfigException e) {
+            Assert.fail(e.getMessage());
+        } finally {
+            DeviceConfig.removeOnPropertiesChangedListener(changeListener);
+        }
+    }
 
     @Test
     public void syncDisabling() throws Exception {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index a43d32d..e8e4330 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -23,6 +23,8 @@
 import android.os.BatteryStats;
 import android.os.BatteryStats.HistoryItem;
 import android.os.BatteryStats.Uid.Sensor;
+import android.os.Process;
+import android.os.UserHandle;
 import android.os.WorkSource;
 import android.util.SparseLongArray;
 import android.view.Display;
@@ -53,6 +55,8 @@
 public class BatteryStatsNoteTest extends TestCase {
 
     private static final int UID = 10500;
+    private static final int ISOLATED_APP_ID = Process.FIRST_ISOLATED_UID + 23;
+    private static final int ISOLATED_UID = UserHandle.getUid(0, ISOLATED_APP_ID);
     private static final WorkSource WS = new WorkSource(UID);
 
     /**
@@ -114,6 +118,88 @@
         assertEquals(120_000, bgTime);
     }
 
+    /**
+     * Test BatteryStatsImpl.Uid.noteStartWakeLocked for an isolated uid.
+     */
+    @SmallTest
+    public void testNoteStartWakeLocked_isolatedUid() throws Exception {
+        final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+        int pid = 10;
+        String name = "name";
+        String historyName = "historyName";
+
+        WorkSource.WorkChain isolatedWorkChain = new WorkSource.WorkChain();
+        isolatedWorkChain.addNode(ISOLATED_UID, name);
+
+        // Map ISOLATED_UID to UID.
+        bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+
+        bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
+        bi.noteStartWakeLocked(ISOLATED_UID, pid, isolatedWorkChain, name, historyName,
+                WAKE_TYPE_PARTIAL, false);
+
+        clocks.realtime = clocks.uptime = 100;
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
+        clocks.realtime = clocks.uptime = 220;
+        bi.noteStopWakeLocked(ISOLATED_UID, pid, isolatedWorkChain, name, historyName,
+                WAKE_TYPE_PARTIAL);
+
+        // ISOLATED_UID wakelock time should be attributed to UID.
+        BatteryStats.Timer aggregTimer = bi.getUidStats().get(UID)
+                .getAggregatedPartialWakelockTimer();
+        long actualTime = aggregTimer.getTotalTimeLocked(300_000, STATS_SINCE_CHARGED);
+        long bgTime = aggregTimer.getSubTimer().getTotalTimeLocked(300_000, STATS_SINCE_CHARGED);
+        assertEquals(220_000, actualTime);
+        assertEquals(120_000, bgTime);
+    }
+
+    /**
+     * Test BatteryStatsImpl.Uid.noteStartWakeLocked for an isolated uid, with a race where the
+     * isolated uid is removed from batterystats before the wakelock has been stopped.
+     */
+    @SmallTest
+    public void testNoteStartWakeLocked_isolatedUidRace() throws Exception {
+        final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+        int pid = 10;
+        String name = "name";
+        String historyName = "historyName";
+
+        WorkSource.WorkChain isolatedWorkChain = new WorkSource.WorkChain();
+        isolatedWorkChain.addNode(ISOLATED_UID, name);
+
+        // Map ISOLATED_UID to UID.
+        bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+
+        bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
+        bi.noteStartWakeLocked(ISOLATED_UID, pid, isolatedWorkChain, name, historyName,
+                WAKE_TYPE_PARTIAL, false);
+
+        clocks.realtime = clocks.uptime = 100;
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
+        clocks.realtime = clocks.uptime = 150;
+        bi.maybeRemoveIsolatedUidLocked(ISOLATED_UID, clocks.realtime, clocks.uptime);
+
+        clocks.realtime = clocks.uptime = 220;
+        bi.noteStopWakeLocked(ISOLATED_UID, pid, isolatedWorkChain, name, historyName,
+                WAKE_TYPE_PARTIAL);
+
+        // ISOLATED_UID wakelock time should be attributed to UID.
+        BatteryStats.Timer aggregTimer = bi.getUidStats().get(UID)
+                .getAggregatedPartialWakelockTimer();
+        long actualTime = aggregTimer.getTotalTimeLocked(300_000, STATS_SINCE_CHARGED);
+        long bgTime = aggregTimer.getSubTimer().getTotalTimeLocked(300_000, STATS_SINCE_CHARGED);
+        assertEquals(220_000, actualTime);
+        assertEquals(120_000, bgTime);
+    }
+
 
     /**
      * Test BatteryStatsImpl.noteUidProcessStateLocked.
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 81d72db..6a93761 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1261,6 +1261,12 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
+    "-702650156": {
+      "message": "Override with TaskFragment remote animation for transit=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/AppTransitionController.java"
+    },
     "-701167286": {
       "message": "applyAnimation: transit=%s, enter=%b, wc=%s",
       "level": "VERBOSE",
@@ -1789,6 +1795,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/TaskFragment.java"
     },
+    "-70719599": {
+      "message": "Unregister remote animations for organizer=%s uid=%d pid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+    },
     "-55185509": {
       "message": "setFocusedTask: taskId=%d touchedActivity=%s",
       "level": "DEBUG",
@@ -2941,6 +2953,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1210037962": {
+      "message": "Register remote animations for organizer=%s uid=%d pid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+    },
     "1219600119": {
       "message": "addWindow: win=%s Callers=%s",
       "level": "DEBUG",
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
index 4206d03..9212a0f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
@@ -62,6 +62,7 @@
     final Map<IBinder, Configuration> mFragmentParentConfigs = new ArrayMap<>();
 
     private final TaskFragmentCallback mCallback;
+    private TaskFragmentAnimationController mAnimationController;
 
     /**
      * Callback that notifies the controller about changes to task fragments.
@@ -83,6 +84,25 @@
         mCallback = callback;
     }
 
+    @Override
+    public void registerOrganizer() {
+        if (mAnimationController != null) {
+            throw new IllegalStateException("Must unregister the organizer before re-register.");
+        }
+        super.registerOrganizer();
+        mAnimationController = new TaskFragmentAnimationController(this);
+        mAnimationController.registerRemoteAnimations();
+    }
+
+    @Override
+    public void unregisterOrganizer() {
+        if (mAnimationController != null) {
+            mAnimationController.unregisterRemoteAnimations();
+            mAnimationController = null;
+        }
+        super.unregisterOrganizer();
+    }
+
     /**
      * Starts a new Activity and puts it into split with an existing Activity side-by-side.
      * @param launchingFragmentToken    token for the launching TaskFragment. If it exists, it will
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
new file mode 100644
index 0000000..b85287d
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
+
+import android.util.Log;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.window.TaskFragmentOrganizer;
+
+/** Controls the TaskFragment remote animations. */
+class TaskFragmentAnimationController {
+
+    private static final String TAG = "TaskFragAnimationCtrl";
+    // TODO(b/196173550) turn off when finalize
+    static final boolean DEBUG = false;
+
+    private final TaskFragmentOrganizer mOrganizer;
+    private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner();
+
+    TaskFragmentAnimationController(TaskFragmentOrganizer organizer) {
+        mOrganizer = organizer;
+    }
+
+    void registerRemoteAnimations() {
+        if (DEBUG) {
+            Log.v(TAG, "registerRemoteAnimations");
+        }
+        final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+        final RemoteAnimationAdapter animationAdapter =
+                new RemoteAnimationAdapter(mRemoteRunner, 0, 0);
+        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter);
+        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
+        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
+        mOrganizer.registerRemoteAnimations(definition);
+    }
+
+    void unregisterRemoteAnimations() {
+        if (DEBUG) {
+            Log.v(TAG, "unregisterRemoteAnimations");
+        }
+        mOrganizer.unregisterRemoteAnimations();
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
new file mode 100644
index 0000000..9ee60d8
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+
+import androidx.annotation.Nullable;
+
+/** To run the TaskFragment animations. */
+class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
+
+    private static final String TAG = "TaskFragAnimationRunner";
+    private final Handler mHandler = new Handler(Looper.myLooper());
+
+    @Nullable
+    private IRemoteAnimationFinishedCallback mFinishedCallback;
+
+    @Override
+    public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+            RemoteAnimationTarget[] apps,
+            RemoteAnimationTarget[] wallpapers,
+            RemoteAnimationTarget[] nonApps,
+            IRemoteAnimationFinishedCallback finishedCallback) {
+        if (wallpapers.length != 0 || nonApps.length != 0) {
+            throw new IllegalArgumentException("TaskFragment shouldn't handle animation with"
+                    + "wallpaper or non-app windows.");
+        }
+        if (TaskFragmentAnimationController.DEBUG) {
+            Log.v(TAG, "onAnimationStart transit=" + transit);
+        }
+        mHandler.post(() -> startAnimation(apps, finishedCallback));
+    }
+
+    @Override
+    public void onAnimationCancelled() {
+        if (TaskFragmentAnimationController.DEBUG) {
+            Log.v(TAG, "onAnimationCancelled");
+        }
+        mHandler.post(this::onAnimationFinished);
+    }
+
+    private void startAnimation(RemoteAnimationTarget[] targets,
+            IRemoteAnimationFinishedCallback finishedCallback) {
+        // TODO(b/196173550) replace with actual animations
+        mFinishedCallback = finishedCallback;
+        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        for (RemoteAnimationTarget target : targets) {
+            if (target.mode == MODE_OPENING) {
+                t.show(target.leash);
+                t.setAlpha(target.leash, 1);
+            }
+            t.setPosition(target.leash, target.localBounds.left, target.localBounds.top);
+        }
+        t.apply();
+        onAnimationFinished();
+    }
+
+    private void onAnimationFinished() {
+        if (mFinishedCallback == null) {
+            return;
+        }
+        try {
+            mFinishedCallback.onAnimationFinished();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        mFinishedCallback = null;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index a646b07..ae8c1b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -60,8 +60,7 @@
     private static final boolean DEBUG = false;
 
     public static final int MENU_STATE_NONE = 0;
-    public static final int MENU_STATE_CLOSE = 1;
-    public static final int MENU_STATE_FULL = 2;
+    public static final int MENU_STATE_FULL = 1;
 
     /**
      * A listener interface to receive notification on changes in PIP.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 67b1e6d..8ef2b6b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -23,7 +23,6 @@
 import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 
-import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE;
 import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL;
 import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE;
 
@@ -203,7 +202,7 @@
 
             @Override
             public boolean performAccessibilityAction(View host, int action, Bundle args) {
-                if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) {
+                if (action == ACTION_CLICK && mMenuState != MENU_STATE_FULL) {
                     mController.showMenu();
                 }
                 return super.performAccessibilityAction(host, action, args);
@@ -271,13 +270,12 @@
                     mDismissButton.getAlpha(), 1f);
             ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
                     mResizeHandle.getAlpha(),
-                    ENABLE_RESIZE_HANDLE && menuState == MENU_STATE_CLOSE && showResizeHandle
-                            ? 1f : 0f);
+                    ENABLE_RESIZE_HANDLE && showResizeHandle ? 1f : 0f);
             if (menuState == MENU_STATE_FULL) {
                 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
                         resizeAnim);
             } else {
-                mMenuContainerAnimator.playTogether(dismissAnim, resizeAnim);
+                mMenuContainerAnimator.playTogether(resizeAnim);
             }
             mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
             mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
@@ -429,7 +427,7 @@
 
         FrameLayout.LayoutParams expandedLp =
                 (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
-        if (mActions.isEmpty() || menuState == MENU_STATE_CLOSE || menuState == MENU_STATE_NONE) {
+        if (mActions.isEmpty() || menuState == MENU_STATE_NONE) {
             actionsContainer.setVisibility(View.INVISIBLE);
 
             // Update the expand container margin to adjust the center of the expand button to
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 7867f93..9f2f6a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -22,7 +22,6 @@
 import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
 import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
 import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
-import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE;
 import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL;
 import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE;
 import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
@@ -81,7 +80,6 @@
 
     private final PhonePipMenuController mMenuController;
     private final AccessibilityManager mAccessibilityManager;
-    private boolean mShowPipMenuOnAnimationEnd = false;
 
     /**
      * Whether PIP stash is enabled or not. When enabled, if the user flings toward the edge of the
@@ -280,7 +278,6 @@
     public void onActivityPinned() {
         mPipDismissTargetHandler.createOrUpdateDismissTarget();
 
-        mShowPipMenuOnAnimationEnd = true;
         mPipResizeGestureHandler.onActivityPinned();
         mFloatingContentCoordinator.onContentAdded(mMotionHelper);
     }
@@ -304,13 +301,6 @@
             // Set the initial bounds as the user resize bounds.
             mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
         }
-
-        if (mShowPipMenuOnAnimationEnd) {
-            mMenuController.showMenu(MENU_STATE_CLOSE, mPipBoundsState.getBounds(),
-                    true /* allowMenuTimeout */, false /* willResizeMenu */,
-                    shouldShowResizeHandle());
-            mShowPipMenuOnAnimationEnd = false;
-        }
     }
 
     public void onConfigurationChanged() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 1529f5b..7a17bb3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -129,7 +129,7 @@
     }
 
     /**
-     * Expands the pip window and dismisses it by clicking on the X button.
+     * Taps the pip window and dismisses it by clicking on the X button.
      */
     fun closePipWindow(wmHelper: WindowManagerStateHelper) {
         if (isTelevision) {
@@ -137,9 +137,12 @@
         } else {
             val windowRect = getWindowRect(wmHelper)
             uiDevice.click(windowRect.centerX(), windowRect.centerY())
-            val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss"))
+            // search and interact with the dismiss button
+            val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
+            uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
+            val dismissPipObject = uiDevice.findObject(dismissSelector)
                     ?: error("PIP window dismiss button not found")
-            val dismissButtonBounds = exitPipObject.visibleBounds
+            val dismissButtonBounds = dismissPipObject.visibleBounds
             uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
         }
 
diff --git a/media/Android.bp b/media/Android.bp
index 82d6160..ce62b03 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -54,6 +54,8 @@
     srcs: [
         "aidl/android/media/audio/common/AudioChannelLayout.aidl",
         "aidl/android/media/audio/common/AudioConfig.aidl",
+        "aidl/android/media/audio/common/AudioConfigBase.aidl",
+        "aidl/android/media/audio/common/AudioEncapsulationMode.aidl",
         "aidl/android/media/audio/common/AudioFormatDescription.aidl",
         "aidl/android/media/audio/common/AudioFormatType.aidl",
         "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
diff --git a/media/aidl/android/media/audio/common/AudioConfig.aidl b/media/aidl/android/media/audio/common/AudioConfig.aidl
index 4a264d8..8d71e8d 100644
--- a/media/aidl/android/media/audio/common/AudioConfig.aidl
+++ b/media/aidl/android/media/audio/common/AudioConfig.aidl
@@ -16,8 +16,7 @@
 
 package android.media.audio.common;
 
-import android.media.audio.common.AudioChannelLayout;
-import android.media.audio.common.AudioFormatDescription;
+import android.media.audio.common.AudioConfigBase;
 import android.media.audio.common.AudioOffloadInfo;
 
 /**
@@ -28,9 +27,8 @@
 @JavaDerive(equals = true, toString = true)
 @VintfStability
 parcelable AudioConfig {
-    int sampleRateHz;
-    AudioChannelLayout channelMask;
-    AudioFormatDescription format;
+    AudioConfigBase base;
     AudioOffloadInfo offloadInfo;
+    /** I/O buffer size in frames. */
     long frameCount;
 }
diff --git a/media/aidl/android/media/audio/common/AudioConfigBase.aidl b/media/aidl/android/media/audio/common/AudioConfigBase.aidl
new file mode 100644
index 0000000..63f12f4
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioConfigBase.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+import android.media.audio.common.AudioChannelLayout;
+import android.media.audio.common.AudioFormatDescription;
+
+/**
+ * Base configuration attributes applicable to any stream of audio.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable AudioConfigBase {
+    int sampleRate;
+    AudioChannelLayout channelMask;
+    AudioFormatDescription format;
+}
diff --git a/media/aidl/android/media/audio/common/AudioEncapsulationMode.aidl b/media/aidl/android/media/audio/common/AudioEncapsulationMode.aidl
new file mode 100644
index 0000000..6f8e9e1
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioEncapsulationMode.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.audio.common;
+
+/**
+ * Encapsulation mode used for sending audio compressed data.
+ *
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="byte")
+enum AudioEncapsulationMode {
+    /**
+     * Used as default value in parcelables to indicate that a value was not
+     * set. Should never be considered a valid setting, except for backward
+     * compatibility scenarios.
+     */
+    INVALID = -1,
+    /** No encapsulation mode for metadata. */
+    NONE = 0,
+    /** Elementary stream payload with metadata. */
+    ELEMENTARY_STREAM = 1,
+    /** Handle-based payload with metadata. */
+    HANDLE = 2,
+}
diff --git a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
index b6b5487..46496c3 100644
--- a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
+++ b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
@@ -16,8 +16,8 @@
 
 package android.media.audio.common;
 
-import android.media.audio.common.AudioChannelLayout;
-import android.media.audio.common.AudioFormatDescription;
+import android.media.audio.common.AudioConfigBase;
+import android.media.audio.common.AudioEncapsulationMode;
 import android.media.audio.common.AudioStreamType;
 import android.media.audio.common.AudioUsage;
 
@@ -29,15 +29,28 @@
 @JavaDerive(equals = true, toString = true)
 @VintfStability
 parcelable AudioOffloadInfo {
-    int sampleRateHz;
-    AudioChannelLayout channelMask;
-    AudioFormatDescription format;
+    /** Base audio configuration. */
+    AudioConfigBase base;
+    /** Stream type. Intended for use by the system only. */
     AudioStreamType streamType = AudioStreamType.INVALID;
+    /** Bit rate in bits per second. */
     int bitRatePerSecond;
-    long durationMicroseconds;
+    /** Duration in microseconds, -1 if unknown. */
+    long durationUs;
+    /** True if the stream is tied to a video stream. */
     boolean hasVideo;
+    /** True if streaming, false if local playback. */
     boolean isStreaming;
-    int bitWidth;
-    int bufferSize;
+    /** Sample bit width. */
+    int bitWidth = 16;
+    /** Offload fragment size. */
+    int offloadBufferSize;
+    /** See the documentation of AudioUsage. */
     AudioUsage usage = AudioUsage.INVALID;
+    /** See the documentation of AudioEncapsulationMode. */
+    AudioEncapsulationMode encapsulationMode = AudioEncapsulationMode.INVALID;
+    /** Content id from tuner HAL (0 if none). */
+    int contentId;
+    /** Sync id from tuner HAL (0 if none). */
+    int syncId;
 }
diff --git a/media/aidl/android/media/audio/common/AudioStreamType.aidl b/media/aidl/android/media/audio/common/AudioStreamType.aidl
index 8b70367..1fa3a9c 100644
--- a/media/aidl/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl/android/media/audio/common/AudioStreamType.aidl
@@ -14,15 +14,13 @@
  * limitations under the License.
  */
 
- // This file has been semi-automatically generated using hidl2aidl from its counterpart in
- // hardware/interfaces/audio/common/5.0/types.hal
-
 package android.media.audio.common;
 
 /**
- *  Audio streams
- *
- * Audio stream type describing the intended use case of a stream.
+ * Audio stream type describing the intended use case of a stream. Streams
+ * must be used in the context of volume management only. For playback type
+ * identification purposes, AudioContentType and AudioUsage must be used,
+ * similar to how it's done in the SDK.
  *
  * {@hide}
  */
@@ -30,21 +28,71 @@
 @Backing(type="int")
 enum AudioStreamType {
     /**
-     * Used as default value in parcelables to indicate that a value was not set.
-     * Should never be considered a valid setting, except for backward compatibility scenarios.
+     * Used as default value in parcelables to indicate that a value was not
+     * set. Should never be considered a valid setting, except for backward
+     * compatibility scenarios.
      */
     INVALID = -2,
+    /**
+     * Indicates that the operation is applied to the "default" stream
+     * in this context, e.g. MUSIC in normal device state, or RING if the
+     * phone is ringing.
+     */
     DEFAULT = -1,
-    MIN = 0,
+    /** Used to identify the volume of audio streams for phone calls. */
     VOICE_CALL = 0,
+    /** Used to identify the volume of audio streams for system sounds. */
     SYSTEM = 1,
+    /**
+     * Used to identify the volume of audio streams for the phone ring and
+     * message alerts.
+     */
     RING = 2,
+    /** Used to identify the volume of audio streams for music playback. */
     MUSIC = 3,
+    /** Used to identify the volume of audio streams for alarms. */
     ALARM = 4,
+    /** Used to identify the volume of audio streams for notifications. */
     NOTIFICATION = 5,
+    /**
+     * Used to identify the volume of audio streams for phone calls when
+     * connected via Bluetooth.
+     */
     BLUETOOTH_SCO = 6,
+    /**
+     * Used to identify the volume of audio streams for enforced system sounds
+     * in certain countries (e.g camera in Japan).
+     */
     ENFORCED_AUDIBLE = 7,
+    /** Used to identify the volume of audio streams for DTMF tones. */
     DTMF = 8,
+    /**
+     * Used to identify the volume of audio streams exclusively transmitted
+     * through the speaker (TTS) of the device.
+     */
     TTS = 9,
+    /**
+     * Used to identify the volume of audio streams for accessibility prompts.
+     */
     ACCESSIBILITY = 10,
+    /**
+     * Used to identify the volume of audio streams for virtual assistant.
+     */
+    ASSISTANT = 11,
+    /**
+     * Used for dynamic policy output mixes. Only used by the audio policy.
+     *
+     * Value reserved for system use only. HALs must never return this value to
+     * the system or accept it from the system.
+     */
+    SYS_RESERVED_REROUTING = 12,
+    /**
+     * Used for audio flinger tracks volume. Only used by the audioflinger.
+     *
+     * Value reserved for system use only. HALs must never return this value to
+     * the system or accept it from the system.
+     */
+    SYS_RESERVED_PATCH = 13,
+    /** Used for the stream corresponding to the call assistant usage. */
+    CALL_ASSISTANT = 14,
 }
diff --git a/media/aidl/android/media/audio/common/AudioUsage.aidl b/media/aidl/android/media/audio/common/AudioUsage.aidl
index 028eefe..34a7185 100644
--- a/media/aidl/android/media/audio/common/AudioUsage.aidl
+++ b/media/aidl/android/media/audio/common/AudioUsage.aidl
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
- // This file has been semi-automatically generated using hidl2aidl from its counterpart in
- // hardware/interfaces/audio/common/5.0/types.hal
-
 package android.media.audio.common;
 
 /**
@@ -26,21 +23,119 @@
 @Backing(type="int")
 enum AudioUsage {
     /**
-     * Used as default value in parcelables to indicate that a value was not set.
-     * Should never be considered a valid setting, except for backward compatibility scenarios.
+     * Used as default value in parcelables to indicate that a value was not
+     * set. Should never be considered a valid setting, except for backward
+     * compatibility scenarios.
      */
     INVALID = -1,
+    /**
+     * Usage value to use when the usage is unknown.
+     */
     UNKNOWN = 0,
+    /**
+     * Usage value to use when the usage is media, such as music, or movie
+     * soundtracks.
+     */
     MEDIA = 1,
+    /**
+     * Usage value to use when the usage is voice communications, such as
+     * telephony or VoIP.
+     */
     VOICE_COMMUNICATION = 2,
+    /**
+     * Usage value to use when the usage is in-call signalling, such as with
+     * a "busy" beep, or DTMF tones.
+     */
     VOICE_COMMUNICATION_SIGNALLING = 3,
+    /**
+     * Usage value to use when the usage is an alarm (e.g. wake-up alarm).
+     */
     ALARM = 4,
+    /**
+     * Usage value to use when the usage is notification. See other notification
+     * usages for more specialized uses.
+     */
     NOTIFICATION = 5,
+    /**
+     * Usage value to use when the usage is telephony ringtone.
+     */
     NOTIFICATION_TELEPHONY_RINGTONE = 6,
+    /**
+     * Usage value to use when the usage is a request to enter/end a
+     * communication, such as a VoIP communication or video-conference.
+     *
+     * Value reserved for system use only. HALs must never return this value to
+     * the system or accept it from the system.
+     */
+    SYS_RESERVED_NOTIFICATION_COMMUNICATION_REQUEST = 7,
+    /**
+     * Usage value to use when the usage is notification for an "instant"
+     * communication such as a chat, or SMS.
+     *
+     * Value reserved for system use only. HALs must never return this value to
+     * the system or accept it from the system.
+     */
+    SYS_RESERVED_NOTIFICATION_COMMUNICATION_INSTANT = 8,
+    /**
+     * Usage value to use when the usage is notification for a
+     * non-immediate type of communication such as e-mail.
+     *
+     * Value reserved for system use only. HALs must never return this value to
+     * the system or accept it from the system.
+     */
+    SYS_RESERVED_NOTIFICATION_COMMUNICATION_DELAYED = 9,
+    /**
+     * Usage value to use when the usage is to attract the user's attention,
+     * such as a reminder or low battery warning.
+     */
+    NOTIFICATION_EVENT = 10,
+    /**
+     * Usage value to use when the usage is for accessibility, such as with
+     * a screen reader.
+     */
     ASSISTANCE_ACCESSIBILITY = 11,
+    /**
+     * Usage value to use when the usage is driving or navigation directions.
+     */
     ASSISTANCE_NAVIGATION_GUIDANCE = 12,
+    /**
+     * Usage value to use when the usage is sonification, such as  with user
+     * interface sounds.
+     */
     ASSISTANCE_SONIFICATION = 13,
+    /**
+     * Usage value to use when the usage is for game audio.
+     */
     GAME = 14,
+    /**
+     * Usage value to use when feeding audio to the platform and replacing
+     * "traditional" audio source, such as audio capture devices.
+     */
     VIRTUAL_SOURCE = 15,
+    /**
+     * Usage value to use for audio responses to user queries, audio
+     * instructions or help utterances.
+     */
     ASSISTANT = 16,
+    /**
+     * Usage value to use for assistant voice interaction with remote caller on
+     * Cell and VoIP calls.
+     */
+    CALL_ASSISTANT = 17,
+    /**
+     * Usage value to use when the usage is an emergency.
+     */
+    EMERGENCY = 1000,
+    /**
+     * Usage value to use when the usage is a safety sound.
+     */
+    SAFETY = 1001,
+    /**
+     * Usage value to use when the usage is a vehicle status.
+     */
+    VEHICLE_STATUS = 1002,
+    /**
+     * Usage value to use when the usage is an announcement.
+     */
+    ANNOUNCEMENT = 1003,
 }
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
index 5fdeb4c..6b8686c 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
@@ -35,9 +35,7 @@
 /* @hide */
 @JavaDerive(equals=true, toString=true) @VintfStability
 parcelable AudioConfig {
-  int sampleRateHz;
-  android.media.audio.common.AudioChannelLayout channelMask;
-  android.media.audio.common.AudioFormatDescription format;
+  android.media.audio.common.AudioConfigBase base;
   android.media.audio.common.AudioOffloadInfo offloadInfo;
   long frameCount;
 }
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfigBase.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfigBase.aidl
new file mode 100644
index 0000000..f3e716b
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfigBase.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioConfigBase {
+  int sampleRate;
+  android.media.audio.common.AudioChannelLayout channelMask;
+  android.media.audio.common.AudioFormatDescription format;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMode.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMode.aidl
new file mode 100644
index 0000000..0cf2f31
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMode.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="byte") @VintfStability
+enum AudioEncapsulationMode {
+  INVALID = -1,
+  NONE = 0,
+  ELEMENTARY_STREAM = 1,
+  HANDLE = 2,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
index 9404a4b..40bd53b2 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
@@ -35,15 +35,16 @@
 /* @hide */
 @JavaDerive(equals=true, toString=true) @VintfStability
 parcelable AudioOffloadInfo {
-  int sampleRateHz;
-  android.media.audio.common.AudioChannelLayout channelMask;
-  android.media.audio.common.AudioFormatDescription format;
+  android.media.audio.common.AudioConfigBase base;
   android.media.audio.common.AudioStreamType streamType = android.media.audio.common.AudioStreamType.INVALID;
   int bitRatePerSecond;
-  long durationMicroseconds;
+  long durationUs;
   boolean hasVideo;
   boolean isStreaming;
-  int bitWidth;
-  int bufferSize;
+  int bitWidth = 16;
+  int offloadBufferSize;
   android.media.audio.common.AudioUsage usage = android.media.audio.common.AudioUsage.INVALID;
+  android.media.audio.common.AudioEncapsulationMode encapsulationMode = android.media.audio.common.AudioEncapsulationMode.INVALID;
+  int contentId;
+  int syncId;
 }
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
index 915c668..cbca6c5 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
@@ -12,8 +12,7 @@
  * 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.
- */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
-// hardware/interfaces/audio/common/5.0/types.hal
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -38,7 +37,6 @@
 enum AudioStreamType {
   INVALID = -2,
   DEFAULT = -1,
-  MIN = 0,
   VOICE_CALL = 0,
   SYSTEM = 1,
   RING = 2,
@@ -50,4 +48,8 @@
   DTMF = 8,
   TTS = 9,
   ACCESSIBILITY = 10,
+  ASSISTANT = 11,
+  SYS_RESERVED_REROUTING = 12,
+  SYS_RESERVED_PATCH = 13,
+  CALL_ASSISTANT = 14,
 }
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
index f5130a4..4c72455 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
@@ -12,8 +12,7 @@
  * 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.
- */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
-// hardware/interfaces/audio/common/5.0/types.hal
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
@@ -44,10 +43,19 @@
   ALARM = 4,
   NOTIFICATION = 5,
   NOTIFICATION_TELEPHONY_RINGTONE = 6,
+  SYS_RESERVED_NOTIFICATION_COMMUNICATION_REQUEST = 7,
+  SYS_RESERVED_NOTIFICATION_COMMUNICATION_INSTANT = 8,
+  SYS_RESERVED_NOTIFICATION_COMMUNICATION_DELAYED = 9,
+  NOTIFICATION_EVENT = 10,
   ASSISTANCE_ACCESSIBILITY = 11,
   ASSISTANCE_NAVIGATION_GUIDANCE = 12,
   ASSISTANCE_SONIFICATION = 13,
   GAME = 14,
   VIRTUAL_SOURCE = 15,
   ASSISTANT = 16,
+  CALL_ASSISTANT = 17,
+  EMERGENCY = 1000,
+  SAFETY = 1001,
+  VEHICLE_STATUS = 1002,
+  ANNOUNCEMENT = 1003,
 }
diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java
index 3b58934..f96ba8c 100644
--- a/media/java/android/media/audio/common/AidlConversion.java
+++ b/media/java/android/media/audio/common/AidlConversion.java
@@ -35,18 +35,25 @@
  *  aidl2api_AIDL-type-name_SDK-type-name
  *  api2aidl_SDK-type-name_AIDL-type-name
  *
+ * Since the range of the SDK values is generally narrower than
+ * the range of AIDL values, when a match can't be found, the
+ * conversion function returns a corresponding 'INVALID' value.
+ *
  * Methods that convert between AIDL and legacy types are called
  * using the following pattern:
  *
  *  aidl2legacy_AIDL-type-name_native-type-name
  *  legacy2aidl_native-type-name_AIDL-type-name
  *
+ * In general, there is a 1:1 mapping between AIDL and framework
+ * types, and a failure to convert a value indicates a programming
+ * error. Thus, the conversion functions may throw an IllegalArgumentException.
+ *
  * @hide
  */
 @VisibleForTesting
 public class AidlConversion {
     /** Convert from AIDL AudioChannelLayout to legacy audio_channel_mask_t. */
-    @VisibleForTesting
     public static int /*audio_channel_mask_t*/ aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
             @NonNull AudioChannelLayout aidl, boolean isInput) {
         Parcel out = Parcel.obtain();
@@ -60,7 +67,6 @@
     }
 
     /** Convert from legacy audio_channel_mask_t to AIDL AudioChannelLayout. */
-    @VisibleForTesting
     public static AudioChannelLayout legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
             int /*audio_channel_mask_t*/ legacy, boolean isInput) {
         Parcel in = legacy2aidl_audio_channel_mask_t_AudioChannelLayout_Parcel(legacy, isInput);
@@ -71,12 +77,11 @@
                 in.recycle();
             }
         }
-        throw new IllegalArgumentException("Invalid legacy audio "
-                + (isInput ? "input" : "output") + " channel mask: " + legacy);
+        throw new IllegalArgumentException("Failed to convert legacy audio "
+                + (isInput ? "input" : "output") + " audio_channel_mask_t " + legacy + " value");
     }
 
     /** Convert from AIDL AudioFormatDescription to legacy audio_format_t. */
-    @VisibleForTesting
     public static int /*audio_format_t*/ aidl2legacy_AudioFormatDescription_audio_format_t(
             @NonNull AudioFormatDescription aidl) {
         Parcel out = Parcel.obtain();
@@ -90,7 +95,6 @@
     }
 
     /** Convert from legacy audio_format_t to AIDL AudioFormatDescription. */
-    @VisibleForTesting
     public static @NonNull AudioFormatDescription legacy2aidl_audio_format_t_AudioFormatDescription(
             int /*audio_format_t*/ legacy) {
         Parcel in = legacy2aidl_audio_format_t_AudioFormatDescription_Parcel(legacy);
@@ -101,28 +105,33 @@
                 in.recycle();
             }
         }
-        throw new IllegalArgumentException("Invalid legacy audio format: " + legacy);
+        throw new IllegalArgumentException(
+                "Failed to convert legacy audio_format_t value " + legacy);
     }
 
+    /** Convert from AIDL AudioEncapsulationMode to legacy audio_encapsulation_mode_t. */
+    public static native int aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(
+            int /*AudioEncapsulationMode.* */ aidl);
+
+    /** Convert from legacy audio_encapsulation_mode_t to AIDL AudioEncapsulationMode. */
+    public static native int legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(
+            int /*audio_encapsulation_mode_t*/ legacy);
+
+    /** Convert from AIDL AudioStreamType to legacy audio_stream_type_t. */
+    public static native int aidl2legacy_AudioStreamType_audio_stream_type_t(
+            int /*AudioStreamType.* */ aidl);
+
     /** Convert from legacy audio_stream_type_t to AIDL AudioStreamType. */
-    @VisibleForTesting
-    public static int legacy2aidl_audio_stream_type_t_AudioStreamType(
-            int /*audio_stream_type_t*/ legacy) {
-        // Relies on the fact that AudioStreamType was converted from
-        // the HIDL definition which uses the same constant values as system/audio.h
-        return legacy;
-    }
+    public static native int legacy2aidl_audio_stream_type_t_AudioStreamType(
+            int /*audio_stream_type_t*/ legacy);
+
+    /** Convert from AIDL AudioUsage to legacy audio_usage_t. */
+    public static native int aidl2legacy_AudioUsage_audio_usage_t(int /*AudioUsage.* */ aidl);
 
     /** Convert from legacy audio_usage_t to AIDL AudioUsage. */
-    @VisibleForTesting
-    public static int legacy2aidl_audio_usage_t_AudioUsage(int /*audio_usage_t*/ legacy) {
-        // Relies on the fact that AudioUsage was converted from
-        // the HIDL definition which uses the same constant values as system/audio.h
-        return legacy;
-    }
+    public static native int legacy2aidl_audio_usage_t_AudioUsage(int /*audio_usage_t*/ legacy);
 
     /** Convert from AIDL AudioChannelLayout to SDK AudioFormat.CHANNEL_*. */
-    @VisibleForTesting
     public static int aidl2api_AudioChannelLayout_AudioFormatChannelMask(
             @NonNull AudioChannelLayout aidlMask, boolean isInput) {
         switch (aidlMask.getTag()) {
@@ -283,29 +292,34 @@
     }
 
     /** Convert from AIDL AudioConfig to SDK AudioFormat. */
-    @VisibleForTesting
     public static @NonNull AudioFormat aidl2api_AudioConfig_AudioFormat(
-            @NonNull AudioConfig audioConfig, boolean isInput) {
+            @NonNull AudioConfig aidl, boolean isInput) {
+        // Only information from the encapsulated AudioConfigBase is used.
+        return aidl2api_AudioConfigBase_AudioFormat(aidl.base, isInput);
+    }
+
+    /** Convert from AIDL AudioConfigBase to SDK AudioFormat. */
+    public static @NonNull AudioFormat aidl2api_AudioConfigBase_AudioFormat(
+            @NonNull AudioConfigBase aidl, boolean isInput) {
         AudioFormat.Builder apiBuilder = new AudioFormat.Builder();
-        apiBuilder.setSampleRate(audioConfig.sampleRateHz);
-        if (audioConfig.channelMask.getTag() != AudioChannelLayout.indexMask) {
+        apiBuilder.setSampleRate(aidl.sampleRate);
+        if (aidl.channelMask.getTag() != AudioChannelLayout.indexMask) {
             apiBuilder.setChannelMask(aidl2api_AudioChannelLayout_AudioFormatChannelMask(
-                            audioConfig.channelMask, isInput));
+                            aidl.channelMask, isInput));
         } else {
             apiBuilder.setChannelIndexMask(aidl2api_AudioChannelLayout_AudioFormatChannelMask(
-                            audioConfig.channelMask, isInput));
+                            aidl.channelMask, isInput));
         }
-        apiBuilder.setEncoding(aidl2api_AudioFormat_AudioFormatEncoding(audioConfig.format));
+        apiBuilder.setEncoding(aidl2api_AudioFormat_AudioFormatEncoding(aidl.format));
         return apiBuilder.build();
     }
 
     /** Convert from AIDL AudioFormat to SDK AudioFormat.ENCODING_*. */
-    @VisibleForTesting
     public static int aidl2api_AudioFormat_AudioFormatEncoding(
-            @NonNull AudioFormatDescription aidlFormat) {
-        switch (aidlFormat.type) {
+            @NonNull AudioFormatDescription aidl) {
+        switch (aidl.type) {
             case AudioFormatType.PCM:
-                switch (aidlFormat.pcm) {
+                switch (aidl.pcm) {
                     case PcmType.UINT_8_BIT:
                         return AudioFormat.ENCODING_PCM_8BIT;
                     case PcmType.INT_16_BIT:
@@ -321,54 +335,54 @@
                         return AudioFormat.ENCODING_INVALID;
                 }
             case AudioFormatType.NON_PCM: // same as DEFAULT
-                if (aidlFormat.encoding != null && !aidlFormat.encoding.isEmpty()) {
-                    if (MediaFormat.MIMETYPE_AUDIO_AC3.equals(aidlFormat.encoding)) {
+                if (aidl.encoding != null && !aidl.encoding.isEmpty()) {
+                    if (MediaFormat.MIMETYPE_AUDIO_AC3.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_AC3;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_EAC3.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_EAC3.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_E_AC3;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_DTS.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_DTS.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_DTS;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_DTS_HD.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_DTS_HD.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_DTS_HD;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_MPEG.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_MPEG.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_MP3;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_AAC_LC.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_AAC_LC.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_AAC_LC;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_AAC_HE_V1.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_AAC_HE_V1.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_AAC_HE_V1;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_AAC_HE_V2.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_AAC_HE_V2.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_AAC_HE_V2;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_IEC61937.equals(aidlFormat.encoding)
-                            && aidlFormat.pcm == PcmType.INT_16_BIT) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_IEC61937.equals(aidl.encoding)
+                            && aidl.pcm == PcmType.INT_16_BIT) {
                         return AudioFormat.ENCODING_IEC61937;
                     } else if (MediaFormat.MIMETYPE_AUDIO_DOLBY_TRUEHD.equals(
-                                    aidlFormat.encoding)) {
+                                    aidl.encoding)) {
                         return AudioFormat.ENCODING_DOLBY_TRUEHD;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_AAC_ELD.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_AAC_ELD.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_AAC_ELD;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_AAC_XHE.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_AAC_XHE.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_AAC_XHE;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_AC4.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_AC4.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_AC4;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_EAC3_JOC.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_EAC3_JOC.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_E_AC3_JOC;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_DOLBY_MAT.equals(aidlFormat.encoding)
-                            || aidlFormat.encoding.startsWith(
+                    } else if (MediaFormat.MIMETYPE_AUDIO_DOLBY_MAT.equals(aidl.encoding)
+                            || aidl.encoding.startsWith(
                                     MediaFormat.MIMETYPE_AUDIO_DOLBY_MAT + ".")) {
                         return AudioFormat.ENCODING_DOLBY_MAT;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_OPUS.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_OPUS.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_OPUS;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_BL_L3.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_BL_L3.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_MPEGH_BL_L3;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_BL_L4.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_BL_L4.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_MPEGH_BL_L4;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_LC_L3.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_LC_L3.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_MPEGH_LC_L3;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_LC_L4.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_MPEGH_LC_L4.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_MPEGH_LC_L4;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_DTS_UHD.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_DTS_UHD.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_DTS_UHD;
-                    } else if (MediaFormat.MIMETYPE_AUDIO_DRA.equals(aidlFormat.encoding)) {
+                    } else if (MediaFormat.MIMETYPE_AUDIO_DRA.equals(aidl.encoding)) {
                         return AudioFormat.ENCODING_DRA;
                     } else {
                         return AudioFormat.ENCODING_INVALID;
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 72cddc9..353556d 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -50,6 +50,13 @@
     private final Context mContext;
     private final Map<Callback, CallbackRecord> mCallbacks;
 
+    /**
+     * Store the WindowContext in a field. If it is a local variable, and it is garbage collected
+     * during a MediaProjection session, the WindowContainer listener no longer exists.
+     */
+    @Nullable
+    private Context mWindowContext;
+
     /** @hide */
     public MediaProjection(Context context, IMediaProjection impl) {
         mCallbacks = new ArrayMap<Callback, CallbackRecord>();
@@ -162,11 +169,11 @@
      */
     private VirtualDisplayConfig.Builder buildMirroredVirtualDisplay(@NonNull String name,
             int width, int height, int dpi) {
-        Context windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
+        mWindowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
                 TYPE_APPLICATION, null /* options */);
         final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
                 height, dpi);
-        builder.setWindowTokenClientToMirror(windowContext.getWindowContextToken());
+        builder.setWindowTokenClientToMirror(mWindowContext.getWindowContextToken());
         return builder;
     }
 
diff --git a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
index c9e79e6..5f64d20 100644
--- a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
+++ b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
@@ -16,19 +16,19 @@
 
 package android.media.audio.common;
 
+import android.media.AudioAttributes;
 import android.media.AudioFormat;
 import android.media.AudioSystem;
+import android.media.AudioTrack;
 import android.media.MediaFormat;
 import android.platform.test.annotations.Presubmit;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import androidx.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.*;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 /**
  * Unit tests for AidlConversion utilities.
@@ -36,10 +36,15 @@
  * Run with "atest AidlConversionUnitTests".
  */
 @Presubmit
-@RunWith(JUnit4.class)
+@RunWith(AndroidJUnit4.class)
 public final class AidlConversionUnitTests {
 
     private static final String TAG = "AidlConvTests";
+    // Negative values are considered to be "invalid" as a general rule.
+    // However, '-1' is sometimes used as "system invalid" value, and thus
+    // does not cause an exception to be thrown during conversion.
+    private static int sInvalidValue = -2;
+    private static byte sInvalidValueByte = -2;
 
     @Test
     public void testAudioChannelConversionApiDefault() {
@@ -92,11 +97,12 @@
     }
 
     @Test
-    public void testAudioConfigConfersionApiIndex() {
+    public void testAudioConfigConversionApiIndex() {
         final AudioConfig aidl = new AudioConfig();
-        aidl.sampleRateHz = 8000;
-        aidl.channelMask = AudioChannelLayout.indexMask(AudioChannelLayout.INDEX_MASK_1);
-        aidl.format = createPcm16FormatAidl();
+        aidl.base = new AudioConfigBase();
+        aidl.base.sampleRate = 8000;
+        aidl.base.channelMask = AudioChannelLayout.indexMask(AudioChannelLayout.INDEX_MASK_1);
+        aidl.base.format = createPcm16FormatAidl();
         // Other fields in AudioConfig are irrelevant.
         final AudioFormat api = AidlConversion.aidl2api_AudioConfig_AudioFormat(
                 aidl, false /*isInput*/);
@@ -109,11 +115,12 @@
     }
 
     @Test
-    public void testAudioConfigConfersionApiLayout() {
+    public void testAudioConfigConversionApiLayout() {
         final AudioConfig aidl = new AudioConfig();
-        aidl.sampleRateHz = 8000;
-        aidl.channelMask = AudioChannelLayout.layoutMask(AudioChannelLayout.LAYOUT_MONO);
-        aidl.format = createPcm16FormatAidl();
+        aidl.base = new AudioConfigBase();
+        aidl.base.sampleRate = 8000;
+        aidl.base.channelMask = AudioChannelLayout.layoutMask(AudioChannelLayout.LAYOUT_MONO);
+        aidl.base.format = createPcm16FormatAidl();
         // Other fields in AudioConfig are irrelevant.
         final AudioFormat api = AidlConversion.aidl2api_AudioConfig_AudioFormat(
                 aidl, false /*isInput*/);
@@ -128,6 +135,40 @@
     }
 
     @Test
+    public void testAudioConfigBaseConversionApiIndex() {
+        final AudioConfigBase aidl = new AudioConfigBase();
+        aidl.sampleRate = 8000;
+        aidl.channelMask = AudioChannelLayout.indexMask(AudioChannelLayout.INDEX_MASK_1);
+        aidl.format = createPcm16FormatAidl();
+        final AudioFormat api = AidlConversion.aidl2api_AudioConfigBase_AudioFormat(
+                aidl, false /*isInput*/);
+        final AudioFormat apiInput = AidlConversion.aidl2api_AudioConfigBase_AudioFormat(
+                aidl, true /*isInput*/);
+        assertEquals(api, apiInput);
+        assertEquals(8000, api.getSampleRate());
+        assertEquals(1, api.getChannelIndexMask());
+        assertEquals(AudioFormat.ENCODING_PCM_16BIT, api.getEncoding());
+    }
+
+    @Test
+    public void testAudioConfigBaseConversionApiLayout() {
+        final AudioConfigBase aidl = new AudioConfigBase();
+        aidl.sampleRate = 8000;
+        aidl.channelMask = AudioChannelLayout.layoutMask(AudioChannelLayout.LAYOUT_MONO);
+        aidl.format = createPcm16FormatAidl();
+        final AudioFormat api = AidlConversion.aidl2api_AudioConfigBase_AudioFormat(
+                aidl, false /*isInput*/);
+        assertEquals(8000, api.getSampleRate());
+        assertEquals(AudioFormat.CHANNEL_OUT_MONO, api.getChannelMask());
+        assertEquals(AudioFormat.ENCODING_PCM_16BIT, api.getEncoding());
+        final AudioFormat apiInput = AidlConversion.aidl2api_AudioConfigBase_AudioFormat(
+                aidl, true /*isInput*/);
+        assertEquals(8000, apiInput.getSampleRate());
+        assertEquals(AudioFormat.CHANNEL_IN_MONO, apiInput.getChannelMask());
+        assertEquals(AudioFormat.ENCODING_PCM_16BIT, apiInput.getEncoding());
+    }
+
+    @Test
     public void testAudioFormatConversionApiDefault() {
         final AudioFormatDescription aidl = new AudioFormatDescription();
         final int api = AidlConversion.aidl2api_AudioFormat_AudioFormatEncoding(aidl);
@@ -236,6 +277,22 @@
     }
 
     @Test
+    public void testAudioChannelConversionLegacyInvalid() {
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
+                        AudioChannelLayout.voiceMask(sInvalidValue), false /*isInput*/));
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
+                        AudioChannelLayout.voiceMask(sInvalidValue), true /*isInput*/));
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
+                        sInvalidValue, false /*isInput*/));
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
+                        sInvalidValue, true /*isInput*/));
+    }
+
+    @Test
     public void testAudioFormatConversionLegacyDefault() {
         final int legacy = AudioSystem.AUDIO_FORMAT_DEFAULT;
         final AudioFormatDescription aidl =
@@ -273,7 +330,67 @@
         assertEquals(legacy, legacyBack);
     }
 
-    private AudioFormatDescription createPcm16FormatAidl() {
+    @Test
+    public void testAudioFormatConversionLegacyInvalid() {
+        final AudioFormatDescription aidl = new AudioFormatDescription();
+        aidl.type = sInvalidValueByte;
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.aidl2legacy_AudioFormatDescription_audio_format_t(aidl));
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.legacy2aidl_audio_format_t_AudioFormatDescription(
+                        sInvalidValue));
+    }
+
+    @Test
+    public void testAudioEncapsulationModeConversionLegacy() {
+        // AIDL values are synchronized with SDK, so we can use the SDK values as AIDL.
+        final int aidl = AudioTrack.ENCAPSULATION_MODE_ELEMENTARY_STREAM;
+        final int legacy =
+                AidlConversion.aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(aidl);
+        final int aidlBack =
+                AidlConversion.legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(
+                        legacy);
+        assertEquals(aidl, aidlBack);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(
+                        sInvalidValue));
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(
+                        sInvalidValue));
+    }
+
+    @Test
+    public void testAudioStreamTypeConversionLegacy() {
+        // AIDL values are synchronized with SDK, so we can use the SDK values as AIDL.
+        final int aidl = AudioSystem.STREAM_MUSIC;
+        final int legacy = AidlConversion.aidl2legacy_AudioStreamType_audio_stream_type_t(aidl);
+        final int aidlBack = AidlConversion.legacy2aidl_audio_stream_type_t_AudioStreamType(legacy);
+        assertEquals(aidl, aidlBack);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.aidl2legacy_AudioStreamType_audio_stream_type_t(
+                        sInvalidValue));
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.legacy2aidl_audio_stream_type_t_AudioStreamType(
+                        sInvalidValue));
+    }
+
+    @Test
+    public void testAudioUsageConversionLegacy() {
+        // AIDL values are synchronized with SDK, so we can use the SDK values as AIDL.
+        final int aidl = AudioAttributes.USAGE_MEDIA;
+        final int legacy = AidlConversion.aidl2legacy_AudioUsage_audio_usage_t(aidl);
+        final int aidlBack = AidlConversion.legacy2aidl_audio_usage_t_AudioUsage(legacy);
+        assertEquals(aidl, aidlBack);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.aidl2legacy_AudioUsage_audio_usage_t(sInvalidValue));
+        assertThrows(IllegalArgumentException.class,
+                () -> AidlConversion.legacy2aidl_audio_usage_t_AudioUsage(sInvalidValue));
+    }
+
+    private static AudioFormatDescription createPcm16FormatAidl() {
         final AudioFormatDescription aidl = new AudioFormatDescription();
         aidl.type = AudioFormatType.PCM;
         aidl.pcm = PcmType.INT_16_BIT;
diff --git a/packages/SystemUI/res/layout/communal_host_view.xml b/packages/SystemUI/res/layout/communal_host_view.xml
index 4406a46..cd9c260 100644
--- a/packages/SystemUI/res/layout/communal_host_view.xml
+++ b/packages/SystemUI/res/layout/communal_host_view.xml
@@ -18,13 +18,6 @@
 <!-- This is a view that shows general status information in Keyguard. -->
 <com.android.systemui.communal.CommunalHostView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/communal_host"
-    android:orientation="vertical"
-    systemui:layout_constraintEnd_toEndOf="parent"
-    systemui:layout_constraintStart_toStartOf="parent"
-    systemui:layout_constraintTop_toTopOf="parent"
-    systemui:layout_constraintBottom_toBottomOf="parent"
-    systemui:layout_constraintHeight_percent="@dimen/communal_source_height_percentage"
-    android:layout_width="0dp"
-    android:layout_height="0dp"/>
\ No newline at end of file
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
index cc44b5e..b1e8c38 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
@@ -49,6 +49,11 @@
         />
     </FrameLayout>
 
+    <!-- We want this to be centered (to align with notches). In order to do that, the following
+         has to hold (in portrait):
+         * date_container and privacy_container must have the same width and weight
+         * header_text_container must be gone
+         -->
     <android.widget.Space
         android:id="@+id/space"
         android:layout_width="0dp"
@@ -75,7 +80,7 @@
         android:layout_weight="1"
         android:gravity="center_vertical|end" >
 
-    <include layout="@layout/ongoing_privacy_chip" />
+        <include layout="@layout/ongoing_privacy_chip" />
 
     </FrameLayout>
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 7513af3..9821fb0 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -25,6 +25,10 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@android:color/transparent">
+
+    <include layout="@layout/communal_host_view"
+             android:visibility="gone"/>
+
     <FrameLayout
         android:id="@+id/big_clock_container"
         android:layout_width="match_parent"
@@ -125,9 +129,6 @@
             systemui:layout_constraintEnd_toEndOf="parent"
         />
 
-        <include layout="@layout/communal_host_view"
-                 android:visibility="gone"/>
-
         <include layout="@layout/ambient_indication"
             android:id="@+id/ambient_indication_container" />
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d901267..2c19212 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -189,7 +189,8 @@
     <!-- UDFPS colors -->
     <color name="udfps_enroll_icon">#000000</color>                         <!-- 100% black -->
     <color name="udfps_moving_target_fill">#cc4285f4</color>                <!-- 80% blue -->
-    <color name="udfps_enroll_progress">#ff669DF6</color>                   <!-- 100% blue -->
+    <color name="udfps_enroll_progress">#ff669DF6</color>                   <!-- blue 400 -->
+    <color name="udfps_enroll_progress_help">#ffEE675C</color>              <!-- red 400 -->
 
     <!-- Logout button -->
     <color name="logout_button_bg_color">#ccffffff</color>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 9b5eae8..49cd279 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
 import com.android.systemui.unfold.updates.DeviceFoldStateProvider
 import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
+import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
 import com.android.systemui.unfold.updates.hinge.RotationSensorHingeAngleProvider
 import java.lang.IllegalStateException
 import java.util.concurrent.Executor
@@ -50,7 +51,13 @@
 
     val hingeAngleProvider =
         if (config.mode == ANIMATION_MODE_HINGE_ANGLE) {
-            RotationSensorHingeAngleProvider(sensorManager)
+            // TODO: after removing temporary "config.mode" we should just
+            //       switch between fixed timing and hinge sensor based on this flag
+            if (config.isHingeAngleEnabled) {
+                HingeSensorAngleProvider(sensorManager)
+            } else {
+                RotationSensorHingeAngleProvider(sensorManager)
+            }
         } else {
             EmptyHingeAngleProvider()
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
index fa6b5de..e7c6998a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -25,6 +25,9 @@
     override val isEnabled: Boolean
         get() = readIsEnabled() && mode != ANIMATION_MODE_DISABLED
 
+    override val isHingeAngleEnabled: Boolean
+        get() = readIsHingeAngleEnabled()
+
     @AnimationMode
     override val mode: Int
         get() = SystemProperties.getInt(UNFOLD_TRANSITION_MODE_PROPERTY_NAME,
@@ -32,6 +35,9 @@
 
     private fun readIsEnabled(): Boolean = context.resources
         .getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
+
+    private fun readIsHingeAngleEnabled(): Boolean = context.resources
+        .getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle)
 }
 
 /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
index 75d9dc3..a569757 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
@@ -19,6 +19,7 @@
 
 interface UnfoldTransitionConfig {
     val isEnabled: Boolean
+    val isHingeAngleEnabled: Boolean
 
     @AnimationMode
     val mode: Int
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index b111892..10e6c2b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.unfold.progress
 
 import android.os.Handler
+import android.util.MathUtils.saturate
 import androidx.dynamicanimation.animation.DynamicAnimation
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringAnimation
@@ -24,6 +25,7 @@
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
 import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
 import com.android.systemui.unfold.updates.FoldStateProvider
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
@@ -33,7 +35,6 @@
  * Maps fold updates to unfold transition progress using DynamicAnimation.
  *
  * TODO(b/193793338) Current limitations:
- *  - doesn't handle folding transition
  *  - doesn't handle postures
  */
 internal class PhysicsBasedUnfoldTransitionProgressProvider(
@@ -75,14 +76,20 @@
 
     override fun onHingeAngleUpdate(angle: Float) {
         if (!isTransitionRunning || isAnimatedCancelRunning) return
-        springAnimation.animateToFinalPosition(angle / 180f)
+        val progress = saturate(angle / FINAL_HINGE_ANGLE_POSITION)
+        springAnimation.animateToFinalPosition(progress)
     }
 
     override fun onFoldUpdate(@FoldUpdate update: Int) {
         when (update) {
             FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> {
-                onStartTransition()
                 startTransition(startValue = 0f)
+
+                // Stop the animation if the device has already opened by the time when
+                // the display is available as we won't receive the full open event anymore
+                if (foldStateProvider.isFullyOpened) {
+                    cancelTransition(endValue = 1f, animate = true)
+                }
             }
             FOLD_UPDATE_FINISH_FULL_OPEN -> {
                 cancelTransition(endValue = 1f, animate = true)
@@ -90,13 +97,16 @@
             FOLD_UPDATE_FINISH_CLOSED -> {
                 cancelTransition(endValue = 0f, animate = false)
             }
+            FOLD_UPDATE_START_CLOSING -> {
+                startTransition(startValue = 1f)
+            }
         }
     }
 
     private fun cancelTransition(endValue: Float, animate: Boolean) {
         handler.removeCallbacks(timeoutRunnable)
 
-        if (animate) {
+        if (isTransitionRunning && animate) {
             isAnimatedCancelRunning = true
             springAnimation.animateToFinalPosition(endValue)
         } else {
@@ -182,3 +192,4 @@
 private const val TRANSITION_TIMEOUT_MILLIS = 2000L
 private const val SPRING_STIFFNESS = 200.0f
 private const val MINIMAL_VISIBLE_CHANGE = 0.001f
+private const val FINAL_HINGE_ANGLE_POSITION = 165f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 949652b..c37ab06 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -68,20 +68,29 @@
         outputListeners.remove(listener)
     }
 
+    override val isFullyOpened: Boolean
+        get() = !isFolded && lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN
+
     private fun onHingeAngle(angle: Float) {
         when (lastFoldUpdate) {
             FOLD_UPDATE_FINISH_FULL_OPEN -> {
-                if (FULLY_OPEN_DEGREES - angle > MOVEMENT_THRESHOLD_DEGREES) {
+                if (FULLY_OPEN_DEGREES - angle > START_CLOSING_THRESHOLD_DEGREES) {
                     lastFoldUpdate = FOLD_UPDATE_START_CLOSING
                     outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_CLOSING) }
                 }
             }
-            FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING -> {
+            FOLD_UPDATE_START_OPENING -> {
                 if (FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES) {
                     lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
                     outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
                 }
             }
+            FOLD_UPDATE_START_CLOSING -> {
+                if (FULLY_OPEN_DEGREES - angle < START_CLOSING_THRESHOLD_DEGREES) {
+                    lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
+                    outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
+                }
+            }
         }
 
         outputListeners.forEach { it.onHingeAngleUpdate(angle) }
@@ -120,5 +129,5 @@
     }
 }
 
-private const val MOVEMENT_THRESHOLD_DEGREES = 10f
-private const val FULLY_OPEN_THRESHOLD_DEGREES = 10f
\ No newline at end of file
+private const val START_CLOSING_THRESHOLD_DEGREES = 95f
+private const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 4c6d241..11984b93 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -28,6 +28,8 @@
     fun start()
     fun stop()
 
+    val isFullyOpened: Boolean
+
     interface FoldUpdatesListener {
         fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float)
         fun onFoldUpdate(@FoldUpdate update: Int)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
index 8549913..2520d35 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
@@ -3,6 +3,12 @@
 import androidx.core.util.Consumer
 import com.android.systemui.statusbar.policy.CallbackController
 
+/**
+ * Emits device hinge angle values (angle between two integral parts of the device).
+ * The hinge angle could be from 0 to 360 degrees inclusive.
+ * For foldable devices usually 0 corresponds to fully closed (folded) state and
+ * 180 degrees corresponds to fully open (flat) state
+ */
 internal interface HingeAngleProvider : CallbackController<Consumer<Float>> {
     fun start()
     fun stop()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
new file mode 100644
index 0000000..a42ebef
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
@@ -0,0 +1,42 @@
+package com.android.systemui.unfold.updates.hinge
+
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
+import androidx.core.util.Consumer
+
+internal class HingeSensorAngleProvider(
+    private val sensorManager: SensorManager
+) : HingeAngleProvider {
+
+    private val sensorListener = HingeAngleSensorListener()
+    private val listeners: MutableList<Consumer<Float>> = arrayListOf()
+
+    override fun start() {
+        val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)
+        sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_FASTEST)
+    }
+
+    override fun stop() {
+        sensorManager.unregisterListener(sensorListener)
+    }
+
+    override fun removeCallback(listener: Consumer<Float>) {
+        listeners.remove(listener)
+    }
+
+    override fun addCallback(listener: Consumer<Float>) {
+        listeners.add(listener)
+    }
+
+    private inner class HingeAngleSensorListener : SensorEventListener {
+
+        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
+        }
+
+        override fun onSensorChanged(event: SensorEvent) {
+            listeners.forEach { it.accept(event.values[0]) }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
index 0b0e430..153da4b 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
@@ -19,6 +19,7 @@
 import com.android.keyguard.KeyguardStatusViewController;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 
 import dagger.BindsInstance;
 import dagger.Subcomponent;
@@ -36,8 +37,8 @@
     interface Factory {
         KeyguardStatusBarViewComponent build(
                 @BindsInstance KeyguardStatusBarView view,
-                @BindsInstance KeyguardStatusBarViewController.ViewStateProvider
-                        viewStateProvider);
+                @BindsInstance NotificationPanelViewController.NotificationPanelViewStateProvider
+                        notificationPanelViewStateProvider);
     }
 
     /** Builds a {@link KeyguardStatusViewController}. */
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 3f61d3c..fd37b35 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -126,6 +126,7 @@
         boolean mCredentialAllowed;
         boolean mSkipIntro;
         long mOperationId;
+        long mRequestId;
         @BiometricMultiSensorMode int mMultiSensorConfig;
     }
 
@@ -172,6 +173,12 @@
             return this;
         }
 
+        /** Unique id for this request. */
+        public Builder setRequestId(long requestId) {
+            mConfig.mRequestId = requestId;
+            return this;
+        }
+
         /** The multi-sensor mode. */
         public Builder setMultiSensorConfig(@BiometricMultiSensorMode int multiSensorConfig) {
             mConfig.mMultiSensorConfig = multiSensorConfig;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index ab5f2b828..bcc0530 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -501,7 +501,7 @@
     @Override
     public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver,
             int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
-            int userId, String opPackageName, long operationId,
+            int userId, long operationId, String opPackageName, long requestId,
             @BiometricMultiSensorMode int multiSensorConfig) {
         @Authenticators.Types final int authenticators = promptInfo.getAuthenticators();
 
@@ -515,6 +515,7 @@
                     + ", credentialAllowed: " + credentialAllowed
                     + ", requireConfirmation: " + requireConfirmation
                     + ", operationId: " + operationId
+                    + ", requestId: " + requestId
                     + ", multiSensorConfig: " + multiSensorConfig);
         }
         SomeArgs args = SomeArgs.obtain();
@@ -526,6 +527,7 @@
         args.argi1 = userId;
         args.arg6 = opPackageName;
         args.arg7 = operationId;
+        args.arg8 = requestId;
         args.argi2 = multiSensorConfig;
 
         boolean skipAnimation = false;
@@ -629,6 +631,7 @@
         if (mCurrentDialog == null) {
             // Could be possible if the caller canceled authentication after credential success
             // but before the client was notified.
+            if (DEBUG) Log.d(TAG, "dialog already gone");
             return;
         }
 
@@ -683,6 +686,7 @@
         final int userId = args.argi1;
         final String opPackageName = (String) args.arg6;
         final long operationId = (long) args.arg7;
+        final long requestId = (long) args.arg8;
         final @BiometricMultiSensorMode int multiSensorConfig = args.argi2;
 
         // Create a new dialog but do not replace the current one yet.
@@ -695,6 +699,7 @@
                 opPackageName,
                 skipAnimation,
                 operationId,
+                requestId,
                 multiSensorConfig);
 
         if (newDialog == null) {
@@ -772,7 +777,7 @@
 
     protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation,
             int userId, int[] sensorIds, boolean credentialAllowed, String opPackageName,
-            boolean skipIntro, long operationId,
+            boolean skipIntro, long operationId, long requestId,
             @BiometricMultiSensorMode int multiSensorConfig) {
         return new AuthContainerView.Builder(mContext)
                 .setCallback(this)
@@ -782,6 +787,7 @@
                 .setOpPackageName(opPackageName)
                 .setSkipIntro(skipIntro)
                 .setOperationId(operationId)
+                .setRequestId(requestId)
                 .setMultiSensorConfig(multiSensorConfig)
                 .build(sensorIds, credentialAllowed, mFpProps, mFaceProps);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index ea69b1d..b5273ab 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -143,6 +143,10 @@
         mProgressDrawable.onLastStepAcquired();
     }
 
+    void onEnrollmentHelp() {
+        mProgressDrawable.onEnrollmentHelp();
+    }
+
     @Override
     public void draw(@NonNull Canvas canvas) {
         mProgressDrawable.draw(canvas);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 6a918a6..19148e3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -50,6 +50,7 @@
     interface Listener {
         void onEnrollmentProgress(int remaining, int totalSteps);
         void onLastStepAcquired();
+        void onEnrollmentHelp();
     }
 
     @NonNull private final Context mContext;
@@ -138,7 +139,9 @@
     }
 
     void onEnrollmentHelp() {
-
+        if (mListener != null) {
+            mListener.onEnrollmentHelp();
+        }
     }
 
     void setListener(Listener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index 4195009..9c486b9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -16,7 +16,9 @@
 
 package com.android.systemui.biometrics;
 
+import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
+import android.annotation.ColorInt;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -46,6 +48,11 @@
     @NonNull private final Paint mProgressPaint;
 
     @Nullable private ValueAnimator mProgressAnimator;
+    @Nullable private ValueAnimator mProgressShowingHelpAnimator;
+    @Nullable private ValueAnimator mProgressHidingHelpAnimator;
+    @ColorInt private final int mProgressColor;
+    @ColorInt private final int mProgressHelpColor;
+    private final int mShortAnimationDuration;
     private float mProgress;
     private int mRotation; // After last step, rotate the progress bar once
     private boolean mLastStepAcquired;
@@ -55,6 +62,11 @@
         mContext = context;
         mParent = parent;
 
+        mShortAnimationDuration = context.getResources()
+                .getInteger(com.android.internal.R.integer.config_shortAnimTime);
+        mProgressColor = context.getColor(R.color.udfps_enroll_progress);
+        mProgressHelpColor = context.getColor(R.color.udfps_enroll_progress_help);
+
         mBackgroundCirclePaint = new Paint();
         mBackgroundCirclePaint.setStrokeWidth(Utils.dpToPixels(context, PROGRESS_BAR_THICKNESS_DP));
         mBackgroundCirclePaint.setColor(context.getColor(R.color.white_disabled));
@@ -74,7 +86,7 @@
         // Progress should not be color extracted
         mProgressPaint = new Paint();
         mProgressPaint.setStrokeWidth(Utils.dpToPixels(context, PROGRESS_BAR_THICKNESS_DP));
-        mProgressPaint.setColor(context.getColor(R.color.udfps_enroll_progress));
+        mProgressPaint.setColor(mProgressColor);
         mProgressPaint.setAntiAlias(true);
         mProgressPaint.setStyle(Paint.Style.STROKE);
         mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
@@ -92,7 +104,9 @@
             return;
         }
 
-        long animationDuration = 150;
+        long animationDuration = mShortAnimationDuration;
+
+        hideEnrollmentHelp();
 
         if (progress == 1.f) {
             animationDuration = 400;
@@ -128,6 +142,47 @@
         mLastStepAcquired = true;
     }
 
+    void onEnrollmentHelp() {
+        if (mProgressShowingHelpAnimator != null || mProgressAnimator == null) {
+            return; // already showing or at 0% (no progress bar visible)
+        }
+
+        if (mProgressHidingHelpAnimator != null && mProgressHidingHelpAnimator.isRunning()) {
+            mProgressHidingHelpAnimator.cancel();
+        }
+        mProgressHidingHelpAnimator = null;
+
+        mProgressShowingHelpAnimator = getProgressColorAnimator(
+                mProgressPaint.getColor(), mProgressHelpColor);
+        mProgressShowingHelpAnimator.start();
+    }
+
+    private void hideEnrollmentHelp() {
+        if (mProgressHidingHelpAnimator != null || mProgressShowingHelpAnimator == null) {
+            return; // already hidden or help never shown
+        }
+
+        if (mProgressShowingHelpAnimator != null && mProgressShowingHelpAnimator.isRunning()) {
+            mProgressShowingHelpAnimator.cancel();
+        }
+        mProgressShowingHelpAnimator = null;
+
+        mProgressHidingHelpAnimator = getProgressColorAnimator(
+                mProgressPaint.getColor(), mProgressColor);
+        mProgressHidingHelpAnimator.start();
+    }
+
+    private ValueAnimator getProgressColorAnimator(@ColorInt int from, @ColorInt int to) {
+        final ValueAnimator animator = ValueAnimator.ofObject(
+                ArgbEvaluator.getInstance(), from, to);
+        animator.setDuration(mShortAnimationDuration);
+        animator.addUpdateListener(animation -> {
+            mProgressPaint.setColor((int) animation.getAnimatedValue());
+            mParent.invalidateSelf();
+        });
+        return animator;
+    }
+
     @Override
     public void draw(@NonNull Canvas canvas) {
         canvas.save();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
index 2cdf49d..56d5b31 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -64,4 +64,8 @@
     void onLastStepAcquired() {
         mHandler.post(mFingerprintDrawable::onLastStepAcquired);
     }
+
+    void onEnrollmentHelp() {
+        mHandler.post(mFingerprintDrawable::onEnrollmentHelp);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 54244a1..33fbe7b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -44,6 +44,11 @@
         public void onLastStepAcquired() {
             mView.onLastStepAcquired();
         }
+
+        @Override
+        public void onEnrollmentHelp() {
+            mView.onEnrollmentHelp();
+        }
     };
 
     protected UdfpsEnrollViewController(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
index 07f765b..73e5afe 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
@@ -28,6 +28,10 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -38,6 +42,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -49,13 +55,16 @@
     private static final String TAG = "CommunalController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final String STATE_LIST_FORMAT = "[%s]";
+    private static final AnimationProperties COMMUNAL_ANIMATION_PROPERTIES =
+            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
 
     private final Executor mMainExecutor;
     private final CommunalStateController mCommunalStateController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final KeyguardStateController mKeyguardStateController;
     private final StatusBarStateController mStatusBarStateController;
-    private WeakReference<CommunalSource> mLastSource;
+    private WeakReference<CommunalSource> mCurrentSource;
+    private Optional<ShowRequest> mLastRequest = Optional.empty();
     private int mState;
     private float mQsExpansion;
     private float mShadeExpansion;
@@ -72,12 +81,43 @@
     // Only show communal view when keyguard is showing and not dozing.
     private static final int SHOW_COMMUNAL_VIEW_REQUIRED_STATES = STATE_KEYGUARD_SHOWING;
     private static final int SHOW_COMMUNAL_VIEW_INVALID_STATES =
-            STATE_DOZING | STATE_BOUNCER_SHOWING | STATE_KEYGUARD_OCCLUDED;
+            STATE_DOZING | STATE_KEYGUARD_OCCLUDED;
 
     private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
 
     private ViewController<? extends View> mCommunalViewController;
 
+    private static class ShowRequest {
+        private boolean mShouldShow;
+        private WeakReference<CommunalSource> mSource;
+
+        ShowRequest(boolean shouldShow, WeakReference<CommunalSource> source) {
+            mShouldShow = shouldShow;
+            mSource = source;
+        }
+
+        CommunalSource getSource() {
+            return mSource != null ? mSource.get() : null;
+        }
+
+        boolean shouldShow() {
+            return mShouldShow;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof ShowRequest)) return false;
+            ShowRequest that = (ShowRequest) o;
+            return mShouldShow == that.mShouldShow && Objects.equals(getSource(), that.getSource());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mShouldShow, mSource);
+        }
+    }
+
     private KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
             new KeyguardUpdateMonitorCallback() {
                 @Override
@@ -160,10 +200,23 @@
                 statusBarState, keyguardFadingAway, goingToFullShade, oldStatusBarState);
     }
 
-    @Override
-    public void init() {
-        super.init();
+    /**
+     * Set keyguard status view alpha.
+     */
+    public void setAlpha(float alpha) {
+        if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
+            mView.setAlpha(alpha);
 
+            // Some communal view implementations, such as SurfaceViews, do not behave correctly
+            // inheriting the alpha of their parent. Directly set child alpha here to work around
+            // this.
+            for (int i = mView.getChildCount() - 1; i >= 0; --i) {
+                mView.getChildAt(i).setAlpha(alpha);
+            }
+        }
+    }
+    @Override
+    public void onInit() {
         setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
         setState(STATE_DOZING, mStatusBarStateController.isDozing());
     }
@@ -238,18 +291,26 @@
     }
 
     private void showSource() {
+        final ShowRequest request = new ShowRequest(
+                (mState & SHOW_COMMUNAL_VIEW_REQUIRED_STATES) == SHOW_COMMUNAL_VIEW_REQUIRED_STATES
+                    && (mState & SHOW_COMMUNAL_VIEW_INVALID_STATES) == 0
+                    && mCurrentSource != null,
+                mCurrentSource);
+
+        if (mLastRequest.isPresent() && Objects.equals(mLastRequest.get(), request)) {
+            return;
+        }
+
+        mLastRequest = Optional.of(request);
+
         // Make sure all necessary states are present for showing communal and all invalid states
         // are absent
         mMainExecutor.execute(() -> {
-            final CommunalSource currentSource = mLastSource != null ? mLastSource.get() : null;
-
             if (DEBUG) {
-                Log.d(TAG, "showSource. currentSource:" + currentSource);
+                Log.d(TAG, "showSource. currentSource:" + request.getSource());
             }
 
-            if ((mState & SHOW_COMMUNAL_VIEW_REQUIRED_STATES) == SHOW_COMMUNAL_VIEW_REQUIRED_STATES
-                    && (mState & SHOW_COMMUNAL_VIEW_INVALID_STATES) == 0
-                    && currentSource != null) {
+            if (request.shouldShow()) {
                 mView.removeAllViews();
 
                 // Make view visible.
@@ -258,7 +319,7 @@
                 final Context context = mView.getContext();
 
                 final ListenableFuture<CommunalSource.CommunalViewResult> listenableFuture =
-                        currentSource.requestCommunalView(context);
+                        request.getSource().requestCommunalView(context);
 
                 if (listenableFuture == null) {
                     Log.e(TAG, "could not request communal view");
@@ -293,11 +354,19 @@
      * @param source The new {@link CommunalSource}, {@code null} if not set.
      */
     public void show(WeakReference<CommunalSource> source) {
-        mLastSource = source;
+        mCurrentSource = source;
         showSource();
     }
 
     /**
+     * Update position of the view with an optional animation
+     */
+    public void updatePosition(int y, boolean animate) {
+        PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, COMMUNAL_ANIMATION_PROPERTIES,
+                animate);
+    }
+
+    /**
      * Invoked when the quick settings is expanded.
      * @param expansionFraction the percentage the QS shade has been expanded.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithm.java
new file mode 100644
index 0000000..424da0b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithm.java
@@ -0,0 +1,74 @@
+/*
+ * 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.communal;
+
+import android.util.Log;
+
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
+
+/**
+ * {@link CommunalHostViewPositionAlgorithm} calculates the position of the communal view given
+ * input such as the notification panel position.
+ */
+public class CommunalHostViewPositionAlgorithm {
+    private static final String TAG = "CommunalPositionAlg";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    /**
+     * @see NotificationPanelViewController#getExpandedFraction()
+     */
+    private float mPanelExpansion;
+
+    /**
+     * Height of {@link CommunalHostView}.
+     */
+    private int mCommunalHeight;
+
+    /**
+     * A data container for the result of the position algorithm.
+     */
+    public static class Result {
+        /**
+         * The y translation of the clock.
+         */
+        public int communalY;
+    }
+
+    /**
+     * Sets the conditions under which the result should be calculated from.
+     * @param panelExpansion The percentage the keyguard panel has been moved upwards.
+     * @param communalHeight The height of the communal panel.
+     */
+    public void setup(float panelExpansion, int communalHeight) {
+        if (DEBUG) {
+            Log.d(TAG, "setup. panelExpansion:" + panelExpansion);
+        }
+        mPanelExpansion = panelExpansion;
+        mCommunalHeight = communalHeight;
+    }
+
+    /**
+     * Calculates the position based on factors input through {link {@link #setup(float, int)}}.
+     * @param result The resulting calculations.
+     */
+    public void run(Result result) {
+        // The panel expansion relates to the keyguard expansion. At full expansion, the communal
+        // view should be aligned at the top (0). Otherwise, it should be shifted offscreen by the
+        // unexpanded amount.
+        result.communalY = (int) ((1 - mPanelExpansion) * -mCommunalHeight);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.java b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.java
index d8bf2dc..b8070ab 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.java
@@ -38,6 +38,7 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -78,6 +79,45 @@
         }
     }
 
+    static class Request {
+        private final int mWidth;
+        private final int mHeight;
+        private final int mDisplayId;
+        private final IBinder mHostToken;
+
+        Request(int width, int height, int displayId, IBinder hostToken) {
+            mWidth = width;
+            mHeight = height;
+            mDisplayId = displayId;
+            mHostToken = hostToken;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof Request)) return false;
+            Request request = (Request) o;
+            return mWidth == request.mWidth && mHeight == request.mHeight
+                    && mDisplayId == request.mDisplayId && Objects.equals(mHostToken,
+                    request.mHostToken);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mWidth, mHeight, mDisplayId, mHostToken);
+        }
+
+        @Override
+        public String toString() {
+            return "Request{"
+                    + "mWidth=" + mWidth
+                    + ", mHeight=" + mHeight
+                    + ", mDisplayId=" + mDisplayId
+                    + ", mHostToken=" + mHostToken
+                    + '}';
+        }
+    }
+
     // mConnected is initialized to true as it is presumed instances are constructed with valid
     // proxies. The source can never be reconnected once the proxy has died. Once this value
     // becomes false, the source will always report disconnected to registering callbacks.
@@ -148,18 +188,15 @@
      * Called internally to request a new {@link android.view.SurfaceControlViewHost.SurfacePackage}
      * for showing communal content.
      *
-     * @param hostToken The HostToken necessary to generate a {@link SurfaceControlViewHost}.
-     * @param displayId The id of the display the surface will be shown on.
-     * @param width     The width of the surface.
-     * @param height    The height of the surface.
+     * @param request A request with the parameters for the new communal surface.
      * @return A future that returns the resulting
      * {@link android.view.SurfaceControlViewHost.SurfacePackage}.
      */
     protected ListenableFuture<SurfaceControlViewHost.SurfacePackage> requestCommunalSurface(
-            IBinder hostToken, int displayId, int width, int height) {
+            Request request) {
         return CallbackToFutureAdapter.getFuture(completer -> {
-            mSourceProxy.getCommunalSurface(hostToken, width, height, displayId,
-                    new ICommunalSurfaceCallback.Stub() {
+            mSourceProxy.getCommunalSurface(request.mHostToken, request.mWidth, request.mHeight,
+                    request.mDisplayId, new ICommunalSurfaceCallback.Stub() {
                         @Override
                         public void onSurface(
                                 SurfaceControlViewHost.SurfacePackage surfacePackage) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.java b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.java
index 89239cf..0de5029 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.content.res.Resources;
+import android.graphics.PixelFormat;
 import android.graphics.Region;
 import android.util.Log;
 import android.view.IWindow;
@@ -36,6 +37,7 @@
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.util.Optional;
 import java.util.concurrent.Executor;
 
 /**
@@ -63,6 +65,8 @@
 
     private int mCurrentState;
 
+    private Optional<CommunalSourceImpl.Request> mLastRequest = Optional.empty();
+
     // The current in-flight request for a surface package.
     private ListenableFuture<SurfaceControlViewHost.SurfacePackage> mCurrentSurfaceFuture;
 
@@ -99,6 +103,9 @@
 
             mSurfaceViewTouchableRegion.set(left, top + topMargin, right, bottom - bottomMargin);
             updateTouchExclusion();
+
+            // Trigger showing (or hiding) surface based on new dimensions.
+            showSurface();
         }
     };
 
@@ -124,8 +131,8 @@
     }
 
     @Override
-    public void init() {
-        super.init();
+    protected void onInit() {
+        mView.getHolder().setFormat(PixelFormat.TRANSPARENT);
         mView.getHolder().addCallback(mSurfaceHolderCallback);
         mView.addOnLayoutChangeListener(mOnLayoutChangeListener);
     }
@@ -148,7 +155,7 @@
 
         mCurrentState = newState;
 
-        showSurface(newState == STATE_CAN_SHOW_SURFACE);
+        showSurface();
 
         updateTouchExclusion();
     }
@@ -164,30 +171,36 @@
             mNotificationShadeWindowController.setTouchExclusionRegion(emptyRegion);
             emptyRegion.recycle();
         }
-        // TODO(b/197036940): This is no longer necessary once the surface view is not on top of the
-        // z-order.
-        mView.setZOrderOnTop(excludeTouches);
     }
 
-    private void showSurface(boolean show) {
+    private void showSurface() {
         mView.setWillNotDraw(false);
 
-        if (!show) {
+        if (mCurrentState != STATE_CAN_SHOW_SURFACE) {
             // If the surface is no longer showing, cancel any in-flight requests.
             if (mCurrentSurfaceFuture != null) {
                 mCurrentSurfaceFuture.cancel(true);
                 mCurrentSurfaceFuture = null;
             }
 
+            mLastRequest = Optional.empty();
             mView.setWillNotDraw(true);
             return;
         }
 
+        final CommunalSourceImpl.Request request = new CommunalSourceImpl.Request(
+                mView.getMeasuredWidth(), mView.getMeasuredHeight(),
+                mView.getDisplay().getDisplayId(), mView.getHostToken());
+
+        if (mLastRequest.isPresent() && mLastRequest.get().equals(request)) {
+            return;
+        }
+
+        mLastRequest = Optional.of(request);
+
         // Since this method is only called when the state has changed, mCurrentSurfaceFuture should
         // be null here.
-        mCurrentSurfaceFuture = mSource.requestCommunalSurface(mView.getHostToken(),
-                        mView.getDisplay().getDisplayId(), mView.getMeasuredWidth(),
-                        mView.getMeasuredHeight());
+        mCurrentSurfaceFuture = mSource.requestCommunalSurface(request);
 
         mCurrentSurfaceFuture.addListener(new Runnable() {
             @Override
@@ -208,7 +221,6 @@
 
                     if (surfacePackage != null) {
                         mView.setChildSurfacePackage(surfacePackage);
-                        mView.setZOrderOnTop(true);
                         mView.postInvalidate();
                         mCommunalStateController.setCommunalViewShowing(true);
                     } else {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index c093219..e7974bc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -196,9 +196,11 @@
     @SysUISingleton
     @Provides
     static ThemeOverlayApplier provideThemeOverlayManager(Context context,
-            @Background Executor bgExecutor, OverlayManager overlayManager,
+            @Background Executor bgExecutor,
+            @Main Executor mainExecutor,
+            OverlayManager overlayManager,
             DumpManager dumpManager) {
-        return new ThemeOverlayApplier(overlayManager, bgExecutor,
+        return new ThemeOverlayApplier(overlayManager, bgExecutor, mainExecutor,
                 context.getString(R.string.launcher_overlayable_package),
                 context.getString(R.string.themepicker_overlayable_package), dumpManager);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a85800b..7eedb3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -140,6 +140,10 @@
         mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
 
         updateResources();
+        Configuration config = mContext.getResources().getConfiguration();
+        setDatePrivacyContainersWidth(config.orientation == Configuration.ORIENTATION_LANDSCAPE);
+        setSecurityHeaderContainerVisibility(
+                config.orientation == Configuration.ORIENTATION_LANDSCAPE);
 
         // QS will always show the estimate, and BatteryMeterView handles the case where
         // it's unavailable or charging
@@ -189,6 +193,8 @@
         super.onConfigurationChanged(newConfig);
         updateResources();
         setDatePrivacyContainersWidth(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
+        setSecurityHeaderContainerVisibility(
+                newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
     }
 
     @Override
@@ -209,6 +215,10 @@
         mPrivacyContainer.setLayoutParams(lp);
     }
 
+    private void setSecurityHeaderContainerVisibility(boolean landscape) {
+        mSecurityHeaderView.setVisibility(landscape ? VISIBLE : GONE);
+    }
+
     private void updateBatteryMode() {
         if (mConfigShowBatteryEstimate && !mHasCenterCutout) {
             mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index dfe78de8..62fa3d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -391,7 +391,8 @@
             level += 1;
             numLevels += 1;
         }
-        return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, false);
+        return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON,
+                !isMobileDataEnabled());
     }
 
     Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 3fbdc23..8def475 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -33,6 +33,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -261,6 +262,7 @@
     private Bitmap mScreenBitmap;
     private SaveImageInBackgroundTask mSaveInBgTask;
     private boolean mScreenshotTakenInPortrait;
+    private boolean mBlockAttach;
 
     private Animator mScreenshotAnimation;
     private RequestCallback mCurrentRequestCallback;
@@ -731,6 +733,7 @@
                     new ViewTreeObserver.OnWindowAttachListener() {
                         @Override
                         public void onWindowAttached() {
+                            mBlockAttach = false;
                             decorView.getViewTreeObserver().removeOnWindowAttachListener(this);
                             action.run();
                         }
@@ -747,14 +750,16 @@
         mWindow.setContentView(contentView);
     }
 
+    @MainThread
     private void attachWindow() {
         View decorView = mWindow.getDecorView();
-        if (decorView.isAttachedToWindow()) {
+        if (decorView.isAttachedToWindow() || mBlockAttach) {
             return;
         }
         if (DEBUG_WINDOW) {
             Log.d(TAG, "attachWindow");
         }
+        mBlockAttach = true;
         mWindowManager.addView(decorView, mWindowLayoutParams);
         decorView.requestApplyInsets();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 90158c32..aba1a24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -291,8 +291,8 @@
         default void showAuthenticationDialog(PromptInfo promptInfo,
                 IBiometricSysuiReceiver receiver,
                 int[] sensorIds, boolean credentialAllowed,
-                boolean requireConfirmation, int userId, String opPackageName,
-                long operationId, @BiometricMultiSensorMode int multiSensorConfig) {
+                boolean requireConfirmation, int userId, long operationId, String opPackageName,
+                long requestId, @BiometricMultiSensorMode int multiSensorConfig) {
         }
 
         /** @see IStatusBar#onBiometricAuthenticated() */
@@ -845,7 +845,7 @@
     @Override
     public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver,
             int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
-            int userId, String opPackageName, long operationId,
+            int userId, long operationId, String opPackageName, long requestId,
             @BiometricMultiSensorMode int multiSensorConfig) {
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
@@ -857,6 +857,7 @@
             args.argi1 = userId;
             args.arg6 = opPackageName;
             args.arg7 = operationId;
+            args.arg8 = requestId;
             args.argi2 = multiSensorConfig;
             mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
                     .sendToTarget();
@@ -1315,8 +1316,9 @@
                                 (boolean) someArgs.arg4 /* credentialAllowed */,
                                 (boolean) someArgs.arg5 /* requireConfirmation */,
                                 someArgs.argi1 /* userId */,
-                                (String) someArgs.arg6 /* opPackageName */,
                                 (long) someArgs.arg7 /* operationId */,
+                                (String) someArgs.arg6 /* opPackageName */,
+                                (long) someArgs.arg8 /* requestId */,
                                 someArgs.argi2 /* multiSensorConfig */);
                     }
                     someArgs.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index af9d921..893aa6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -25,6 +25,7 @@
 import android.animation.ValueAnimator;
 import android.content.res.Resources;
 import android.hardware.biometrics.BiometricSourceType;
+import android.util.MathUtils;
 import android.view.View;
 
 import androidx.annotation.NonNull;
@@ -40,6 +41,9 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -57,6 +61,21 @@
 
 /** View Controller for {@link com.android.systemui.statusbar.phone.KeyguardStatusBarView}. */
 public class KeyguardStatusBarViewController extends ViewController<KeyguardStatusBarView> {
+    private static final AnimationProperties KEYGUARD_HUN_PROPERTIES =
+            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+
+    private float mKeyguardHeadsUpShowingAmount = 0.0f;
+    private final AnimatableProperty mHeadsUpShowingAmountAnimation = AnimatableProperty.from(
+            "KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
+            (view, aFloat) -> {
+                mKeyguardHeadsUpShowingAmount = aFloat;
+                updateViewState();
+            },
+            view -> mKeyguardHeadsUpShowingAmount,
+            R.id.keyguard_hun_animator_tag,
+            R.id.keyguard_hun_animator_end_tag,
+            R.id.keyguard_hun_animator_start_tag);
+
     private final CarrierTextController mCarrierTextController;
     private final ConfigurationController mConfigurationController;
     private final SystemStatusAnimationScheduler mAnimationScheduler;
@@ -65,7 +84,8 @@
     private final StatusBarIconController mStatusBarIconController;
     private final StatusBarIconController.TintedIconManager.Factory mTintedIconManagerFactory;
     private final BatteryMeterViewController mBatteryMeterViewController;
-    private final ViewStateProvider mViewStateProvider;
+    private final NotificationPanelViewController.NotificationPanelViewStateProvider
+            mNotificationPanelViewStateProvider;
     private final KeyguardStateController mKeyguardStateController;
     private final KeyguardBypassController mKeyguardBypassController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -176,6 +196,7 @@
             };
 
     private final List<String> mBlockedIcons;
+    private final int mNotificationsHeaderCollideDistance;
 
     private boolean mBatteryListening;
     private StatusBarIconController.TintedIconManager mTintedIconManager;
@@ -193,6 +214,7 @@
     private boolean mDelayShowingKeyguardStatusBar;
     private int mStatusBarState;
     private boolean mDozing;
+    private boolean mShowingKeyguardHeadsUp;
 
     @Inject
     public KeyguardStatusBarViewController(
@@ -205,7 +227,8 @@
             StatusBarIconController statusBarIconController,
             StatusBarIconController.TintedIconManager.Factory tintedIconManagerFactory,
             BatteryMeterViewController batteryMeterViewController,
-            ViewStateProvider viewStateProvider,
+            NotificationPanelViewController.NotificationPanelViewStateProvider
+                    notificationPanelViewStateProvider,
             KeyguardStateController keyguardStateController,
             KeyguardBypassController bypassController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -220,7 +243,7 @@
         mStatusBarIconController = statusBarIconController;
         mTintedIconManagerFactory = tintedIconManagerFactory;
         mBatteryMeterViewController = batteryMeterViewController;
-        mViewStateProvider = viewStateProvider;
+        mNotificationPanelViewStateProvider = notificationPanelViewStateProvider;
         mKeyguardStateController = keyguardStateController;
         mKeyguardBypassController = bypassController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -245,6 +268,8 @@
                 r.getString(com.android.internal.R.string.status_bar_volume),
                 r.getString(com.android.internal.R.string.status_bar_alarm_clock),
                 r.getString(com.android.internal.R.string.status_bar_call_strength)));
+        mNotificationsHeaderCollideDistance = r.getDimensionPixelSize(
+                R.dimen.header_notifications_collide_distance);
     }
 
     @Override
@@ -355,16 +380,20 @@
     }
 
     /**
-     * Updates the {@link KeyguardStatusBarView} state based on what the {@link ViewStateProvider}
-     * and other controllers provide.
+     * Updates the {@link KeyguardStatusBarView} state based on what the
+     * {@link NotificationPanelViewController.NotificationPanelViewStateProvider} and other
+     * controllers provide.
      */
     public void updateViewState() {
-        ViewState newViewState = mViewStateProvider.provideViewState();
         if (!isKeyguardShowing()) {
             return;
         }
 
-        float newAlpha = newViewState.mAlpha * mKeyguardStatusBarAnimateAlpha;
+        float alphaQsExpansion = 1 - Math.min(
+                1, mNotificationPanelViewStateProvider.getQsExpansionFraction() * 2);
+        float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
+                * mKeyguardStatusBarAnimateAlpha
+                * (1.0f - mKeyguardHeadsUpShowingAmount);
 
         boolean hideForBypass =
                 mFirstBypassAttempt && mKeyguardUpdateMonitor.shouldListenForFace()
@@ -383,6 +412,54 @@
         mView.setVisibility(visibility);
     }
 
+    /**
+     * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
+     * during swiping up.
+     */
+    private float getKeyguardContentsAlpha() {
+        float alpha;
+        if (isKeyguardShowing()) {
+            // When on Keyguard, we hide the header as soon as we expanded close enough to the
+            // header
+            alpha = mNotificationPanelViewStateProvider.getPanelViewExpandedHeight()
+                    / (mView.getHeight() + mNotificationsHeaderCollideDistance);
+        } else {
+            // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
+            // soon as we start translating the stack.
+            alpha = mNotificationPanelViewStateProvider.getPanelViewExpandedHeight()
+                    / mView.getHeight();
+        }
+        alpha = MathUtils.saturate(alpha);
+        alpha = (float) Math.pow(alpha, 0.75);
+        return alpha;
+    }
+
+    /**
+     * Update {@link KeyguardStatusBarView}'s visibility based on whether keyguard is showing and
+     * whether heads up is visible.
+     */
+    public void updateForHeadsUp() {
+        updateForHeadsUp(true);
+    }
+
+    void updateForHeadsUp(boolean animate) {
+        boolean showingKeyguardHeadsUp =
+                isKeyguardShowing() && mNotificationPanelViewStateProvider.shouldHeadsUpBeVisible();
+        if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
+            mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
+            if (isKeyguardShowing()) {
+                PropertyAnimator.setProperty(
+                        mView,
+                        mHeadsUpShowingAmountAnimation,
+                        showingKeyguardHeadsUp ? 1.0f : 0.0f,
+                        KEYGUARD_HUN_PROPERTIES,
+                        animate);
+            } else {
+                PropertyAnimator.applyImmediately(mView, mHeadsUpShowingAmountAnimation, 0.0f);
+            }
+        }
+    }
+
     private boolean isKeyguardShowing() {
         return mStatusBarState == KEYGUARD;
     }
@@ -394,18 +471,4 @@
         mView.dump(fd, pw, args);
     }
 
-    /** An interface that provides the desired state of {@link KeyguardStatusBarView}. */
-    public interface ViewStateProvider {
-        /** Provides the state. */
-        ViewState provideViewState();
-    }
-
-    /** A POJO for the desired state of {@link KeyguardStatusBarView}. */
-    static class ViewState {
-        final float mAlpha;
-
-        ViewState(float alpha) {
-            this.mAlpha = alpha;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 0faf520..f0a35d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -108,6 +108,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.communal.CommunalHostView;
 import com.android.systemui.communal.CommunalHostViewController;
+import com.android.systemui.communal.CommunalHostViewPositionAlgorithm;
 import com.android.systemui.communal.CommunalSource;
 import com.android.systemui.communal.CommunalSourceMonitor;
 import com.android.systemui.communal.CommunalStateController;
@@ -118,6 +119,7 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.idle.IdleHostView;
 import com.android.systemui.idle.IdleHostViewController;
 import com.android.systemui.idle.dagger.IdleViewComponent;
 import com.android.systemui.media.KeyguardMediaController;
@@ -190,7 +192,6 @@
 import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
-import java.util.function.Function;
 
 import javax.inject.Inject;
 import javax.inject.Provider;
@@ -271,17 +272,6 @@
     private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
     private static final Rect EMPTY_RECT = new Rect();
 
-    private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from(
-            "KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
-            (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat),
-            (Function<NotificationPanelView, Float>) notificationPanelView ->
-                    getKeyguardHeadsUpShowingAmount(),
-            R.id.keyguard_hun_animator_tag, R.id.keyguard_hun_animator_end_tag,
-            R.id.keyguard_hun_animator_start_tag);
-    private static final AnimationProperties
-            KEYGUARD_HUN_PROPERTIES =
-            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-
     private final LayoutInflater mLayoutInflater;
     private final PowerManager mPowerManager;
     private final AccessibilityManager mAccessibilityManager;
@@ -380,7 +370,6 @@
     private FlingAnimationUtils mFlingAnimationUtils;
     private int mStatusBarMinHeight;
     private int mStatusBarHeaderHeightKeyguard;
-    private int mNotificationsHeaderCollideDistance;
     private float mOverStretchAmount;
     private float mDownX;
     private float mDownY;
@@ -394,6 +383,12 @@
     private final KeyguardClockPositionAlgorithm.Result
             mClockPositionResult =
             new KeyguardClockPositionAlgorithm.Result();
+    private final CommunalHostViewPositionAlgorithm
+            mCommunalPositionAlgorithm =
+            new CommunalHostViewPositionAlgorithm();
+    private final CommunalHostViewPositionAlgorithm.Result
+            mCommunalPositionResult =
+            new CommunalHostViewPositionAlgorithm.Result();
     private boolean mIsExpanding;
 
     private boolean mBlockTouches;
@@ -532,8 +527,6 @@
     private int mDarkIconSize;
     private int mHeadsUpInset;
     private boolean mHeadsUpPinnedMode;
-    private float mKeyguardHeadsUpShowingAmount = 0.0f;
-    private boolean mShowingKeyguardHeadsUp;
     private boolean mAllowExpandForSmallExpansion;
     private Runnable mExpandAfterLayoutRunnable;
 
@@ -702,17 +695,6 @@
         }
     };
 
-    private final KeyguardStatusBarViewController.ViewStateProvider mViewStateProvider =
-            new KeyguardStatusBarViewController.ViewStateProvider() {
-                @Override
-                public KeyguardStatusBarViewController.ViewState provideViewState() {
-                    float alphaQsExpansion = 1 - Math.min(1, computeQsExpansionFraction() * 2);
-                    float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
-                            * (1.0f - mKeyguardHeadsUpShowingAmount);
-                    return new KeyguardStatusBarViewController.ViewState(newAlpha);
-                }
-            };
-
     @Inject
     public NotificationPanelViewController(NotificationPanelView view,
             @Main Resources resources,
@@ -912,7 +894,7 @@
         mKeyguardStatusBarViewController =
                 mKeyguardStatusBarViewComponentFactory.build(
                         mKeyguardStatusBar,
-                        mViewStateProvider)
+                        mNotificationPanelViewStateProvider)
                         .getKeyguardStatusBarViewController();
         mKeyguardStatusBarViewController.init();
 
@@ -921,10 +903,20 @@
         mIdleHostViewController = idleViewComponent.getIdleHostViewController();
         mIdleHostViewController.init();
 
+        if (mCommunalView != null) {
+            CommunalViewComponent communalViewComponent =
+                    mCommunalViewComponentFactory.build(mCommunalView);
+            mCommunalViewController =
+                    communalViewComponent.getCommunalHostViewController();
+            mCommunalViewController.init();
+        }
+
+
         updateViewControllers(
                 mView.findViewById(R.id.keyguard_status_view),
                 userAvatarView,
                 keyguardUserSwitcherView,
+                mView.findViewById(R.id.idle_host_view),
                 mCommunalView);
         mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
         NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
@@ -952,7 +944,7 @@
         mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
             @Override
             public void onFullyHiddenChanged(boolean isFullyHidden) {
-                updateKeyguardStatusBarForHeadsUp();
+                mKeyguardStatusBarViewController.updateForHeadsUp();
             }
 
             @Override
@@ -989,8 +981,6 @@
         mStatusBarHeaderHeightKeyguard = mResources.getDimensionPixelSize(
                 R.dimen.status_bar_header_height_keyguard);
         mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
-        mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize(
-                R.dimen.header_notifications_collide_distance);
         mClockPositionAlgorithm.loadDimens(mResources);
         mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold);
         mPositionMinSideMargin = mResources.getDimensionPixelSize(
@@ -1017,6 +1007,7 @@
     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
             UserAvatarView userAvatarView,
             KeyguardUserSwitcherView keyguardUserSwitcherView,
+            IdleHostView idleHostView,
             CommunalHostView communalView) {
         // Re-associate the KeyguardStatusViewController
         KeyguardStatusViewComponent statusViewComponent =
@@ -1024,13 +1015,9 @@
         mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
         mKeyguardStatusViewController.init();
 
-        if (communalView != null) {
-            CommunalViewComponent communalViewComponent =
-                    mCommunalViewComponentFactory.build(communalView);
-            mCommunalViewController =
-                    communalViewComponent.getCommunalHostViewController();
-            mCommunalViewController.init();
-        }
+        IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(idleHostView);
+        mIdleHostViewController = idleViewComponent.getIdleHostViewController();
+        mIdleHostViewController.init();
 
         if (mKeyguardUserSwitcherController != null) {
             // Try to close the switcher so that callbacks are triggered if necessary.
@@ -1195,7 +1182,7 @@
 
         mBigClockContainer.removeAllViews();
         updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
-                keyguardUserSwitcherView, mCommunalView);
+                keyguardUserSwitcherView, mView.findViewById(R.id.idle_host_view), mCommunalView);
 
         // Update keyguard bottom area
         int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1335,6 +1322,11 @@
         boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
         int stackScrollerPadding;
         boolean onKeyguard = isOnKeyguard();
+
+        if (onKeyguard) {
+            updateCommunalViewAppearance();
+        }
+
         if (onKeyguard || forceClockUpdate) {
             updateClockAppearance();
         }
@@ -1360,6 +1352,22 @@
         mAnimateNextPositionUpdate = false;
     }
 
+    private void updateCommunalViewAppearance() {
+        if (mCommunalViewController == null) {
+            return;
+        }
+
+        float expandedFraction =
+                mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
+                        ? 1.0f : getExpandedFraction();
+        mCommunalPositionAlgorithm.setup(expandedFraction, mCommunalView.getHeight());
+        mCommunalPositionAlgorithm.run(mCommunalPositionResult);
+        boolean animate =
+                mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending()
+                        || mAnimateNextPositionUpdate;
+        mCommunalViewController.updatePosition(mCommunalPositionResult.communalY, animate);
+    }
+
     private void updateClockAppearance() {
         int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
         boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
@@ -1574,6 +1582,12 @@
         return true;
     }
 
+    private void updateCommunal() {
+        if (mCommunalViewController != null) {
+            mCommunalViewController.setAlpha(mKeyguardOnlyContentAlpha);
+        }
+    }
+
     private void updateClock() {
         float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha;
         mKeyguardStatusViewController.setAlpha(alpha);
@@ -2675,6 +2689,7 @@
             updateKeyguardBottomAreaAlpha();
         }
         updateClock();
+        updateCommunal();
     }
 
     private void trackMovement(MotionEvent event) {
@@ -3027,30 +3042,6 @@
         return Math.min(0, translation);
     }
 
-    /**
-     * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
-     * during swiping up
-     */
-    private float getKeyguardContentsAlpha() {
-        float alpha;
-        if (mBarState == KEYGUARD) {
-
-            // When on Keyguard, we hide the header as soon as we expanded close enough to the
-            // header
-            alpha =
-                    getExpandedHeight() / (mKeyguardStatusBar.getHeight()
-                            + mNotificationsHeaderCollideDistance);
-        } else {
-
-            // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
-            // soon as we start translating the stack.
-            alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight();
-        }
-        alpha = MathUtils.saturate(alpha);
-        alpha = (float) Math.pow(alpha, 0.75);
-        return alpha;
-    }
-
     private void updateKeyguardBottomAreaAlpha() {
         // There are two possible panel expansion behaviors:
         // • User dragging up to unlock: we want to fade out as quick as possible
@@ -3346,31 +3337,6 @@
         mPanelAlphaEndAction = r;
     }
 
-    private void updateKeyguardStatusBarForHeadsUp() {
-        boolean
-                showingKeyguardHeadsUp =
-                mKeyguardShowing && mHeadsUpAppearanceController.shouldBeVisible();
-        if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
-            mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
-            if (mKeyguardShowing) {
-                PropertyAnimator.setProperty(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
-                        showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
-                        true /* animate */);
-            } else {
-                PropertyAnimator.applyImmediately(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
-            }
-        }
-    }
-
-    private void setKeyguardHeadsUpShowingAmount(float amount) {
-        mKeyguardHeadsUpShowingAmount = amount;
-        mKeyguardStatusBarViewController.updateViewState();
-    }
-
-    private float getKeyguardHeadsUpShowingAmount() {
-        return mKeyguardHeadsUpShowingAmount;
-    }
-
     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
         mHeadsUpAnimatingAway = headsUpAnimatingAway;
         mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway);
@@ -4351,7 +4317,7 @@
             updateGestureExclusionRect();
             mHeadsUpPinnedMode = inPinnedMode;
             updateHeadsUpVisibility();
-            updateKeyguardStatusBarForHeadsUp();
+            mKeyguardStatusBarViewController.updateForHeadsUp();
         }
 
         @Override
@@ -4519,7 +4485,7 @@
                     }
                 }
             }
-            updateKeyguardStatusBarForHeadsUp();
+            mKeyguardStatusBarViewController.updateForHeadsUp();
             if (keyguardShowing) {
                 updateDozingVisibilities(false /* animate */);
             }
@@ -4545,6 +4511,43 @@
     }
 
     /**
+     * An interface that provides the current state of the notification panel and related views,
+     * which is needed to calculate {@link KeyguardStatusBarView}'s state in
+     * {@link KeyguardStatusBarViewController}.
+     */
+    public interface NotificationPanelViewStateProvider {
+        /** Returns the expanded height of the panel view. */
+        float getPanelViewExpandedHeight();
+        /** Returns the fraction of QS that's expanded. */
+        float getQsExpansionFraction();
+        /**
+         * Returns true if heads up should be visible.
+         *
+         * TODO(b/138786270): If HeadsUpAppearanceController was injectable, we could inject it into
+         * {@link KeyguardStatusBarViewController} and remove this method.
+         */
+        boolean shouldHeadsUpBeVisible();
+    }
+
+    private final NotificationPanelViewStateProvider mNotificationPanelViewStateProvider =
+            new NotificationPanelViewStateProvider() {
+                @Override
+                public float getPanelViewExpandedHeight() {
+                    return getExpandedHeight();
+                }
+
+                @Override
+                public float getQsExpansionFraction() {
+                    return computeQsExpansionFraction();
+                }
+
+                @Override
+                public boolean shouldHeadsUpBeVisible() {
+                    return mHeadsUpAppearanceController.shouldBeVisible();
+                }
+            };
+
+    /**
      * Reconfigures the shade to show the AOD UI (clock, smartspace, etc). This is called by the
      * screen off animation controller in order to animate in AOD without "actually" fully switching
      * to the KEYGUARD state, which is a heavy transition that causes jank as 10+ files react to the
@@ -4616,8 +4619,6 @@
             mStatusBarStateController.removeCallback(mStatusBarStateListener);
             mConfigurationController.removeCallback(mConfigurationListener);
             mCommunalSourceMonitor.removeCallback(mCommunalSourceMonitorCallback);
-            // Clear source when detached.
-            setCommunalSource(null /*source*/);
             mFalsingManager.removeTapListener(mFalsingTapListener);
             mCommunalStateController.removeCallback(mCommunalStateCallback);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index c3b4fbe..fe0b970 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -132,14 +132,18 @@
     /* Target package for each overlay category. */
     private final Map<String, String> mCategoryToTargetPackage = new ArrayMap<>();
     private final OverlayManager mOverlayManager;
-    private final Executor mExecutor;
+    private final Executor mBgExecutor;
+    private final Executor mMainExecutor;
     private final String mLauncherPackage;
     private final String mThemePickerPackage;
 
-    public ThemeOverlayApplier(OverlayManager overlayManager, Executor executor,
+    public ThemeOverlayApplier(OverlayManager overlayManager,
+            Executor bgExecutor,
+            Executor mainExecutor,
             String launcherPackage, String themePickerPackage, DumpManager dumpManager) {
         mOverlayManager = overlayManager;
-        mExecutor = executor;
+        mBgExecutor = bgExecutor;
+        mMainExecutor = mainExecutor;
         mLauncherPackage = launcherPackage;
         mThemePickerPackage = themePickerPackage;
         mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet(
@@ -170,12 +174,13 @@
      * Apply the set of overlay packages to the set of {@code UserHandle}s provided. Overlays that
      * affect sysui will also be applied to the system user.
      */
-    void applyCurrentUserOverlays(
+    public void applyCurrentUserOverlays(
             Map<String, OverlayIdentifier> categoryToPackage,
             FabricatedOverlay[] pendingCreation,
             int currentUser,
-            Set<UserHandle> managedProfiles) {
-        mExecutor.execute(() -> {
+            Set<UserHandle> managedProfiles,
+            Runnable onOverlaysApplied) {
+        mBgExecutor.execute(() -> {
 
             // Disable all overlays that have not been specified in the user setting.
             final Set<String> overlayCategoriesToDisable = new HashSet<>(THEME_CATEGORIES);
@@ -221,6 +226,7 @@
 
             try {
                 mOverlayManager.commit(transaction.build());
+                mMainExecutor.execute(onOverlaysApplied);
             } catch (SecurityException | IllegalStateException e) {
                 Log.e(TAG, "setEnabled failed", e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 377a7e6..a5871f0 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -526,16 +526,16 @@
                     .map(key -> key + " -> " + categoryToPackage.get(key)).collect(
                             Collectors.joining(", ")));
         }
+        Runnable overlaysAppliedRunnable = () -> onOverlaysApplied();
         if (mNeedsOverlayCreation) {
             mNeedsOverlayCreation = false;
             mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
                     mSecondaryOverlay, mNeutralOverlay
-            }, currentUser, managedProfiles);
+            }, currentUser, managedProfiles, overlaysAppliedRunnable);
         } else {
             mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
-                    managedProfiles);
+                    managedProfiles, overlaysAppliedRunnable);
         }
-        onOverlaysApplied();
     }
 
     protected void onOverlaysApplied() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 9283403..3c3cc64 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -58,6 +58,10 @@
         }
 
         override fun onTransitionStarted() {
+            // When unfolding the view is added earlier, add view for folding case
+            if (scrimView == null) {
+                addOverlayView()
+            }
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 39d5314..8dd5d6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -565,8 +565,9 @@
                 credentialAllowed,
                 true /* requireConfirmation */,
                 0 /* userId */,
-                "testPackage",
                 0 /* operationId */,
+                "testPackage",
+                1 /* requestId */,
                 BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT);
     }
 
@@ -612,7 +613,7 @@
         @Override
         protected AuthDialog buildDialog(PromptInfo promptInfo,
                 boolean requireConfirmation, int userId, int[] sensorIds, boolean credentialAllowed,
-                String opPackageName, boolean skipIntro, long operationId,
+                String opPackageName, boolean skipIntro, long operationId, long requestId,
                 @BiometricManager.BiometricMultiSensorMode int multiSensorConfig) {
 
             mLastBiometricPromptInfo = promptInfo;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
index b712832..6cfa40a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
@@ -16,7 +16,10 @@
 
 package com.android.systemui.communal;
 
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -77,6 +80,9 @@
     @Mock
     private CommunalSource mCommunalSource;
 
+    @Mock
+    private View mChildView;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -116,33 +122,6 @@
     }
 
     @Test
-    public void testHideOnBouncer() {
-        ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
-                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
-
-        // Capture callback value for later use.
-        verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
-
-        // Establish a visible communal view.
-        mController.show(new WeakReference<>(mCommunalSource));
-        mFakeExecutor.runAllReady();
-        verify(mCommunalView).setVisibility(View.VISIBLE);
-        Mockito.clearInvocations(mCommunalView);
-
-        // Trigger bouncer.
-        Mockito.clearInvocations(mCommunalView);
-        callbackCapture.getValue().onKeyguardBouncerChanged(true);
-        mFakeExecutor.runAllReady();
-        verify(mCommunalView).setVisibility(View.INVISIBLE);
-
-        // Hide bouncer
-        Mockito.clearInvocations(mCommunalView);
-        callbackCapture.getValue().onKeyguardBouncerChanged(false);
-        mFakeExecutor.runAllReady();
-        verify(mCommunalView).setVisibility(View.VISIBLE);
-    }
-
-    @Test
     public void testHideOnOcclude() {
         ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
                 ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
@@ -211,4 +190,53 @@
         // Verify state controller is notified communal view is hidden.
         verify(mCommunalStateController).setCommunalViewShowing(false);
     }
+
+    @Test
+    public void testAlphaPropagation() {
+        final float alpha = 0.8f;
+
+        // Ensure alpha setting is propagated to children.
+        when(mCommunalView.getChildCount()).thenReturn(1);
+        when(mCommunalView.getChildAt(0)).thenReturn(mChildView);
+        mController.setAlpha(alpha);
+        verify(mChildView).setAlpha(alpha);
+        verify(mCommunalView).setAlpha(alpha);
+    }
+
+    @Test
+    public void testMultipleShowRequestSuppression() {
+        // Ensure first request invokes source.
+        mController.show(new WeakReference<>(mCommunalSource));
+        mFakeExecutor.runAllReady();
+        verify(mCommunalSource).requestCommunalView(any());
+        clearInvocations(mCommunalSource);
+
+        // Ensure subsequent identical request is suppressed
+        mController.show(new WeakReference<>(mCommunalSource));
+        mFakeExecutor.runAllReady();
+        verify(mCommunalSource, never()).requestCommunalView(any());
+    }
+
+    @Test
+    public void testNoShowInvocationOnBouncer() {
+        ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
+                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+
+        // Capture callback value for later use.
+        verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
+
+        // Set source so it will be cleared if in invalid state.
+        mController.show(new WeakReference<>(mCommunalSource));
+        mFakeExecutor.runAllReady();
+        clearInvocations(mCommunalStateController, mCommunalView);
+
+        // Change bouncer to showing.
+        callbackCapture.getValue().onKeyguardBouncerChanged(true);
+        mFakeExecutor.runAllReady();
+
+        // Verify that there were no requests to remove all child views or set the communal
+        // state to not showing.
+        verify(mCommunalStateController, never()).setCommunalViewShowing(eq(false));
+        verify(mCommunalView, never()).removeAllViews();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithmTest.java
new file mode 100644
index 0000000..0a0266b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewPositionAlgorithmTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.CommunalHostViewPositionAlgorithm.Result;
+
+import org.junit.Test;
+
+
+@SmallTest
+public class CommunalHostViewPositionAlgorithmTest extends SysuiTestCase {
+    @Test
+    public void testOutput() {
+        final float expansion = 0.25f;
+        final int height = 120;
+
+        final CommunalHostViewPositionAlgorithm algorithm = new CommunalHostViewPositionAlgorithm();
+        algorithm.setup(expansion, height);
+        final Result result = new Result();
+        algorithm.run(result);
+
+        // Verify the communal view is shifted offscreen vertically by the correct amount.
+        assertThat((1 - expansion) * -height).isEqualTo(result.communalY);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java
index 6ca1357..cf2e029 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java
@@ -18,14 +18,15 @@
 
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.res.Resources;
+import android.graphics.PixelFormat;
 import android.graphics.Region;
 import android.os.IBinder;
 import android.view.Display;
@@ -125,6 +126,7 @@
         final ArgumentCaptor<SurfaceHolder.Callback> callbackCapture =
                 ArgumentCaptor.forClass(SurfaceHolder.Callback.class);
         verify(mSurfaceHolder).addCallback(callbackCapture.capture());
+        verify(mSurfaceHolder).setFormat(PixelFormat.TRANSPARENT);
         mCallback = callbackCapture.getValue();
 
         final ArgumentCaptor<View.OnLayoutChangeListener> listenerCapture =
@@ -134,7 +136,7 @@
 
         mPackageFuture = SettableFuture.create();
 
-        when(mCommunalSource.requestCommunalSurface(any(), anyInt(), anyInt(), anyInt()))
+        when(mCommunalSource.requestCommunalSurface(any()))
                 .thenReturn(mPackageFuture);
     }
 
@@ -142,19 +144,20 @@
     public void testSetSurfacePackage() {
         // There should be no requests without the proper state.
         verify(mCommunalSource, times(0))
-                .requestCommunalSurface(any(), anyInt(), anyInt(), anyInt());
+                .requestCommunalSurface(any());
 
         // The full state must be present to make a request.
         mController.onViewAttached();
         verify(mCommunalSource, times(0))
-                .requestCommunalSurface(any(), anyInt(), anyInt(), anyInt());
+                .requestCommunalSurface(any());
 
         clearInvocations(mSurfaceView);
 
         // Request surface view once all conditions are met.
         mCallback.surfaceCreated(mSurfaceHolder);
-        verify(mCommunalSource)
-                .requestCommunalSurface(mHostToken, DISPLAY_ID, MEASURED_WIDTH, MEASURED_HEIGHT);
+        final CommunalSourceImpl.Request expectedRequest = new CommunalSourceImpl.Request(
+                MEASURED_WIDTH, MEASURED_HEIGHT, DISPLAY_ID, mHostToken);
+        verify(mCommunalSource).requestCommunalSurface(eq(expectedRequest));
 
         when(mSurfaceView.isAttachedToWindow()).thenReturn(true);
 
@@ -214,8 +217,9 @@
         mFakeExecutor.runAllReady();
         clearInvocations(mSurfaceView);
 
-        verify(mCommunalSource, times(1))
-                .requestCommunalSurface(mHostToken, DISPLAY_ID, MEASURED_WIDTH, MEASURED_HEIGHT);
+        final CommunalSourceImpl.Request expectedRequest = new CommunalSourceImpl.Request(
+                MEASURED_WIDTH, MEASURED_HEIGHT, DISPLAY_ID, mHostToken);
+        verify(mCommunalSource, times(1)).requestCommunalSurface(eq(expectedRequest));
 
         mController.onViewDetached();
         assertTrue(mPackageFuture.isCancelled());
@@ -268,4 +272,31 @@
         verify(mNotificationShadeWindowController)
                 .setTouchExclusionRegion(eq(new Region()));
     }
+
+    @Test
+    public void testLayoutChange() {
+        final int left = 0;
+        final int top = 0;
+        final int right = 200;
+        final int bottom = 100;
+
+        givenSurfacePresent();
+
+        // Layout change should trigger a request to get new communal surface.
+        mLayoutChangeListener.onLayoutChange(mSurfaceView, left, top, right, bottom, 0, 0, 0,
+                0);
+        // Note that the measured are preset and different than the layout input.
+        final CommunalSourceImpl.Request expectedRequest =
+                new CommunalSourceImpl.Request(MEASURED_WIDTH, MEASURED_HEIGHT, DISPLAY_ID,
+                        mHostToken);
+        verify(mCommunalSource)
+                .requestCommunalSurface(eq(expectedRequest));
+
+        clearInvocations(mCommunalSource);
+
+        // Subsequent matching layout change should not trigger any request.
+        mLayoutChangeListener.onLayoutChange(mSurfaceView, left, top, right, bottom, 0, 0, 0,
+                0);
+        verify(mCommunalSource, never()).requestCommunalSurface(any());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index be7917a..2416132 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -425,17 +425,18 @@
         final boolean credentialAllowed = true;
         final boolean requireConfirmation = true;
         final int userId = 10;
-        final String packageName = "test";
         final long operationId = 1;
+        final String packageName = "test";
+        final long requestId = 10;
         final int multiSensorConfig = BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT;
 
         mCommandQueue.showAuthenticationDialog(promptInfo, receiver, sensorIds,
-                credentialAllowed, requireConfirmation , userId, packageName, operationId,
+                credentialAllowed, requireConfirmation, userId, operationId, packageName, requestId,
                 multiSensorConfig);
         waitForIdleSync();
         verify(mCallbacks).showAuthenticationDialog(eq(promptInfo), eq(receiver), eq(sensorIds),
-                eq(credentialAllowed), eq(requireConfirmation), eq(userId), eq(packageName),
-                eq(operationId), eq(multiSensorConfig));
+                eq(credentialAllowed), eq(requireConfirmation), eq(userId), eq(operationId),
+                eq(packageName), eq(requestId), eq(multiSensorConfig));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index f9e3707..faf968b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -86,15 +86,14 @@
     @Mock
     private SysuiStatusBarStateController mStatusBarStateController;
 
+    private TestNotificationPanelViewStateProvider mNotificationPanelViewStateProvider;
     private KeyguardStatusBarView mKeyguardStatusBarView;
     private KeyguardStatusBarViewController mController;
 
-    private float mAlpha = 0.5f;
-    private final KeyguardStatusBarViewController.ViewStateProvider mViewStateProvider =
-            () -> new KeyguardStatusBarViewController.ViewState(mAlpha);
-
     @Before
     public void setup() throws Exception {
+        mNotificationPanelViewStateProvider = new TestNotificationPanelViewStateProvider();
+
         MockitoAnnotations.initMocks(this);
 
         allowTestableLooperAsMainThread();
@@ -114,7 +113,7 @@
                 mStatusBarIconController,
                 new StatusBarIconController.TintedIconManager.Factory(mFeatureFlags),
                 mBatteryMeterViewController,
-                mViewStateProvider,
+                mNotificationPanelViewStateProvider,
                 mKeyguardStateController,
                 mKeyguardBypassController,
                 mKeyguardUpdateMonitor,
@@ -210,7 +209,6 @@
 
     @Test
     public void updateViewState_notKeyguardState_nothingUpdated() {
-        mAlpha = 0.255f;
         mController.onViewAttached();
         updateStateToNotKeyguard();
 
@@ -264,8 +262,59 @@
         assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
+    @Test
+    public void updateViewState_panelExpandedHeightZero_viewHidden() {
+        mController.onViewAttached();
+        updateStateToKeyguard();
+
+        mNotificationPanelViewStateProvider.setPanelViewExpandedHeight(0);
+
+        mController.updateViewState();
+
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
+    }
+
+    @Test
+    public void updateViewState_qsExpansionOne_viewHidden() {
+        mController.onViewAttached();
+        updateStateToKeyguard();
+
+        mNotificationPanelViewStateProvider.setQsExpansionFraction(1f);
+
+        mController.updateViewState();
+
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
+    }
+
     // TODO(b/195442899): Add more tests for #updateViewState once CLs are finalized.
 
+    @Test
+    public void updateForHeadsUp_headsUpShouldBeVisible_viewHidden() {
+        mController.onViewAttached();
+        updateStateToKeyguard();
+        mKeyguardStatusBarView.setVisibility(View.VISIBLE);
+
+        mNotificationPanelViewStateProvider.setShouldHeadsUpBeVisible(true);
+        mController.updateForHeadsUp(/* animate= */ false);
+
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
+    }
+
+    @Test
+    public void updateForHeadsUp_headsUpShouldNotBeVisible_viewShown() {
+        mController.onViewAttached();
+        updateStateToKeyguard();
+
+        // Start with the opposite state.
+        mNotificationPanelViewStateProvider.setShouldHeadsUpBeVisible(true);
+        mController.updateForHeadsUp(/* animate= */ false);
+
+        mNotificationPanelViewStateProvider.setShouldHeadsUpBeVisible(false);
+        mController.updateForHeadsUp(/* animate= */ false);
+
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
     private void updateStateToNotKeyguard() {
         updateStatusBarState(SHADE);
     }
@@ -295,4 +344,41 @@
 
         callback.onFinishedGoingToSleep(0);
     }
+
+    private static class TestNotificationPanelViewStateProvider
+            implements NotificationPanelViewController.NotificationPanelViewStateProvider {
+
+        TestNotificationPanelViewStateProvider() {}
+
+        private float mPanelViewExpandedHeight = 100f;
+        private float mQsExpansionFraction = 0f;
+        private boolean mShouldHeadsUpBeVisible = false;
+
+        @Override
+        public float getPanelViewExpandedHeight() {
+            return mPanelViewExpandedHeight;
+        }
+
+        @Override
+        public float getQsExpansionFraction() {
+            return mQsExpansionFraction;
+        }
+
+        @Override
+        public boolean shouldHeadsUpBeVisible() {
+            return mShouldHeadsUpBeVisible;
+        }
+
+        public void setPanelViewExpandedHeight(float panelViewExpandedHeight) {
+            this.mPanelViewExpandedHeight = panelViewExpandedHeight;
+        }
+
+        public void setQsExpansionFraction(float qsExpansionFraction) {
+            this.mQsExpansionFraction = qsExpansionFraction;
+        }
+
+        public void setShouldHeadsUpBeVisible(boolean shouldHeadsUpBeVisible) {
+            this.mShouldHeadsUpBeVisible = shouldHeadsUpBeVisible;
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index dc22036..a5c9527 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -29,6 +29,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeast;
@@ -850,9 +851,10 @@
 
     @Test
     public void testCommunalhostViewControllerInit() {
+        verify(mCommunalHostViewController, times(1)).init();
         clearInvocations(mCommunalHostViewController);
         givenViewAttached();
-        verify(mCommunalHostViewController).init();
+        verify(mCommunalHostViewController, never()).init();
     }
 
     @Test
@@ -873,8 +875,6 @@
         clearInvocations(mCommunalHostViewController);
         givenViewDetached();
         verify(mCommunalSourceMonitor).removeCallback(any());
-        verify(mCommunalHostViewController).show(sourceCapture.capture());
-        assertThat(sourceCapture.getValue()).isEqualTo(null);
     }
 
     @Test
@@ -901,6 +901,28 @@
                 anyBoolean(), anyBoolean(), anyInt());
     }
 
+    @Test
+    public void testCommunalAlphaUpdate() {
+        // Verify keyguard content alpha changes are propagate. Note the actual value set is not
+        // checked since an interpolation is applied to the incoming value.
+        mNotificationPanelViewController.setKeyguardOnlyContentAlpha(0.8f);
+        verify(mCommunalHostViewController).setAlpha(anyFloat());
+    }
+
+    @Test
+    public void testCommunalPositionUpdate() {
+        // Verify that the communal position is updated on interaction with the
+        // NotificationPanelViewController. Note that there a number of paths where the position
+        // might be updated and therefore the check isn't strictly on a single invocation.
+        clearInvocations(mCommunalHostViewController);
+        final View.OnLayoutChangeListener layoutChangeListener =
+                mNotificationPanelViewController.createLayoutChangeListener();
+        mNotificationPanelViewController.mStatusBarStateController.setState(KEYGUARD);
+        layoutChangeListener.onLayoutChange(mView, 0, 0, 200, 200, 0, 0, 200, 200);
+        verify(mCommunalHostViewController, atLeast(1))
+                .updatePosition(anyInt(), anyBoolean());
+    }
+
     private void triggerPositionClockAndNotifications() {
         mNotificationPanelViewController.closeQs();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index 9c47f19..e6dc4db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -96,6 +96,8 @@
     DumpManager mDumpManager;
     @Mock
     OverlayManagerTransaction.Builder mTransactionBuilder;
+    @Mock
+    Runnable mOnOverlaysApplied;
 
     private ThemeOverlayApplier mManager;
     private boolean mGetOverlayInfoEnabled = true;
@@ -103,7 +105,8 @@
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mManager = new ThemeOverlayApplier(mOverlayManager, MoreExecutors.directExecutor(),
+        mManager = new ThemeOverlayApplier(mOverlayManager,
+                MoreExecutors.directExecutor(), MoreExecutors.directExecutor(),
                 LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager) {
             @Override
             protected OverlayManagerTransaction.Builder getTransactionBuilder() {
@@ -173,7 +176,7 @@
     @Test
     public void allCategoriesSpecified_allEnabledExclusively() {
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, mOnOverlaysApplied);
         verify(mOverlayManager).commit(any());
 
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
@@ -185,7 +188,7 @@
     @Test
     public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() {
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, mOnOverlaysApplied);
 
         for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) {
             if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) {
@@ -202,8 +205,9 @@
     public void allCategoriesSpecified_enabledForAllUserHandles() {
         Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                userHandles);
+                userHandles, mOnOverlaysApplied);
 
+        verify(mOnOverlaysApplied).run();
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
             verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
                     eq(TEST_USER.getIdentifier()));
@@ -219,7 +223,7 @@
 
         Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                userHandles);
+                userHandles, mOnOverlaysApplied);
 
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
             verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true),
@@ -233,7 +237,7 @@
                 mock(FabricatedOverlay.class)
         };
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation,
-                TEST_USER.getIdentifier(), TEST_USER_HANDLES);
+                TEST_USER.getIdentifier(), TEST_USER_HANDLES, mOnOverlaysApplied);
 
         for (FabricatedOverlay overlay : pendingCreation) {
             verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay));
@@ -247,7 +251,7 @@
         categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID);
 
         mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, mOnOverlaysApplied);
 
         for (OverlayIdentifier overlayPackage : categoryToPackage.values()) {
             verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
@@ -264,7 +268,7 @@
     @Test
     public void zeroCategoriesSpecified_allDisabled() {
         mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, mOnOverlaysApplied);
 
         for (String category : THEME_CATEGORIES) {
             verify(mTransactionBuilder).setEnabled(
@@ -279,7 +283,7 @@
         categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category"));
 
         mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, mOnOverlaysApplied);
 
         verify(mTransactionBuilder, never()).setEnabled(
                 eq(new OverlayIdentifier("com.example.blah.category")), eq(false),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index f6a54936..cd911cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -161,7 +161,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -183,7 +183,7 @@
         mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
                 null, null), WallpaperManager.FLAG_SYSTEM);
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -204,7 +204,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -240,7 +240,7 @@
                 .isFalse();
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -270,7 +270,7 @@
                 "android.theme.customization.color_both\":\"0")).isTrue();
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -300,7 +300,7 @@
                 "android.theme.customization.color_both\":\"1")).isTrue();
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -327,7 +327,7 @@
         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
                 .isFalse();
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -354,7 +354,7 @@
         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
                 .isFalse();
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -382,7 +382,7 @@
                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -411,14 +411,14 @@
 
 
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
     public void onProfileAdded_setsTheme() {
         mBroadcastReceiver.getValue().onReceive(null,
                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -428,7 +428,7 @@
         mBroadcastReceiver.getValue().onReceive(null,
                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -438,7 +438,7 @@
         mBroadcastReceiver.getValue().onReceive(null,
                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -450,7 +450,7 @@
                 Color.valueOf(Color.BLUE), null);
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
 
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         // Regression test: null events should not reset the internal state and allow colors to be
         // applied again.
@@ -458,11 +458,11 @@
         mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
         mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
-                any());
+                any(), any());
         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN),
                 null, null), WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
-                any());
+                any(), any());
     }
 
     @Test
@@ -499,7 +499,7 @@
         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
 
         // Colors were applied during controller initialization.
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
         clearInvocations(mThemeOverlayApplier);
     }
 
@@ -533,7 +533,7 @@
         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
 
         // Colors were applied during controller initialization.
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
         clearInvocations(mThemeOverlayApplier);
 
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -542,12 +542,12 @@
 
         // Defers event because we already have initial colors.
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         // Then event happens after setup phase is over.
         when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
         mDeviceProvisionedListener.getValue().onUserSetupChanged();
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -568,11 +568,11 @@
                 Color.valueOf(Color.RED), null);
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         mWakefulnessLifecycle.dispatchFinishedGoingToSleep();
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -592,10 +592,10 @@
                 Color.valueOf(Color.RED), null);
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep();
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -614,7 +614,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index 95f3560..b095e3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -433,13 +433,28 @@
                     MotionEvent.BUTTON_PRIMARY, 1.0f, 1.0f, mLastMotionEvent.getDeviceId(), 0,
                     mLastMotionEvent.getSource(), mLastMotionEvent.getFlags());
 
-            // The only real difference between these two events is the action flag.
+            MotionEvent pressEvent = MotionEvent.obtain(downEvent);
+            pressEvent.setAction(MotionEvent.ACTION_BUTTON_PRESS);
+            pressEvent.setActionButton(MotionEvent.BUTTON_PRIMARY);
+
+            MotionEvent releaseEvent = MotionEvent.obtain(downEvent);
+            releaseEvent.setAction(MotionEvent.ACTION_BUTTON_RELEASE);
+            releaseEvent.setActionButton(MotionEvent.BUTTON_PRIMARY);
+            releaseEvent.setButtonState(0);
+
             MotionEvent upEvent = MotionEvent.obtain(downEvent);
             upEvent.setAction(MotionEvent.ACTION_UP);
+            upEvent.setButtonState(0);
 
             AutoclickController.super.onMotionEvent(downEvent, downEvent, mEventPolicyFlags);
             downEvent.recycle();
 
+            AutoclickController.super.onMotionEvent(pressEvent, pressEvent, mEventPolicyFlags);
+            pressEvent.recycle();
+
+            AutoclickController.super.onMotionEvent(releaseEvent, releaseEvent, mEventPolicyFlags);
+            releaseEvent.recycle();
+
             AutoclickController.super.onMotionEvent(upEvent, upEvent, mEventPolicyFlags);
             upEvent.recycle();
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
index 5f9aaae..9801407 100644
--- a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
+++ b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
@@ -40,6 +40,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -47,6 +48,7 @@
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.R;
+import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.ImageUtils;
@@ -71,6 +73,7 @@
     @VisibleForTesting
     protected static final String ACTION_DISMISS_NOTIFICATION =
             TAG + ".ACTION_DISMISS_NOTIFICATION";
+    private static final String EXTRA_TIME_FOR_LOGGING = "start_time_to_log_a11y_tool";
     private static final int SEND_NOTIFICATION_DELAY_HOURS = 24;
 
     /** Current enabled accessibility services. */
@@ -179,7 +182,8 @@
         intent.setPackage(context.getPackageName())
                 .setIdentifier(serviceComponentName.flattenToShortString())
                 .putExtra(Intent.EXTRA_COMPONENT_NAME, serviceComponentName)
-                .putExtra(Intent.EXTRA_USER_ID, userId);
+                .putExtra(Intent.EXTRA_USER_ID, userId)
+                .putExtra(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
         return intent;
     }
 
@@ -214,11 +218,24 @@
             if (TextUtils.isEmpty(action) || componentName == null) {
                 return;
             }
+            final long startTimeMills = intent.getLongExtra(Intent.EXTRA_TIME, 0);
+            final long durationMills =
+                    startTimeMills > 0 ? SystemClock.elapsedRealtime() - startTimeMills : 0;
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_SYSTEM);
             if (ACTION_SEND_NOTIFICATION.equals(action)) {
-                trySendNotification(userId, componentName);
+                if (trySendNotification(userId, componentName)) {
+                    AccessibilityStatsLogUtils.logNonA11yToolServiceWarningReported(
+                            componentName.getPackageName(),
+                            AccessibilityStatsLogUtils.ACCESSIBILITY_PRIVACY_WARNING_STATUS_SHOWN,
+                            durationMills);
+                }
             } else if (ACTION_A11Y_SETTINGS.equals(action)) {
-                launchSettings(userId, componentName);
+                if (tryLaunchSettings(userId, componentName)) {
+                    AccessibilityStatsLogUtils.logNonA11yToolServiceWarningReported(
+                            componentName.getPackageName(),
+                            AccessibilityStatsLogUtils.ACCESSIBILITY_PRIVACY_WARNING_STATUS_CLICKED,
+                            durationMills);
+                }
                 mNotificationManager.cancel(componentName.flattenToShortString(),
                         NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
                 onNotificationCanceled(userId, componentName);
@@ -240,12 +257,12 @@
             }
         }
 
-        private void trySendNotification(int userId, ComponentName componentName) {
+        private boolean trySendNotification(int userId, ComponentName componentName) {
             if (!AccessibilitySecurityPolicy.POLICY_WARNING_ENABLED) {
-                return;
+                return false;
             }
             if (userId != mCurrentUserId) {
-                return;
+                return false;
             }
 
             List<AccessibilityServiceInfo> enabledServiceInfos = getEnabledServiceInfos();
@@ -264,23 +281,27 @@
                                 android.R.dimen.app_icon_size);
                         sendNotification(userId, componentName, displayName,
                                 ImageUtils.buildScaledBitmap(drawable, size, size));
+                        return true;
                     }
                     break;
                 }
             }
+            return false;
         }
 
-        private void launchSettings(int userId, ComponentName componentName) {
+        private boolean tryLaunchSettings(int userId, ComponentName componentName) {
             if (userId != mCurrentUserId) {
-                return;
+                return false;
             }
             final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
             intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName.flattenToShortString());
+            intent.putExtra(EXTRA_TIME_FOR_LOGGING, SystemClock.elapsedRealtime());
             final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(
                     mContext.getDisplayId()).toBundle();
             mContext.startActivityAsUser(intent, bundle, UserHandle.of(userId));
             mContext.getSystemService(StatusBarManager.class).collapsePanels();
+            return true;
         }
 
         protected void onNotificationCanceled(int userId, ComponentName componentName) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index c87a6fa..6505c20 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -207,10 +207,3 @@
     name: "protolog.conf.json.gz",
     src: ":services.core.json.gz",
 }
-
-filegroup {
-    name: "services.core-sources-deviceconfig-interface",
-    srcs: [
-        "java/com/android/server/utils/DeviceConfigInterface.java",
-    ],
-}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 449f02e..3c6b682 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -492,7 +492,7 @@
                     for (int uid : uidsToRemove) {
                         FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, -1, uid,
                                 FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
-                        mStats.removeIsolatedUidLocked(uid, SystemClock.elapsedRealtime(),
+                        mStats.maybeRemoveIsolatedUidLocked(uid, SystemClock.elapsedRealtime(),
                                 SystemClock.uptimeMillis());
                     }
                     mStats.clearPendingRemovedUidsLocked();
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index dd620ac..f5d29fc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5271,6 +5271,10 @@
     // TODO investigate internal users due to deprecation of SDK API
     /** @see AudioManager#setBluetoothA2dpOn(boolean) */
     public void setBluetoothA2dpOn(boolean on) {
+        if (!checkAudioSettingsPermission("setBluetoothA2dpOn()")) {
+            return;
+        }
+
         // for logging only
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
@@ -5296,6 +5300,10 @@
 
     /** @see AudioManager#startBluetoothSco() */
     public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
+        if (!checkAudioSettingsPermission("startBluetoothSco()")) {
+            return;
+        }
+
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
         final int scoAudioMode =
@@ -5318,6 +5326,10 @@
 
     /** @see AudioManager#startBluetoothScoVirtualCall() */
     public void startBluetoothScoVirtualCall(IBinder cb) {
+        if (!checkAudioSettingsPermission("startBluetoothScoVirtualCall()")) {
+            return;
+        }
+
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
         final String eventSource = new StringBuilder("startBluetoothScoVirtualCall()")
diff --git a/services/core/java/com/android/server/audio/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java
index ad72166..872b1fe 100644
--- a/services/core/java/com/android/server/audio/RotationHelper.java
+++ b/services/core/java/com/android/server/audio/RotationHelper.java
@@ -21,20 +21,26 @@
 import android.media.AudioSystem;
 import android.os.Handler;
 import android.util.Log;
+import android.view.Display;
 import android.view.Surface;
 import android.view.WindowManager;
 
 /**
  * Class to handle device rotation events for AudioService, and forward device rotation
- * to the audio HALs through AudioSystem.
+ * and current active ID to the audio HALs through AudioSystem.
  *
  * The role of this class is to monitor device orientation changes, and upon rotation,
  * verify the UI orientation. In case of a change, send the new orientation, in increments
  * of 90deg, through AudioSystem.
  *
+ * Another role of this class is to track current active display ID changes. In case of a
+ * change, send the new active display ID through AudioSystem.
+ *
  * Note that even though we're responding to device orientation events, we always
  * query the display rotation so audio stays in sync with video/dialogs. This is
  * done with .getDefaultDisplay().getRotation() from WINDOW_SERVICE.
+ *
+ * We also monitor current display ID and audio is able to know which display is active.
  */
 class RotationHelper {
 
@@ -43,7 +49,9 @@
     private static AudioDisplayListener sDisplayListener;
 
     private static final Object sRotationLock = new Object();
+    private static final Object sActiveDisplayLock = new Object();
     private static int sDeviceRotation = Surface.ROTATION_0; // R/W synchronized on sRotationLock
+    private static int sDisplayId = Display.DEFAULT_DISPLAY; // synchronized on sActiveDisplayLock
 
     private static Context sContext;
     private static Handler sHandler;
@@ -112,6 +120,18 @@
     }
 
     /**
+     * Query current display active id and publish the change if any.
+     */
+    static void updateActiveDisplayId(int displayId) {
+        synchronized (sActiveDisplayLock) {
+            if (displayId != Display.DEFAULT_DISPLAY && sDisplayId != displayId) {
+                sDisplayId = displayId;
+                AudioSystem.setParameters("active_displayId=" + sDisplayId);
+            }
+        }
+    }
+
+    /**
      * Uses android.hardware.display.DisplayManager.DisplayListener
      */
     final static class AudioDisplayListener implements DisplayManager.DisplayListener {
@@ -126,6 +146,7 @@
 
         @Override
         public void onDisplayChanged(int displayId) {
+            updateActiveDisplayId(displayId);
             updateOrientation();
         }
     }
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 0cd2e3d..b42f898 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -206,7 +206,7 @@
         }
 
         @Override
-        public void authenticate(IBinder token, long sessionId, int userId,
+        public long authenticate(IBinder token, long sessionId, int userId,
                 IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo)
                 throws RemoteException {
             // Only allow internal clients to authenticate with a different userId.
@@ -223,18 +223,18 @@
 
             if (!checkAppOps(callingUid, opPackageName, "authenticate()")) {
                 authenticateFastFail("Denied by app ops: " + opPackageName, receiver);
-                return;
+                return -1;
             }
 
             if (token == null || receiver == null || opPackageName == null || promptInfo == null) {
                 authenticateFastFail(
                         "Unable to authenticate, one or more null arguments", receiver);
-                return;
+                return -1;
             }
 
             if (!Utils.isForeground(callingUid, callingPid)) {
                 authenticateFastFail("Caller is not foreground: " + opPackageName, receiver);
-                return;
+                return -1;
             }
 
             if (promptInfo.containsTestConfigurations()) {
@@ -251,7 +251,7 @@
 
             final long identity = Binder.clearCallingIdentity();
             try {
-                mBiometricService.authenticate(
+                return mBiometricService.authenticate(
                         token, sessionId, userId, receiver, opPackageName, promptInfo);
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -270,7 +270,7 @@
         }
 
         @Override
-        public void cancelAuthentication(IBinder token, String opPackageName)
+        public void cancelAuthentication(IBinder token, String opPackageName, long requestId)
                 throws RemoteException {
             checkPermission();
 
@@ -281,7 +281,7 @@
 
             final long identity = Binder.clearCallingIdentity();
             try {
-                mBiometricService.cancelAuthentication(token, opPackageName);
+                mBiometricService.cancelAuthentication(token, opPackageName, requestId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index bdde980..0da6a1b 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -128,6 +128,7 @@
     @VisibleForTesting final IBinder mToken;
     // Info to be shown on BiometricDialog when all cookies are returned.
     @VisibleForTesting final PromptInfo mPromptInfo;
+    private final long mRequestId;
     private final long mOperationId;
     private final int mUserId;
     private final IBiometricSensorReceiver mSensorReceiver;
@@ -142,6 +143,8 @@
     private @BiometricMultiSensorMode int mMultiSensorMode;
     private @MultiSensorState int mMultiSensorState;
     private int[] mSensors;
+    // TODO(b/197265902): merge into state
+    private boolean mCancelled;
     // For explicit confirmation, do not send to keystore until the user has confirmed
     // the authentication.
     private byte[] mTokenEscrow;
@@ -162,6 +165,7 @@
             @NonNull ClientDeathReceiver clientDeathReceiver,
             @NonNull PreAuthInfo preAuthInfo,
             @NonNull IBinder token,
+            long requestId,
             long operationId,
             int userId,
             @NonNull IBiometricSensorReceiver sensorReceiver,
@@ -179,6 +183,7 @@
         mClientDeathReceiver = clientDeathReceiver;
         mPreAuthInfo = preAuthInfo;
         mToken = token;
+        mRequestId = requestId;
         mOperationId = operationId;
         mUserId = userId;
         mSensorReceiver = sensorReceiver;
@@ -187,6 +192,7 @@
         mPromptInfo = promptInfo;
         mDebugEnabled = debugEnabled;
         mFingerprintSensorProperties = fingerprintSensorProperties;
+        mCancelled = false;
 
         try {
             mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */);
@@ -233,7 +239,7 @@
                 Slog.v(TAG, "waiting for cooking for sensor: " + sensor.id);
             }
             sensor.goToStateWaitingForCookie(requireConfirmation, mToken, mOperationId,
-                    mUserId, mSensorReceiver, mOpPackageName, cookie,
+                    mUserId, mSensorReceiver, mOpPackageName, mRequestId, cookie,
                     mPromptInfo.isAllowBackgroundAuthentication());
         }
     }
@@ -255,8 +261,9 @@
                     true /* credentialAllowed */,
                     false /* requireConfirmation */,
                     mUserId,
-                    mOpPackageName,
                     mOperationId,
+                    mOpPackageName,
+                    mRequestId,
                     mMultiSensorMode);
         } else if (!mPreAuthInfo.eligibleSensors.isEmpty()) {
             // Some combination of biometric or biometric|credential is requested
@@ -270,6 +277,11 @@
     }
 
     void onCookieReceived(int cookie) {
+        if (mCancelled) {
+            Slog.w(TAG, "Received cookie but already cancelled (ignoring): " + cookie);
+            return;
+        }
+
         for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) {
             sensor.goToStateCookieReturnedIfCookieMatches(cookie);
         }
@@ -301,8 +313,9 @@
                             mPreAuthInfo.shouldShowCredential(),
                             requireConfirmation,
                             mUserId,
-                            mOpPackageName,
                             mOperationId,
+                            mOpPackageName,
+                            mRequestId,
                             mMultiSensorMode);
                     mState = STATE_AUTH_STARTED;
                 } catch (RemoteException e) {
@@ -369,7 +382,7 @@
                 final boolean shouldCancel = filter.apply(sensor);
                 Slog.d(TAG, "sensorId: " + sensor.id + ", shouldCancel: " + shouldCancel);
                 if (shouldCancel) {
-                    sensor.goToStateCancelling(mToken, mOpPackageName);
+                    sensor.goToStateCancelling(mToken, mOpPackageName, mRequestId);
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Unable to cancel authentication");
@@ -425,8 +438,9 @@
                             true /* credentialAllowed */,
                             false /* requireConfirmation */,
                             mUserId,
-                            mOpPackageName,
                             mOperationId,
+                            mOpPackageName,
+                            mRequestId,
                             mMultiSensorMode);
                 } else {
                     mClientReceiver.onError(modality, error, vendorCode);
@@ -775,6 +789,8 @@
      * @return true if this AuthSession is finished, e.g. should be set to null
      */
     boolean onCancelAuthSession(boolean force) {
+        mCancelled = true;
+
         final boolean authStarted = mState == STATE_AUTH_CALLED
                 || mState == STATE_AUTH_STARTED
                 || mState == STATE_AUTH_STARTED_UI_SHOWING;
@@ -820,6 +836,7 @@
         return Utils.isCredentialRequested(mPromptInfo);
     }
 
+    @VisibleForTesting
     boolean allCookiesReceived() {
         final int remainingCookies = mPreAuthInfo.numSensorsWaitingForCookie();
         Slog.d(TAG, "Remaining cookies: " + remainingCookies);
@@ -839,6 +856,10 @@
         return mState;
     }
 
+    long getRequestId() {
+        return mRequestId;
+    }
+
     private int statsModality() {
         int modality = 0;
 
@@ -901,7 +922,9 @@
     @Override
     public String toString() {
         return "State: " + mState
+                + ", cancelled: " + mCancelled
                 + ", isCrypto: " + isCrypto()
-                + ", PreAuthInfo: " + mPreAuthInfo;
+                + ", PreAuthInfo: " + mPreAuthInfo
+                + ", requestId: " + mRequestId;
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/BiometricSensor.java b/services/core/java/com/android/server/biometrics/BiometricSensor.java
index 8a842b5..0333c3e 100644
--- a/services/core/java/com/android/server/biometrics/BiometricSensor.java
+++ b/services/core/java/com/android/server/biometrics/BiometricSensor.java
@@ -108,11 +108,11 @@
 
     void goToStateWaitingForCookie(boolean requireConfirmation, IBinder token, long sessionId,
             int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
-            int cookie, boolean allowBackgroundAuthentication)
+            long requestId, int cookie, boolean allowBackgroundAuthentication)
             throws RemoteException {
         mCookie = cookie;
         impl.prepareForAuthentication(requireConfirmation, token,
-                sessionId, userId, sensorReceiver, opPackageName, mCookie,
+                sessionId, userId, sensorReceiver, opPackageName, requestId, mCookie,
                 allowBackgroundAuthentication);
         mSensorState = STATE_WAITING_FOR_COOKIE;
     }
@@ -129,8 +129,9 @@
         mSensorState = STATE_AUTHENTICATING;
     }
 
-    void goToStateCancelling(IBinder token, String opPackageName) throws RemoteException {
-        impl.cancelAuthenticationFromService(token, opPackageName);
+    void goToStateCancelling(IBinder token, String opPackageName, long requestId)
+            throws RemoteException {
+        impl.cancelAuthenticationFromService(token, opPackageName, requestId);
         mSensorState = STATE_CANCELING;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index b1d300c..e0775d4 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -83,6 +83,7 @@
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * System service that arbitrates the modality for BiometricPrompt to use.
@@ -115,6 +116,7 @@
     final SettingObserver mSettingObserver;
     private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
     private final Random mRandom = new Random();
+    @NonNull private final AtomicLong mRequestCounter;
 
     @VisibleForTesting
     IStatusBarService mStatusBarService;
@@ -194,6 +196,7 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     handleAuthenticate(
                             (IBinder) args.arg1 /* token */,
+                            (long) args.arg6 /* requestId */,
                             (long) args.arg2 /* operationId */,
                             args.argi1 /* userid */,
                             (IBiometricServiceReceiver) args.arg3 /* receiver */,
@@ -204,7 +207,9 @@
                 }
 
                 case MSG_CANCEL_AUTHENTICATION: {
-                    handleCancelAuthentication();
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    handleCancelAuthentication((long) args.arg3 /* requestId */);
+                    args.recycle();
                     break;
                 }
 
@@ -683,13 +688,13 @@
         }
 
         @Override // Binder call
-        public void authenticate(IBinder token, long operationId, int userId,
+        public long authenticate(IBinder token, long operationId, int userId,
                 IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) {
             checkInternalPermission();
 
             if (token == null || receiver == null || opPackageName == null || promptInfo == null) {
                 Slog.e(TAG, "Unable to authenticate, one or more null arguments");
-                return;
+                return -1;
             }
 
             if (!Utils.isValidAuthenticatorConfig(promptInfo)) {
@@ -706,6 +711,8 @@
                 }
             }
 
+            final long requestId = mRequestCounter.incrementAndGet();
+
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = token;
             args.arg2 = operationId;
@@ -713,15 +720,23 @@
             args.arg3 = receiver;
             args.arg4 = opPackageName;
             args.arg5 = promptInfo;
+            args.arg6 = requestId;
 
             mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
+
+            return requestId;
         }
 
         @Override // Binder call
-        public void cancelAuthentication(IBinder token, String opPackageName) {
+        public void cancelAuthentication(IBinder token, String opPackageName, long requestId) {
             checkInternalPermission();
 
-            mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION).sendToTarget();
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = token;
+            args.arg2 = opPackageName;
+            args.arg3 = requestId;
+
+            mHandler.obtainMessage(MSG_CANCEL_AUTHENTICATION, args).sendToTarget();
         }
 
         @Override // Binder call
@@ -1111,6 +1126,10 @@
             return Settings.Secure.getInt(context.getContentResolver(),
                     CoexCoordinator.FACE_HAPTIC_DISABLE, 1) != 0;
         }
+
+        public AtomicLong getRequestGenerator() {
+            return new AtomicLong(0);
+        }
     }
 
     /**
@@ -1136,6 +1155,7 @@
         mEnabledOnKeyguardCallbacks = new ArrayList<>();
         mSettingObserver = mInjector.getSettingObserver(context, mHandler,
                 mEnabledOnKeyguardCallbacks);
+        mRequestCounter = mInjector.getRequestGenerator();
 
         // TODO(b/193089985) This logic lives here (outside of CoexCoordinator) so that it doesn't
         //  need to depend on context. We can remove this code once the advanced logic is enabled
@@ -1349,7 +1369,7 @@
         mCurrentAuthSession.onCookieReceived(cookie);
     }
 
-    private void handleAuthenticate(IBinder token, long operationId, int userId,
+    private void handleAuthenticate(IBinder token, long requestId, long operationId, int userId,
             IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo) {
         mHandler.post(() -> {
             try {
@@ -1360,7 +1380,8 @@
                 final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus();
 
                 Slog.d(TAG, "handleAuthenticate: modality(" + preAuthStatus.first
-                        + "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo);
+                        + "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo
+                        + " requestId: " + requestId);
 
                 if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) {
                     // If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but
@@ -1372,8 +1393,8 @@
                         promptInfo.setAuthenticators(Authenticators.DEVICE_CREDENTIAL);
                     }
 
-                    authenticateInternal(token, operationId, userId, receiver, opPackageName,
-                            promptInfo, preAuthInfo);
+                    authenticateInternal(token, requestId, operationId, userId, receiver,
+                            opPackageName, promptInfo, preAuthInfo);
                 } else {
                     receiver.onError(preAuthStatus.first /* modality */,
                             preAuthStatus.second /* errorCode */,
@@ -1394,7 +1415,7 @@
      * Note that this path is NOT invoked when the BiometricPrompt "Try again" button is pressed.
      * In that case, see {@link #handleOnTryAgainPressed()}.
      */
-    private void authenticateInternal(IBinder token, long operationId, int userId,
+    private void authenticateInternal(IBinder token, long requestId, long operationId, int userId,
             IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo,
             PreAuthInfo preAuthInfo) {
         Slog.d(TAG, "Creating authSession with authRequest: " + preAuthInfo);
@@ -1412,9 +1433,9 @@
 
         final boolean debugEnabled = mInjector.isDebugEnabled(getContext(), userId);
         mCurrentAuthSession = new AuthSession(getContext(), mStatusBarService, mSysuiReceiver,
-                mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, token, operationId, userId,
-                mBiometricSensorReceiver, receiver, opPackageName, promptInfo, debugEnabled,
-                mInjector.getFingerprintSensorProperties(getContext()));
+                mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, token, requestId,
+                operationId, userId, mBiometricSensorReceiver, receiver, opPackageName, promptInfo,
+                debugEnabled, mInjector.getFingerprintSensorProperties(getContext()));
         try {
             mCurrentAuthSession.goToInitialState();
         } catch (RemoteException e) {
@@ -1422,11 +1443,21 @@
         }
     }
 
-    private void handleCancelAuthentication() {
+    private void handleCancelAuthentication(long requestId) {
         if (mCurrentAuthSession == null) {
             Slog.e(TAG, "handleCancelAuthentication: AuthSession is null");
             return;
         }
+        if (mCurrentAuthSession.getRequestId() != requestId) {
+            // TODO: actually cancel the operation
+            // This can happen if the operation has been queued, but is cancelled before
+            // it reaches the head of the scheduler. Consider it a programming error for now
+            // and ignore it.
+            Slog.e(TAG, "handleCancelAuthentication: AuthSession mismatch current requestId: "
+                    + mCurrentAuthSession.getRequestId() + " cancel for: " + requestId
+                    + " (ignoring cancellation)");
+            return;
+        }
 
         final boolean finished = mCurrentAuthSession.onCancelAuthSession(false /* force */);
         if (finished) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 3eb6f4a..0a1c77b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -101,6 +101,7 @@
     private final int mSensorId; // sensorId as configured by the framework
 
     @Nullable private IBinder mToken;
+    private long mRequestId;
     @Nullable private ClientMonitorCallbackConverter mListener;
     // Currently only used for authentication client. The cookie generated by BiometricService
     // is never 0.
@@ -154,6 +155,7 @@
         mSequentialId = sCount++;
         mContext = context;
         mToken = token;
+        mRequestId = -1;
         mListener = listener;
         mTargetUserId = userId;
         mOwner = owner;
@@ -258,6 +260,29 @@
         return mSensorId;
     }
 
+    /** Unique request id. */
+    public final long getRequestId() {
+        return mRequestId;
+    }
+
+    /** If a unique id has been set via {@link #setRequestId(long)} */
+    public final boolean hasRequestId() {
+        return mRequestId > 0;
+    }
+
+    /**
+     * A unique identifier used to tie this operation to a request (i.e an API invocation).
+     *
+     * Subclasses should not call this method if this operation does not have a direct
+     * correspondence to a request and {@link #hasRequestId()} will return false.
+     */
+    protected final void setRequestId(long id) {
+        if (id <= 0) {
+            throw new IllegalArgumentException("request id must be positive");
+        }
+        mRequestId = id;
+    }
+
     @VisibleForTesting
     public Callback getCallback() {
         return mCallback;
@@ -270,6 +295,7 @@
                 + ", proto=" + getProtoEnum()
                 + ", owner=" + getOwnerString()
                 + ", cookie=" + getCookie()
+                + ", requestId=" + getRequestId()
                 + ", userId=" + getTargetUserId() + "}";
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index feb9e2a..361ec40 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -643,22 +643,18 @@
     /**
      * Requests to cancel authentication or detection.
      * @param token from the caller, should match the token passed in when requesting authentication
+     * @param requestId the id returned when requesting authentication
      */
-    public void cancelAuthenticationOrDetection(IBinder token) {
-        if (mCurrentOperation == null) {
-            Slog.e(getTag(), "Unable to cancel authentication, null operation");
-            return;
-        }
-        final boolean isCorrectClient = isAuthenticationOrDetectionOperation(mCurrentOperation);
-        final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token;
+    public void cancelAuthenticationOrDetection(IBinder token, long requestId) {
+        Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId
+                + " current: " + mCurrentOperation
+                + " stack size: " + mPendingOperations.size());
 
-        Slog.d(getTag(), "cancelAuthenticationOrDetection, isCorrectClient: " + isCorrectClient
-                + ", tokenMatches: " + tokenMatches);
-
-        if (isCorrectClient && tokenMatches) {
+        if (mCurrentOperation != null
+                && canCancelAuthOperation(mCurrentOperation, token, requestId)) {
             Slog.d(getTag(), "Cancelling: " + mCurrentOperation);
             cancelInternal(mCurrentOperation);
-        } else if (!isCorrectClient) {
+        } else {
             // Look through the current queue for all authentication clients for the specified
             // token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking
             // all of them, instead of just the first one, since the API surface currently doesn't
@@ -666,8 +662,7 @@
             // process. However, this generally does not happen anyway, and would be a class of
             // bugs on its own.
             for (Operation operation : mPendingOperations) {
-                if (isAuthenticationOrDetectionOperation(operation)
-                        && operation.mClientMonitor.getToken() == token) {
+                if (canCancelAuthOperation(operation, token, requestId)) {
                     Slog.d(getTag(), "Marking " + operation
                             + " as STATE_WAITING_IN_QUEUE_CANCELING");
                     operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
@@ -676,10 +671,26 @@
         }
     }
 
-    private boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) {
-        final boolean isAuthentication = operation.mClientMonitor
-                instanceof AuthenticationConsumer;
-        final boolean isDetection = operation.mClientMonitor instanceof DetectionConsumer;
+    private static boolean canCancelAuthOperation(Operation operation, IBinder token,
+            long requestId) {
+        // TODO: restrict callers that can cancel without requestId (negative value)?
+        return isAuthenticationOrDetectionOperation(operation)
+                && operation.mClientMonitor.getToken() == token
+                && isMatchingRequestId(operation, requestId);
+    }
+
+    // By default, monitors are not associated with a request id to retain the original
+    // behavior (i.e. if no requestId is explicitly set then assume it matches)
+    private static boolean isMatchingRequestId(Operation operation, long requestId) {
+        return !operation.mClientMonitor.hasRequestId()
+                || operation.mClientMonitor.getRequestId() == requestId;
+    }
+
+    private static boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) {
+        final boolean isAuthentication =
+                operation.mClientMonitor instanceof AuthenticationConsumer;
+        final boolean isDetection =
+                operation.mClientMonitor instanceof DetectionConsumer;
         return isAuthentication || isDetection;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index 0bc4f1b..b2fd46d1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -61,10 +61,11 @@
     @Override
     public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
             long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
-            String opPackageName, int cookie, boolean allowBackgroundAuthentication)
+            String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication)
             throws RemoteException {
         mFaceService.prepareForAuthentication(mSensorId, requireConfirmation, token, operationId,
-                userId, sensorReceiver, opPackageName, cookie, allowBackgroundAuthentication);
+                userId, sensorReceiver, opPackageName, requestId, cookie,
+                allowBackgroundAuthentication);
     }
 
     @Override
@@ -73,9 +74,9 @@
     }
 
     @Override
-    public void cancelAuthenticationFromService(IBinder token, String opPackageName)
+    public void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId)
             throws RemoteException {
-        mFaceService.cancelAuthenticationFromService(mSensorId, token, opPackageName);
+        mFaceService.cancelAuthenticationFromService(mSensorId, token, opPackageName, requestId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 12d6b08..675ee545 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -250,7 +250,7 @@
         }
 
         @Override // Binder call
-        public void authenticate(final IBinder token, final long operationId, int userId,
+        public long authenticate(final IBinder token, final long operationId, int userId,
                 final IFaceServiceReceiver receiver, final String opPackageName,
                 boolean isKeyguardBypassEnabled) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
@@ -270,38 +270,38 @@
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
             if (provider == null) {
                 Slog.w(TAG, "Null provider for authenticate");
-                return;
+                return -1;
             }
 
-            provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
+            return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
                     0 /* cookie */,
                     new ClientMonitorCallbackConverter(receiver), opPackageName, restricted,
                     statsClient, isKeyguard, isKeyguardBypassEnabled);
         }
 
         @Override // Binder call
-        public void detectFace(final IBinder token, final int userId,
+        public long detectFace(final IBinder token, final int userId,
                 final IFaceServiceReceiver receiver, final String opPackageName) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
             if (!Utils.isKeyguard(getContext(), opPackageName)) {
                 Slog.w(TAG, "detectFace called from non-sysui package: " + opPackageName);
-                return;
+                return -1;
             }
 
             if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
                 // If this happens, something in KeyguardUpdateMonitor is wrong. This should only
                 // ever be invoked when the user is encrypted or lockdown.
                 Slog.e(TAG, "detectFace invoked when user is not encrypted or lockdown");
-                return;
+                return -1;
             }
 
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
             if (provider == null) {
                 Slog.w(TAG, "Null provider for detectFace");
-                return;
+                return -1;
             }
 
-            provider.second.scheduleFaceDetect(provider.first, token, userId,
+            return provider.second.scheduleFaceDetect(provider.first, token, userId,
                     new ClientMonitorCallbackConverter(receiver), opPackageName,
                     BiometricsProtoEnums.CLIENT_KEYGUARD);
         }
@@ -309,8 +309,8 @@
         @Override // Binder call
         public void prepareForAuthentication(int sensorId, boolean requireConfirmation,
                 IBinder token, long operationId, int userId,
-                IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie,
-                boolean allowBackgroundAuthentication) {
+                IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId,
+                int cookie, boolean allowBackgroundAuthentication) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
 
             final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -322,9 +322,9 @@
             final boolean isKeyguardBypassEnabled = false; // only valid for keyguard clients
             final boolean restricted = true; // BiometricPrompt is always restricted
             provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie,
-                    new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted,
-                    BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, allowBackgroundAuthentication,
-                    isKeyguardBypassEnabled);
+                    new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId,
+                    restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+                    allowBackgroundAuthentication, isKeyguardBypassEnabled);
         }
 
         @Override // Binder call
@@ -341,7 +341,8 @@
         }
 
         @Override // Binder call
-        public void cancelAuthentication(final IBinder token, final String opPackageName) {
+        public void cancelAuthentication(final IBinder token, final String opPackageName,
+                final long requestId) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
 
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -350,11 +351,12 @@
                 return;
             }
 
-            provider.second.cancelAuthentication(provider.first, token);
+            provider.second.cancelAuthentication(provider.first, token, requestId);
         }
 
         @Override // Binder call
-        public void cancelFaceDetect(final IBinder token, final String opPackageName) {
+        public void cancelFaceDetect(final IBinder token, final String opPackageName,
+                final long requestId) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
             if (!Utils.isKeyguard(getContext(), opPackageName)) {
                 Slog.w(TAG, "cancelFaceDetect called from non-sysui package: "
@@ -368,12 +370,12 @@
                 return;
             }
 
-            provider.second.cancelFaceDetect(provider.first, token);
+            provider.second.cancelFaceDetect(provider.first, token, requestId);
         }
 
         @Override // Binder call
         public void cancelAuthenticationFromService(int sensorId, final IBinder token,
-                final String opPackageName) {
+                final String opPackageName, final long requestId) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
 
             final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -382,7 +384,7 @@
                 return;
             }
 
-            provider.cancelAuthentication(sensorId, token);
+            provider.cancelAuthentication(sensorId, token, requestId);
         }
 
         @Override // Binder call
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 93ab1b6..e099ba3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -101,18 +101,23 @@
 
     void cancelEnrollment(int sensorId, @NonNull IBinder token);
 
-    void scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId,
+    long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
             int statsClient);
 
-    void cancelFaceDetect(int sensorId, @NonNull IBinder token);
+    void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId);
 
-    void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+    long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
             int cookie, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull String opPackageName, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled);
 
-    void cancelAuthentication(int sensorId, @NonNull IBinder token);
+    void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+            int cookie, @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
+            boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled);
+
+    void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId);
 
     void scheduleRemove(int sensorId, @NonNull IBinder token, int faceId, int userId,
             @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index d66a279..cbceba6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -65,7 +65,8 @@
     @FaceManager.FaceAcquired private int mLastAcquire = FaceManager.FACE_ACQUIRED_UNKNOWN;
 
     FaceAuthenticationClient(@NonNull Context context,
-            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+            @NonNull LazyDaemon<ISession> lazyDaemon,
+            @NonNull IBinder token, long requestId,
             @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
             boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
             boolean isStrongBiometric, int statsClient, @NonNull UsageStats usageStats,
@@ -76,6 +77,7 @@
                 BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
                 lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */,
                 isKeyguardBypassEnabled);
+        setRequestId(requestId);
         mUsageStats = usageStats;
         mLockoutCache = lockoutCache;
         mNotificationManager = context.getSystemService(NotificationManager.class);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 1e73ac5..2ef0911 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -43,11 +43,13 @@
     @Nullable private ICancellationSignal mCancellationSignal;
 
     public FaceDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
-            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull IBinder token, long requestId,
+            @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull String owner, int sensorId, boolean isStrongBiometric, int statsClient) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
                 true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FACE,
                 BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+        setRequestId(requestId);
         mIsStrongBiometric = isStrongBiometric;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 718b9da..4bae775 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -65,6 +65,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * Provider for a single instance of the {@link IFace} HAL.
@@ -83,6 +84,8 @@
     @NonNull private final UsageStats mUsageStats;
     @NonNull private final ActivityTaskManager mActivityTaskManager;
     @NonNull private final BiometricTaskStackListener mTaskStackListener;
+    // for requests that do not use biometric prompt
+    @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
 
     @Nullable private IFace mDaemon;
 
@@ -110,8 +113,8 @@
                                 && !client.isAlreadyDone()) {
                             Slog.e(getTag(), "Stopping background authentication, top: "
                                     + topPackage + " currentClient: " + client);
-                            mSensors.valueAt(i).getScheduler()
-                                    .cancelAuthenticationOrDetection(client.getToken());
+                            mSensors.valueAt(i).getScheduler().cancelAuthenticationOrDetection(
+                                    client.getToken(), client.getRequestId());
                         }
                     }
                 }
@@ -356,34 +359,39 @@
     }
 
     @Override
-    public void scheduleFaceDetect(int sensorId, @NonNull IBinder token,
+    public long scheduleFaceDetect(int sensorId, @NonNull IBinder token,
             int userId, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull String opPackageName, int statsClient) {
+        final long id = mRequestCounter.incrementAndGet();
+
         mHandler.post(() -> {
             final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
             final FaceDetectClient client = new FaceDetectClient(mContext,
-                    mSensors.get(sensorId).getLazySession(), token, callback, userId, opPackageName,
+                    mSensors.get(sensorId).getLazySession(),
+                    token, id, callback, userId, opPackageName,
                     sensorId, isStrongBiometric, statsClient);
             scheduleForSensor(sensorId, client);
         });
+
+        return id;
     }
 
     @Override
-    public void cancelFaceDetect(int sensorId, @NonNull IBinder token) {
+    public void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId) {
         mHandler.post(() -> mSensors.get(sensorId).getScheduler()
-                .cancelAuthenticationOrDetection(token));
+                .cancelAuthenticationOrDetection(token, requestId));
     }
 
     @Override
     public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
             int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
-            @NonNull String opPackageName, boolean restricted, int statsClient,
+            @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) {
         mHandler.post(() -> {
             final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
             final FaceAuthenticationClient client = new FaceAuthenticationClient(
-                    mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId,
-                    operationId, restricted, opPackageName, cookie,
+                    mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
+                    userId, operationId, restricted, opPackageName, cookie,
                     false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
                     mUsageStats, mSensors.get(sensorId).getLockoutCache(),
                     allowBackgroundAuthentication, isKeyguardBypassEnabled);
@@ -392,9 +400,23 @@
     }
 
     @Override
-    public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
+    public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+            int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull String opPackageName, boolean restricted, int statsClient,
+            boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) {
+        final long id = mRequestCounter.incrementAndGet();
+
+        scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback,
+                opPackageName, id, restricted, statsClient,
+                allowBackgroundAuthentication, isKeyguardBypassEnabled);
+
+        return id;
+    }
+
+    @Override
+    public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) {
         mHandler.post(() -> mSensors.get(sensorId).getScheduler()
-                .cancelAuthenticationOrDetection(token));
+                .cancelAuthenticationOrDetection(token, requestId));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index d05333d..f4dcbbb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -87,6 +87,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * Supports a single instance of the {@link android.hardware.biometrics.face.V1_0} or its extended
@@ -115,6 +116,8 @@
     @NonNull private final Map<Integer, Long> mAuthenticatorIds;
     @Nullable private IBiometricsFace mDaemon;
     @NonNull private final HalResultController mHalResultController;
+    // for requests that do not use biometric prompt
+    @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
     private int mCurrentUserId = UserHandle.USER_NULL;
     private final int mSensorId;
     private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
@@ -605,7 +608,7 @@
     }
 
     @Override
-    public void scheduleFaceDetect(int sensorId, @NonNull IBinder token,
+    public long scheduleFaceDetect(int sensorId, @NonNull IBinder token,
             int userId, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull String opPackageName, int statsClient) {
         throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you"
@@ -613,7 +616,7 @@
     }
 
     @Override
-    public void cancelFaceDetect(int sensorId, @NonNull IBinder token) {
+    public void cancelFaceDetect(int sensorId, @NonNull IBinder token, long requestId) {
         throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did you"
                 + "forget to check the supportsFaceDetection flag?");
     }
@@ -621,26 +624,38 @@
     @Override
     public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
             int userId, int cookie, @NonNull ClientMonitorCallbackConverter receiver,
-            @NonNull String opPackageName, boolean restricted, int statsClient,
+            @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) {
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
             final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId);
             final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
-                    mLazyDaemon, token, receiver, userId, operationId, restricted, opPackageName,
-                    cookie, false /* requireConfirmation */, mSensorId, isStrongBiometric,
-                    statsClient, mLockoutTracker, mUsageStats, allowBackgroundAuthentication,
-                    isKeyguardBypassEnabled);
+                    mLazyDaemon, token, requestId, receiver, userId, operationId, restricted,
+                    opPackageName, cookie, false /* requireConfirmation */, mSensorId,
+                    isStrongBiometric, statsClient, mLockoutTracker, mUsageStats,
+                    allowBackgroundAuthentication, isKeyguardBypassEnabled);
             mScheduler.scheduleClientMonitor(client);
         });
     }
 
     @Override
-    public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
-        mHandler.post(() -> {
-            mScheduler.cancelAuthenticationOrDetection(token);
-        });
+    public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+            int userId, int cookie, @NonNull ClientMonitorCallbackConverter receiver,
+            @NonNull String opPackageName, boolean restricted, int statsClient,
+            boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) {
+        final long id = mRequestCounter.incrementAndGet();
+
+        scheduleAuthenticate(sensorId, token, operationId, userId, cookie, receiver,
+                opPackageName, id, restricted, statsClient,
+                allowBackgroundAuthentication, isKeyguardBypassEnabled);
+
+        return id;
+    }
+
+    @Override
+    public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) {
+        mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token, requestId));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 33950af..40f2801 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -57,7 +57,8 @@
     private int mLastAcquire;
 
     FaceAuthenticationClient(@NonNull Context context,
-            @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
+            @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+            @NonNull IBinder token, long requestId,
             @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
             boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
             boolean isStrongBiometric, int statsClient, @NonNull LockoutTracker lockoutTracker,
@@ -68,6 +69,7 @@
                 BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
                 lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */,
                 isKeyguardBypassEnabled);
+        setRequestId(requestId);
         mUsageStats = usageStats;
 
         final Resources resources = getContext().getResources();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index 1e59429..52d887a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -61,10 +61,10 @@
     @Override
     public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
             long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
-            String opPackageName, int cookie, boolean allowBackgroundAuthentication)
+            String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication)
             throws RemoteException {
         mFingerprintService.prepareForAuthentication(mSensorId, token, operationId, userId,
-                sensorReceiver, opPackageName, cookie, allowBackgroundAuthentication);
+                sensorReceiver, opPackageName, requestId, cookie, allowBackgroundAuthentication);
     }
 
     @Override
@@ -73,9 +73,10 @@
     }
 
     @Override
-    public void cancelAuthenticationFromService(IBinder token, String opPackageName)
+    public void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId)
             throws RemoteException {
-        mFingerprintService.cancelAuthenticationFromService(mSensorId, token, opPackageName);
+        mFingerprintService.cancelAuthenticationFromService(
+                mSensorId, token, opPackageName, requestId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 85817be..fa02c51 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -245,7 +245,7 @@
 
         @SuppressWarnings("deprecation")
         @Override // Binder call
-        public void authenticate(final IBinder token, final long operationId,
+        public long authenticate(final IBinder token, final long operationId,
                 final int sensorId, final int userId, final IFingerprintServiceReceiver receiver,
                 final String opPackageName) {
             final int callingUid = Binder.getCallingUid();
@@ -255,7 +255,7 @@
             if (!canUseFingerprint(opPackageName, true /* requireForeground */, callingUid,
                     callingPid, callingUserId)) {
                 Slog.w(TAG, "Authenticate rejecting package: " + opPackageName);
-                return;
+                return -1;
             }
 
             // Keyguard check must be done on the caller's binder identity, since it also checks
@@ -270,7 +270,7 @@
                     // SafetyNet for b/79776455
                     EventLog.writeEvent(0x534e4554, "79776455");
                     Slog.e(TAG, "Authenticate invoked when user is encrypted or lockdown");
-                    return;
+                    return -1;
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity1);
@@ -290,7 +290,7 @@
             }
             if (provider == null) {
                 Slog.w(TAG, "Null provider for authenticate");
-                return;
+                return -1;
             }
 
             final FingerprintSensorPropertiesInternal sensorProps =
@@ -299,18 +299,17 @@
                     && sensorProps != null && sensorProps.isAnyUdfpsType()) {
                 final long identity2 = Binder.clearCallingIdentity();
                 try {
-                    authenticateWithPrompt(operationId, sensorProps, userId, receiver);
+                    return authenticateWithPrompt(operationId, sensorProps, userId, receiver);
                 } finally {
                     Binder.restoreCallingIdentity(identity2);
                 }
-            } else {
-                provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
-                        0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
-                        restricted, statsClient, isKeyguard, mFingerprintStateCallback);
             }
+            return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
+                    0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
+                    restricted, statsClient, isKeyguard, mFingerprintStateCallback);
         }
 
-        private void authenticateWithPrompt(
+        private long authenticateWithPrompt(
                 final long operationId,
                 @NonNull final FingerprintSensorPropertiesInternal props,
                 final int userId,
@@ -387,33 +386,33 @@
                         }
                     };
 
-            biometricPrompt.authenticateUserForOperation(
+            return biometricPrompt.authenticateUserForOperation(
                     new CancellationSignal(), executor, promptCallback, userId, operationId);
         }
 
         @Override
-        public void detectFingerprint(final IBinder token, final int userId,
+        public long detectFingerprint(final IBinder token, final int userId,
                 final IFingerprintServiceReceiver receiver, final String opPackageName) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
             if (!Utils.isKeyguard(getContext(), opPackageName)) {
                 Slog.w(TAG, "detectFingerprint called from non-sysui package: " + opPackageName);
-                return;
+                return -1;
             }
 
             if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
                 // If this happens, something in KeyguardUpdateMonitor is wrong. This should only
                 // ever be invoked when the user is encrypted or lockdown.
                 Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown");
-                return;
+                return -1;
             }
 
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
             if (provider == null) {
                 Slog.w(TAG, "Null provider for detectFingerprint");
-                return;
+                return -1;
             }
 
-            provider.second.scheduleFingerDetect(provider.first, token, userId,
+            return provider.second.scheduleFingerDetect(provider.first, token, userId,
                     new ClientMonitorCallbackConverter(receiver), opPackageName,
                     BiometricsProtoEnums.CLIENT_KEYGUARD, mFingerprintStateCallback);
         }
@@ -421,7 +420,7 @@
         @Override // Binder call
         public void prepareForAuthentication(int sensorId, IBinder token, long operationId,
                 int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
-                int cookie, boolean allowBackgroundAuthentication) {
+                long requestId, int cookie, boolean allowBackgroundAuthentication) {
             Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
 
             final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -432,9 +431,9 @@
 
             final boolean restricted = true; // BiometricPrompt is always restricted
             provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie,
-                    new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted,
-                    BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, allowBackgroundAuthentication,
-                    mFingerprintStateCallback);
+                    new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId,
+                    restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+                    allowBackgroundAuthentication, mFingerprintStateCallback);
         }
 
         @Override // Binder call
@@ -452,7 +451,8 @@
 
 
         @Override // Binder call
-        public void cancelAuthentication(final IBinder token, final String opPackageName) {
+        public void cancelAuthentication(final IBinder token, final String opPackageName,
+                long requestId) {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
             final int callingUserId = UserHandle.getCallingUserId();
@@ -469,11 +469,12 @@
                 return;
             }
 
-            provider.second.cancelAuthentication(provider.first, token);
+            provider.second.cancelAuthentication(provider.first, token, requestId);
         }
 
         @Override // Binder call
-        public void cancelFingerprintDetect(final IBinder token, final String opPackageName) {
+        public void cancelFingerprintDetect(final IBinder token, final String opPackageName,
+                final long requestId) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
             if (!Utils.isKeyguard(getContext(), opPackageName)) {
                 Slog.w(TAG, "cancelFingerprintDetect called from non-sysui package: "
@@ -489,12 +490,12 @@
                 return;
             }
 
-            provider.second.cancelAuthentication(provider.first, token);
+            provider.second.cancelAuthentication(provider.first, token, requestId);
         }
 
         @Override // Binder call
         public void cancelAuthenticationFromService(final int sensorId, final IBinder token,
-                final String opPackageName) {
+                final String opPackageName, final long requestId) {
 
             Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
 
@@ -506,7 +507,7 @@
                 return;
             }
 
-            provider.cancelAuthentication(sensorId, token);
+            provider.cancelAuthentication(sensorId, token, requestId);
         }
 
         @Override // Binder call
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 706ac10..b9fcd8e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -95,20 +95,26 @@
 
     void cancelEnrollment(int sensorId, @NonNull IBinder token);
 
-    void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
+    long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
             int statsClient,
             @NonNull FingerprintStateCallback fingerprintStateCallback);
 
     void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
             int cookie, @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
+            boolean allowBackgroundAuthentication,
+            @NonNull FingerprintStateCallback fingerprintStateCallback);
+
+    long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+            int cookie, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull String opPackageName, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication,
             @NonNull FingerprintStateCallback fingerprintStateCallback);
 
     void startPreparedClient(int sensorId, int cookie);
 
-    void cancelAuthentication(int sensorId, @NonNull IBinder token);
+    void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId);
 
     void scheduleRemove(int sensorId, @NonNull IBinder token,
             @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 37ee76a..9d911e0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -61,7 +61,8 @@
     private boolean mIsPointerDown;
 
     FingerprintAuthenticationClient(@NonNull Context context,
-            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+            @NonNull LazyDaemon<ISession> lazyDaemon,
+            @NonNull IBinder token, long requestId,
             @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
             boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
             int sensorId, boolean isStrongBiometric, int statsClient,
@@ -74,6 +75,7 @@
                 BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
                 lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */,
                 false /* isKeyguardBypassEnabled */);
+        setRequestId(requestId);
         mLockoutCache = lockoutCache;
         mUdfpsOverlayController = udfpsOverlayController;
         mSensorProps = sensorProps;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index c5dc449..da91cdd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -47,13 +47,15 @@
     @Nullable private ICancellationSignal mCancellationSignal;
 
     FingerprintDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
-            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull IBinder token, long requestId,
+            @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull String owner, int sensorId,
             @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric,
             int statsClient) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
                 true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT,
                 BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+        setRequestId(requestId);
         mIsStrongBiometric = isStrongBiometric;
         mUdfpsOverlayController = udfpsOverlayController;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 102b074..377feca 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -71,6 +71,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * Provider for a single instance of the {@link IFingerprint} HAL.
@@ -88,6 +89,8 @@
     @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
     @NonNull private final ActivityTaskManager mActivityTaskManager;
     @NonNull private final BiometricTaskStackListener mTaskStackListener;
+    // for requests that do not use biometric prompt
+    @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
 
     @Nullable private IFingerprint mDaemon;
     @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@@ -118,8 +121,8 @@
                                 && !client.isAlreadyDone()) {
                             Slog.e(getTag(), "Stopping background authentication, top: "
                                     + topPackage + " currentClient: " + client);
-                            mSensors.valueAt(i).getScheduler()
-                                    .cancelAuthenticationOrDetection(client.getToken());
+                            mSensors.valueAt(i).getScheduler().cancelAuthenticationOrDetection(
+                                    client.getToken(), client.getRequestId());
                         }
                     }
                 }
@@ -369,31 +372,35 @@
     }
 
     @Override
-    public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
+    public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
             int statsClient,
             @NonNull FingerprintStateCallback fingerprintStateCallback) {
+        final long id = mRequestCounter.incrementAndGet();
+
         mHandler.post(() -> {
             final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
             final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
-                    mSensors.get(sensorId).getLazySession(), token, callback, userId,
+                    mSensors.get(sensorId).getLazySession(), token, id, callback, userId,
                     opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric,
                     statsClient);
             scheduleForSensor(sensorId, client, fingerprintStateCallback);
         });
+
+        return id;
     }
 
     @Override
     public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
             int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
-            @NonNull String opPackageName, boolean restricted, int statsClient,
+            @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication,
             @NonNull FingerprintStateCallback fingerprintStateCallback) {
         mHandler.post(() -> {
             final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
             final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
-                    mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId,
-                    operationId, restricted, opPackageName, cookie,
+                    mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
+                    userId, operationId, restricted, opPackageName, cookie,
                     false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
                     mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
                     mUdfpsOverlayController, allowBackgroundAuthentication,
@@ -403,14 +410,29 @@
     }
 
     @Override
+    public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+            int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull String opPackageName, boolean restricted, int statsClient,
+            boolean allowBackgroundAuthentication,
+            @NonNull FingerprintStateCallback fingerprintStateCallback) {
+        final long id = mRequestCounter.incrementAndGet();
+
+        scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback,
+                opPackageName, id, restricted, statsClient, allowBackgroundAuthentication,
+                fingerprintStateCallback);
+
+        return id;
+    }
+
+    @Override
     public void startPreparedClient(int sensorId, int cookie) {
         mHandler.post(() -> mSensors.get(sensorId).getScheduler().startPreparedClient(cookie));
     }
 
     @Override
-    public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
+    public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) {
         mHandler.post(() -> mSensors.get(sensorId).getScheduler()
-                .cancelAuthenticationOrDetection(token));
+                .cancelAuthenticationOrDetection(token, requestId));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 2f5b5c7..f17bcc8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -88,6 +88,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * Supports a single instance of the {@link android.hardware.biometrics.fingerprint.V2_1} or
@@ -115,6 +116,8 @@
     @NonNull private final HalResultController mHalResultController;
     @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
     @Nullable private ISidefpsController mSidefpsController;
+    // for requests that do not use biometric prompt
+    @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
     private int mCurrentUserId = UserHandle.USER_NULL;
     private final boolean mIsUdfps;
     private final int mSensorId;
@@ -142,7 +145,8 @@
                             && !client.isAlreadyDone()) {
                         Slog.e(TAG, "Stopping background authentication, top: "
                                 + topPackage + " currentClient: " + client);
-                        mScheduler.cancelAuthenticationOrDetection(client.getToken());
+                        mScheduler.cancelAuthenticationOrDetection(
+                                client.getToken(), client.getRequestId());
                     }
                 }
             });
@@ -591,26 +595,30 @@
     }
 
     @Override
-    public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
+    public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
             int statsClient,
             @NonNull FingerprintStateCallback fingerprintStateCallback) {
+        final long id = mRequestCounter.incrementAndGet();
+
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
             final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
             final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
-                    mLazyDaemon, token, listener, userId, opPackageName,
+                    mLazyDaemon, token, id, listener, userId, opPackageName,
                     mSensorProperties.sensorId, mUdfpsOverlayController, isStrongBiometric,
                     statsClient);
             mScheduler.scheduleClientMonitor(client, fingerprintStateCallback);
         });
+
+        return id;
     }
 
     @Override
     public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
             int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
-            @NonNull String opPackageName, boolean restricted, int statsClient,
+            @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
             boolean allowBackgroundAuthentication,
             @NonNull FingerprintStateCallback fingerprintStateCallback) {
         mHandler.post(() -> {
@@ -618,8 +626,8 @@
 
             final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
             final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
-                    mContext, mLazyDaemon, token, listener, userId, operationId, restricted,
-                    opPackageName, cookie, false /* requireConfirmation */,
+                    mContext, mLazyDaemon, token, requestId, listener, userId, operationId,
+                    restricted, opPackageName, cookie, false /* requireConfirmation */,
                     mSensorProperties.sensorId, isStrongBiometric, statsClient,
                     mTaskStackListener, mLockoutTracker, mUdfpsOverlayController,
                     allowBackgroundAuthentication, mSensorProperties);
@@ -628,14 +636,29 @@
     }
 
     @Override
+    public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+            int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
+            @NonNull String opPackageName, boolean restricted, int statsClient,
+            boolean allowBackgroundAuthentication,
+            @NonNull FingerprintStateCallback fingerprintStateCallback) {
+        final long id = mRequestCounter.incrementAndGet();
+
+        scheduleAuthenticate(sensorId, token, operationId, userId, cookie, listener,
+                opPackageName, id, restricted, statsClient, allowBackgroundAuthentication,
+                fingerprintStateCallback);
+
+        return id;
+    }
+
+    @Override
     public void startPreparedClient(int sensorId, int cookie) {
         mHandler.post(() -> mScheduler.startPreparedClient(cookie));
     }
 
     @Override
-    public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
+    public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) {
         Slog.d(TAG, "cancelAuthentication, sensorId: " + sensorId);
-        mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token));
+        mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token, requestId));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 5060744..7d95ec0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -59,7 +59,8 @@
     private boolean mIsPointerDown;
 
     FingerprintAuthenticationClient(@NonNull Context context,
-            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon,
+            @NonNull IBinder token, long requestId,
             @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
             boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
             int sensorId, boolean isStrongBiometric, int statsClient,
@@ -73,6 +74,7 @@
                 BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
                 lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */,
                 false /* isKeyguardBypassEnabled */);
+        setRequestId(requestId);
         mLockoutFrameworkImpl = lockoutTracker;
         mUdfpsOverlayController = udfpsOverlayController;
         mSensorProps = sensorProps;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 8e73ee6b..147a206 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -52,13 +52,15 @@
     private boolean mIsPointerDown;
 
     public FingerprintDetectClient(@NonNull Context context,
-            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+            @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon,
+            @NonNull IBinder token, long requestId,
             @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
             int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController,
             boolean isStrongBiometric, int statsClient) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
                 true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT,
                 BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+        setRequestId(requestId);
         mUdfpsOverlayController = udfpsOverlayController;
         mIsStrongBiometric = isStrongBiometric;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
index 4918185..5c0c362 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
@@ -59,7 +59,7 @@
     @Override
     public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
             long sessionId, int userId, IBiometricSensorReceiver sensorReceiver,
-            String opPackageName, int cookie, boolean allowBackgroundAuthentication)
+            String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication)
             throws RemoteException {
     }
 
@@ -68,7 +68,7 @@
     }
 
     @Override
-    public void cancelAuthenticationFromService(IBinder token, String opPackageName)
+    public void cancelAuthenticationFromService(IBinder token, String opPackageName, long requestId)
             throws RemoteException {
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index a25cfd8..e16a241 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -41,6 +41,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.IndentingPrintWriter;
@@ -61,7 +62,6 @@
 import com.android.server.sensors.SensorManagerInternal;
 import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
 import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index fa84d12..27a78dd 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location.contexthub;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.PendingIntent;
@@ -27,12 +28,6 @@
 import android.database.ContentObserver;
 import android.hardware.SensorPrivacyManager;
 import android.hardware.SensorPrivacyManagerInternal;
-import android.hardware.contexthub.V1_0.AsyncEventType;
-import android.hardware.contexthub.V1_0.ContextHubMsg;
-import android.hardware.contexthub.V1_0.Result;
-import android.hardware.contexthub.V1_0.TransactionResult;
-import android.hardware.contexthub.V1_2.HubAppInfo;
-import android.hardware.contexthub.V1_2.IContexthubCallback;
 import android.hardware.location.ContextHubInfo;
 import android.hardware.location.ContextHubMessage;
 import android.hardware.location.ContextHubTransaction;
@@ -66,6 +61,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
@@ -96,6 +93,20 @@
 
     private static final int OS_APP_INSTANCE = -1;
 
+    /**
+     * Constants describing an async event from the Context Hub.
+     * {@hide}
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "CONTEXT_HUB_EVENT_" }, value = {
+            CONTEXT_HUB_EVENT_UNKNOWN,
+            CONTEXT_HUB_EVENT_RESTARTED,
+    })
+    public @interface Type { }
+
+    public static final int CONTEXT_HUB_EVENT_UNKNOWN = 0;
+    public static final int CONTEXT_HUB_EVENT_RESTARTED = 1;
+
     /*
      * Local flag to enable debug logging.
      */
@@ -135,7 +146,7 @@
     /**
      * Class extending the callback to register with a Context Hub.
      */
-    private class ContextHubServiceCallback extends IContexthubCallback.Stub {
+    private class ContextHubServiceCallback implements IContextHubWrapper.ICallback {
         private final int mContextHubId;
 
         ContextHubServiceCallback(int contextHubId) {
@@ -143,46 +154,31 @@
         }
 
         @Override
-        public void handleClientMsg(ContextHubMsg message) {
-            handleClientMessageCallback(mContextHubId, message,
-                    Collections.emptyList() /* nanoappPermissions */,
-                    Collections.emptyList() /* messagePermissions */);
+        public void handleTransactionResult(int transactionId, boolean success) {
+            handleTransactionResultCallback(mContextHubId, transactionId, success);
         }
 
         @Override
-        public void handleTxnResult(int transactionId, int result) {
-            handleTransactionResultCallback(mContextHubId, transactionId,
-                    result == TransactionResult.SUCCESS);
-        }
-
-        @Override
-        public void handleHubEvent(int eventType) {
+        public void handleContextHubEvent(int eventType) {
             handleHubEventCallback(mContextHubId, eventType);
         }
 
         @Override
-        public void handleAppAbort(long nanoAppId, int abortCode) {
-            handleAppAbortCallback(mContextHubId, nanoAppId, abortCode);
+        public void handleNanoappAbort(long nanoappId, int abortCode) {
+            handleAppAbortCallback(mContextHubId, nanoappId, abortCode);
         }
 
         @Override
-        public void handleAppsInfo(
-                ArrayList<android.hardware.contexthub.V1_0.HubAppInfo> nanoAppInfoList) {
-            handleQueryAppsCallback(mContextHubId,
-                    ContextHubServiceUtil.toHubAppInfo_1_2(nanoAppInfoList));
+        public void handleNanoappInfo(List<NanoAppState> nanoappStateList) {
+            handleQueryAppsCallback(mContextHubId, nanoappStateList);
         }
 
         @Override
-        public void handleClientMsg_1_2(android.hardware.contexthub.V1_2.ContextHubMsg message,
-                ArrayList<String> messagePermissions) {
-            handleClientMessageCallback(mContextHubId, message.msg_1_0, message.permissions,
+        public void handleNanoappMessage(short hostEndpointId, NanoAppMessage message,
+                List<String> nanoappPermissions, List<String> messagePermissions) {
+            handleClientMessageCallback(mContextHubId, hostEndpointId, message, nanoappPermissions,
                     messagePermissions);
         }
-
-        @Override
-        public void handleAppsInfo_1_2(ArrayList<HubAppInfo> nanoAppInfoList) {
-            handleQueryAppsCallback(mContextHubId, nanoAppInfoList);
-        }
     }
 
     public ContextHubService(Context context) {
@@ -329,7 +325,7 @@
 
             @Override
             public void onHubReset() {
-                byte[] data = {TransactionResult.SUCCESS};
+                byte[] data = {android.hardware.contexthub.V1_0.TransactionResult.SUCCESS};
                 onMessageReceiptOldApi(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
             }
 
@@ -565,12 +561,12 @@
      * cache or as a result of an explicit query requested by a client through the sendMessage API.
      *
      * @param contextHubId the ID of the hub to do the query
-     * @return the result of the query
+     * @return true if the query succeeded
      * @throws IllegalStateException if the transaction queue is full
      */
-    private int queryNanoAppsInternal(int contextHubId) {
+    private boolean queryNanoAppsInternal(int contextHubId) {
         if (mContextHubWrapper == null) {
-            return Result.UNKNOWN_FAILURE;
+            return false;
         }
 
         IContextHubTransactionCallback onCompleteCallback =
@@ -579,7 +575,7 @@
                 contextHubId, onCompleteCallback, getCallingPackageName());
 
         mTransactionManager.addTransaction(transaction);
-        return Result.OK;
+        return true;
     }
 
     @Override
@@ -605,7 +601,7 @@
         boolean success = false;
         if (nanoAppHandle == OS_APP_INSTANCE) {
             if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
-                success = (queryNanoAppsInternal(contextHubHandle) == Result.OK);
+                success = queryNanoAppsInternal(contextHubHandle);
             } else {
                 Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
             }
@@ -631,16 +627,16 @@
      * Handles a unicast or broadcast message from a nanoapp.
      *
      * @param contextHubId   the ID of the hub the message came from
+     * @param hostEndpointId the host endpoint ID of the client receiving this message
      * @param message        the message contents
      * @param reqPermissions the permissions required to consume this message
      */
     private void handleClientMessageCallback(
-            int contextHubId, ContextHubMsg message, List<String> nanoappPermissions,
+            int contextHubId, short hostEndpointId, NanoAppMessage message,
+            List<String> nanoappPermissions,
             List<String> messagePermissions) {
-        NanoAppMessage clientMessage = ContextHubServiceUtil.createNanoAppMessage(message);
         mClientManager.onMessageFromNanoApp(
-                contextHubId, message.hostEndPoint, clientMessage, nanoappPermissions,
-                messagePermissions);
+                contextHubId, hostEndpointId, message, nanoappPermissions, messagePermissions);
     }
 
     /**
@@ -690,10 +686,10 @@
      * Handles an asynchronous event from a Context Hub.
      *
      * @param contextHubId the ID of the hub the response came from
-     * @param eventType    the type of the event as defined in Context Hub HAL AsyncEventType
+     * @param eventType    the type of the event as in CONTEXT_HUB_EVENT_*
      */
     private void handleHubEventCallback(int contextHubId, int eventType) {
-        if (eventType == AsyncEventType.RESTARTED) {
+        if (eventType == CONTEXT_HUB_EVENT_RESTARTED) {
             sendLocationSettingUpdate();
             sendWifiSettingUpdate(true /* forceUpdate */);
             sendAirplaneModeSettingUpdate();
@@ -723,15 +719,12 @@
     /**
      * Handles a query response from a Context Hub.
      *
-     * @param contextHubId    the ID of the hub of the response
-     * @param nanoAppInfoList the list of loaded nanoapps
+     * @param contextHubId     the ID of the hub of the response
+     * @param nanoappStateList the list of loaded nanoapps
      */
-    private void handleQueryAppsCallback(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
-        List<NanoAppState> nanoAppStateList =
-                ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList);
-
-        mNanoAppStateManager.updateCache(contextHubId, nanoAppInfoList);
-        mTransactionManager.onQueryResponse(nanoAppStateList);
+    private void handleQueryAppsCallback(int contextHubId, List<NanoAppState> nanoappStateList) {
+        mNanoAppStateManager.updateCache(contextHubId, nanoappStateList);
+        mTransactionManager.onQueryResponse(nanoappStateList);
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index d0e00c4..df6cc05 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -20,6 +20,7 @@
 
 import android.Manifest;
 import android.content.Context;
+import android.hardware.contexthub.V1_0.AsyncEventType;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
 import android.hardware.contexthub.V1_0.HostEndPoint;
 import android.hardware.contexthub.V1_0.Result;
@@ -256,4 +257,21 @@
         }
         return newAppInfo;
     }
+
+    /**
+     * Converts a HIDL AsyncEventType to the corresponding ContextHubService.CONTEXT_HUB_EVENT_*.
+     *
+     * @param hidlEventType The AsyncEventType value.
+     * @return The converted event type.
+     */
+    /* package */
+    static int toContextHubEvent(int hidlEventType) {
+        switch (hidlEventType) {
+            case AsyncEventType.RESTARTED:
+                return ContextHubService.CONTEXT_HUB_EVENT_RESTARTED;
+            default:
+                Log.e(TAG, "toContextHubEvent: Unknown event type: " + hidlEventType);
+                return ContextHubService.CONTEXT_HUB_EVENT_UNKNOWN;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 092e1a1..d733db0 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -15,16 +15,20 @@
  */
 package com.android.server.location.contexthub;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.contexthub.V1_0.ContextHub;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.TransactionResult;
 import android.hardware.contexthub.V1_1.Setting;
 import android.hardware.contexthub.V1_1.SettingValue;
+import android.hardware.contexthub.V1_2.HubAppInfo;
 import android.hardware.contexthub.V1_2.IContexthubCallback;
 import android.hardware.location.ContextHubInfo;
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.NanoAppBinary;
 import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppState;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Pair;
@@ -41,6 +45,45 @@
     private static final String TAG = "IContextHubWrapper";
 
     /**
+     * The callback interface to use in registerCallback.
+     */
+    public interface ICallback {
+        /**
+         * @param transactionId The ID of the transaction that completed.
+         * @param success       true if the transaction succeeded.
+         */
+        void handleTransactionResult(int transactionId, boolean success);
+
+        /**
+         * @param eventType The Context Hub event type defined by ContextHubService
+         *                  .CONTEXT_HUB_EVENT_*.
+         */
+        void handleContextHubEvent(int eventType);
+
+        /**
+         * @param nanoappId The ID of the nanoapp that aborted.
+         * @param abortCode The nanoapp-defined abort code.
+         */
+        void handleNanoappAbort(long nanoappId, int abortCode);
+
+        /**
+         * @param nanoappStateList The list of loaded nanoapps on the Context Hub.
+         */
+        void handleNanoappInfo(List<NanoAppState> nanoappStateList);
+
+        /**
+         * Handles a message from a nanoapp to a ContextHubClient.
+         *
+         * @param hostEndpointId     The host endpoint ID of the recipient.
+         * @param message            The message from the nanoapp.
+         * @param nanoappPermissions The list of permissions held by the nanoapp.
+         * @param messagePermissions The list of permissions required to receive the message.
+         */
+        void handleNanoappMessage(short hostEndpointId, NanoAppMessage message,
+                List<String> nanoappPermissions, List<String> messagePermissions);
+    }
+
+    /**
      * Attempts to connect to the Contexthub HAL 1.0 service, if it exists.
      *
      * @return A valid IContextHubWrapper if the connection was successful, null otherwise.
@@ -103,12 +146,6 @@
     public abstract Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException;
 
     /**
-     * Calls the appropriate registerCallback function depending on the HAL version.
-     */
-    public abstract void registerCallback(
-            int hubId, IContexthubCallback callback) throws RemoteException;
-
-    /**
      * @return True if this version of the Contexthub HAL supports Location setting notifications.
      */
     public abstract boolean supportsLocationSettingNotifications();
@@ -213,11 +250,75 @@
     public abstract int queryNanoapps(int contextHubId) throws RemoteException;
 
     /**
+     * Registers a callback with the Context Hub.
+     *
+     * @param contextHubId The ID of the Context Hub to register the callback with.
+     * @param callback     The callback to register.
+     */
+    public abstract void registerCallback(int contextHubId, @NonNull ICallback callback)
+            throws RemoteException;
+
+    /**
      * An abstract call that defines methods common to all HIDL IContextHubWrappers.
      */
     private abstract static class ContextHubWrapperHidl extends IContextHubWrapper {
         private android.hardware.contexthub.V1_0.IContexthub mHub;
 
+        protected ICallback mCallback = null;
+
+        protected final ContextHubWrapperHidlCallback mHidlCallback =
+                new ContextHubWrapperHidlCallback();
+
+        protected class ContextHubWrapperHidlCallback extends IContexthubCallback.Stub {
+            @Override
+            public void handleClientMsg(ContextHubMsg message) {
+                mCallback.handleNanoappMessage(
+                        message.hostEndPoint,
+                        ContextHubServiceUtil.createNanoAppMessage(message),
+                        Collections.emptyList() /* nanoappPermissions */,
+                        Collections.emptyList() /* messagePermissions */);
+            }
+
+            @Override
+            public void handleTxnResult(int transactionId, int result) {
+                mCallback.handleTransactionResult(transactionId,
+                        result == TransactionResult.SUCCESS);
+            }
+
+            @Override
+            public void handleHubEvent(int eventType) {
+                mCallback.handleContextHubEvent(
+                        ContextHubServiceUtil.toContextHubEvent(eventType));
+            }
+
+            @Override
+            public void handleAppAbort(long nanoAppId, int abortCode) {
+                mCallback.handleNanoappAbort(nanoAppId, abortCode);
+            }
+
+            @Override
+            public void handleAppsInfo(
+                    ArrayList<android.hardware.contexthub.V1_0.HubAppInfo> nanoAppInfoList) {
+                handleAppsInfo_1_2(ContextHubServiceUtil.toHubAppInfo_1_2(nanoAppInfoList));
+            }
+
+            @Override
+            public void handleClientMsg_1_2(android.hardware.contexthub.V1_2.ContextHubMsg message,
+                    ArrayList<String> messagePermissions) {
+                mCallback.handleNanoappMessage(
+                        message.msg_1_0.hostEndPoint,
+                        ContextHubServiceUtil.createNanoAppMessage(message.msg_1_0),
+                        message.permissions, messagePermissions);
+            }
+
+            @Override
+            public void handleAppsInfo_1_2(ArrayList<HubAppInfo> nanoAppInfoList) {
+                List<NanoAppState> nanoAppStateList =
+                        ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList);
+                mCallback.handleNanoappInfo(nanoAppStateList);
+            }
+        }
+
         ContextHubWrapperHidl(android.hardware.contexthub.V1_0.IContexthub hub) {
             mHub = hub;
         }
@@ -267,6 +368,11 @@
             return ContextHubServiceUtil.toTransactionResult(
                     mHub.queryApps(contextHubId));
         }
+
+        public void registerCallback(int contextHubId, ICallback callback) throws RemoteException {
+            mCallback = callback;
+            mHub.registerCallback(contextHubId, mHidlCallback);
+        }
     }
 
     private static class ContextHubWrapperV1_0 extends ContextHubWrapperHidl {
@@ -285,11 +391,6 @@
             return new Pair(hubInfoList, new ArrayList<String>());
         }
 
-        public void registerCallback(
-                int hubId, IContexthubCallback callback) throws RemoteException {
-            mHub.registerCallback(hubId, callback);
-        }
-
         public boolean supportsLocationSettingNotifications() {
             return false;
         }
@@ -335,11 +436,6 @@
             return new Pair(hubInfoList, new ArrayList<String>());
         }
 
-        public void registerCallback(
-                int hubId, IContexthubCallback callback) throws RemoteException {
-            mHub.registerCallback(hubId, callback);
-        }
-
         public boolean supportsLocationSettingNotifications() {
             return true;
         }
@@ -401,11 +497,6 @@
             return mHubInfo;
         }
 
-        public void registerCallback(
-                int hubId, IContexthubCallback callback) throws RemoteException {
-            mHub.registerCallback_1_2(hubId, callback);
-        }
-
         public boolean supportsLocationSettingNotifications() {
             return true;
         }
@@ -445,6 +536,11 @@
                     enabled ? SettingValue.DISABLED : SettingValue.ENABLED);
         }
 
+        public void registerCallback(int contextHubId, ICallback callback) throws RemoteException {
+            mCallback = callback;
+            mHub.registerCallback_1_2(contextHubId, mHidlCallback);
+        }
+
         private void sendSettingChanged(byte setting, byte newValue) {
             try {
                 mHub.onSettingChanged_1_2(setting, newValue);
diff --git a/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java b/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java
index 667fb98..b6d5496 100644
--- a/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java
+++ b/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java
@@ -17,8 +17,8 @@
 package com.android.server.location.contexthub;
 
 import android.annotation.Nullable;
-import android.hardware.contexthub.V1_2.HubAppInfo;
 import android.hardware.location.NanoAppInstanceInfo;
+import android.hardware.location.NanoAppState;
 import android.util.Log;
 
 import java.util.HashMap;
@@ -31,8 +31,8 @@
  * Manages the state of loaded nanoapps at the Context Hubs.
  *
  * This class maintains a list of nanoapps that have been informed as loaded at the hubs. The state
- * should be updated based on the hub callbacks (defined in IContexthubCallback.hal), as a result
- * of either loadNanoApp, unloadNanoApp, or queryApps.
+ * should be updated based on the hub callbacks (defined in IContexthubCallback.hal), as a result of
+ * either loadNanoApp, unloadNanoApp, or queryApps.
  *
  * The state tracked by this manager is used by clients of ContextHubService that use the old APIs.
  *
@@ -61,7 +61,7 @@
     /**
      * @param nanoAppHandle the nanoapp handle
      * @return the NanoAppInstanceInfo for the given nanoapp, or null if the nanoapp does not exist
-     *         in the cache
+     * in the cache
      */
     @Nullable
     /* package */
@@ -83,7 +83,7 @@
 
     /**
      * @param contextHubId the ID of the hub to search for the instance
-     * @param nanoAppId the unique 64-bit ID of the nanoapp
+     * @param nanoAppId    the unique 64-bit ID of the nanoapp
      * @return the nanoapp handle, -1 if the nanoapp is not in the cache
      */
     /* package */
@@ -99,12 +99,12 @@
 
     /**
      * Adds a nanoapp instance to the cache.
-     *
+     * <p>
      * If the cache already contained the nanoapp, the entry is removed and a new nanoapp handle is
      * generated.
      *
-     * @param contextHubId the ID of the hub the nanoapp is loaded in
-     * @param nanoAppId the unique 64-bit ID of the nanoapp
+     * @param contextHubId   the ID of the hub the nanoapp is loaded in
+     * @param nanoAppId      the unique 64-bit ID of the nanoapp
      * @param nanoAppVersion the version of the nanoapp
      */
     /* package */
@@ -147,15 +147,17 @@
     /**
      * Performs a batch update of the nanoapp cache given a nanoapp query response.
      *
-     * @param contextHubId    the ID of the hub the response came from
-     * @param nanoAppInfoList the list of loaded nanoapps
+     * @param contextHubId     the ID of the hub the response came from
+     * @param nanoappStateList the list of loaded nanoapps
      */
     /* package */
-    synchronized void updateCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
+    synchronized void updateCache(int contextHubId, List<NanoAppState> nanoappStateList) {
         HashSet<Long> nanoAppIdSet = new HashSet<>();
-        for (HubAppInfo appInfo : nanoAppInfoList) {
-            handleQueryAppEntry(contextHubId, appInfo.info_1_0.appId, appInfo.info_1_0.version);
-            nanoAppIdSet.add(appInfo.info_1_0.appId);
+        for (NanoAppState nanoappState : nanoappStateList) {
+            handleQueryAppEntry(
+                    contextHubId, nanoappState.getNanoAppId(),
+                    (int) nanoappState.getNanoAppVersion());
+            nanoAppIdSet.add(nanoappState.getNanoAppId());
         }
 
         Iterator<NanoAppInstanceInfo> iterator = mNanoAppHash.values().iterator();
@@ -172,8 +174,8 @@
      * If the nanoapp exists in the cache, then the entry is updated. Otherwise, inserts a new
      * instance of the nanoapp in the cache. This method should only be invoked from updateCache.
      *
-     * @param contextHubId the ID of the hub the nanoapp is loaded in
-     * @param nanoAppId the unique 64-bit ID of the nanoapp
+     * @param contextHubId   the ID of the hub the nanoapp is loaded in
+     * @param nanoAppId      the unique 64-bit ID of the nanoapp
      * @param nanoAppVersion the version of the nanoapp
      */
     private void handleQueryAppEntry(int contextHubId, long nanoAppId, int nanoAppVersion) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 557fa89..df372b1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -290,7 +290,8 @@
                 combined.getValues(augmentStart, augmentEnd, entry);
             }
 
-            final long rawBytes = entry.rxBytes + entry.txBytes;
+            final long rawBytes = (entry.rxBytes + entry.txBytes) == 0 ? 1 :
+                    (entry.rxBytes + entry.txBytes);
             final long rawRxBytes = entry.rxBytes == 0 ? 1 : entry.rxBytes;
             final long rawTxBytes = entry.txBytes == 0 ? 1 : entry.txBytes;
             final long targetBytes = augmentPlan.getDataUsageBytes();
diff --git a/services/core/java/com/android/server/pm/HandlerParams.java b/services/core/java/com/android/server/pm/HandlerParams.java
index b8c2eb8..ab24ee5 100644
--- a/services/core/java/com/android/server/pm/HandlerParams.java
+++ b/services/core/java/com/android/server/pm/HandlerParams.java
@@ -16,20 +16,35 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+
+import android.annotation.NonNull;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInfoLite;
+import android.content.pm.PackageManager;
 import android.os.UserHandle;
+import android.util.Pair;
 import android.util.Slog;
 
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
 import static com.android.server.pm.PackageManagerService.TAG;
 
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
 abstract class HandlerParams {
     /** User handle for the user requesting the information or installation. */
     private final UserHandle mUser;
     String mTraceMethod;
     int mTraceCookie;
+    @NonNull
+    final PackageManagerService mPm;
 
-    HandlerParams(UserHandle user) {
+    // TODO(b/198166813): remove PMS dependency
+    HandlerParams(UserHandle user, PackageManagerService pm) {
         mUser = user;
+        mPm = pm;
     }
 
     UserHandle getUser() {
@@ -54,4 +69,135 @@
 
     abstract void handleStartCopy();
     abstract void handleReturnCode();
+
+    Pair<Integer, String> verifyReplacingVersionCode(PackageInfoLite pkgLite,
+            long requiredInstalledVersionCode, int installFlags) {
+        if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+            return verifyReplacingVersionCodeForApex(
+                    pkgLite, requiredInstalledVersionCode, installFlags);
+        }
+
+        String packageName = pkgLite.packageName;
+        synchronized (mPm.mLock) {
+            // Package which currently owns the data that the new package will own if installed.
+            // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg
+            // will be null whereas dataOwnerPkg will contain information about the package
+            // which was uninstalled while keeping its data.
+            AndroidPackage dataOwnerPkg = mPm.mPackages.get(packageName);
+            if (dataOwnerPkg  == null) {
+                PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+                if (ps != null) {
+                    dataOwnerPkg = ps.pkg;
+                }
+            }
+
+            if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
+                if (dataOwnerPkg == null) {
+                    String errorMsg = "Required installed version code was "
+                            + requiredInstalledVersionCode
+                            + " but package is not installed";
+                    Slog.w(TAG, errorMsg);
+                    return Pair.create(
+                            PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
+                }
+
+                if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
+                    String errorMsg = "Required installed version code was "
+                            + requiredInstalledVersionCode
+                            + " but actual installed version is "
+                            + dataOwnerPkg.getLongVersionCode();
+                    Slog.w(TAG, errorMsg);
+                    return Pair.create(
+                            PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
+                }
+            }
+
+            if (dataOwnerPkg != null) {
+                if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
+                        dataOwnerPkg.isDebuggable())) {
+                    try {
+                        checkDowngrade(dataOwnerPkg, pkgLite);
+                    } catch (PackageManagerException e) {
+                        String errorMsg = "Downgrade detected: " + e.getMessage();
+                        Slog.w(TAG, errorMsg);
+                        return Pair.create(
+                                PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
+                    }
+                }
+            }
+        }
+        return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
+    }
+
+    private Pair<Integer, String> verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite,
+            long requiredInstalledVersionCode, int installFlags) {
+        String packageName = pkgLite.packageName;
+
+        final PackageInfo activePackage = mPm.mApexManager.getPackageInfo(packageName,
+                ApexManager.MATCH_ACTIVE_PACKAGE);
+        if (activePackage == null) {
+            String errorMsg = "Attempting to install new APEX package " + packageName;
+            Slog.w(TAG, errorMsg);
+            return Pair.create(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, errorMsg);
+        }
+
+        final long activeVersion = activePackage.getLongVersionCode();
+        if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST
+                && activeVersion != requiredInstalledVersionCode) {
+            String errorMsg = "Installed version of APEX package " + packageName
+                    + " does not match required. Active version: " + activeVersion
+                    + " required: " + requiredInstalledVersionCode;
+            Slog.w(TAG, errorMsg);
+            return Pair.create(PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
+        }
+
+        final boolean isAppDebuggable = (activePackage.applicationInfo.flags
+                & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+        final long newVersionCode = pkgLite.getLongVersionCode();
+        if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable)
+                && newVersionCode < activeVersion) {
+            String errorMsg = "Downgrade of APEX package " + packageName
+                    + " is not allowed. Active version: " + activeVersion
+                    + " attempted: " + newVersionCode;
+            Slog.w(TAG, errorMsg);
+            return Pair.create(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
+        }
+
+        return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
+    }
+
+    /**
+     * Check and throw if the given before/after packages would be considered a
+     * downgrade.
+     */
+    private static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
+            throws PackageManagerException {
+        if (after.getLongVersionCode() < before.getLongVersionCode()) {
+            throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+                    "Update version code " + after.versionCode + " is older than current "
+                            + before.getLongVersionCode());
+        } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
+            if (after.baseRevisionCode < before.getBaseRevisionCode()) {
+                throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+                        "Update base revision code " + after.baseRevisionCode
+                                + " is older than current " + before.getBaseRevisionCode());
+            }
+
+            if (!ArrayUtils.isEmpty(after.splitNames)) {
+                for (int i = 0; i < after.splitNames.length; i++) {
+                    final String splitName = after.splitNames[i];
+                    final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
+                    if (j != -1) {
+                        if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
+                            throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+                                    "Update split " + splitName + " revision code "
+                                            + after.splitRevisionCodes[i]
+                                            + " is older than current "
+                                            + before.getSplitRevisionCodes()[j]);
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
new file mode 100644
index 0000000..d600ae5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -0,0 +1,1325 @@
+/*
+ * 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.pm;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
+import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
+import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.incremental.IncrementalManager.isIncrementalPath;
+import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
+import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
+import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
+import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
+import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
+import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
+import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.decompressFile;
+import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
+import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
+import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.system.ErrnoException;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.F2fsUtils;
+import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.security.VerityUtils;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.EventLogTags;
+import com.android.server.pm.parsing.PackageCacher;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.utils.WatchedArrayMap;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.DigestException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Part of PackageManagerService that handles init and system packages. This class still needs
+ * further cleanup and eventually all the installation/scanning related logic will go to another
+ * class.
+ */
+public class InitAndSystemPackageHelper {
+    final PackageManagerService mPm;
+
+    // TODO(b/198166813): remove PMS dependency
+    public InitAndSystemPackageHelper(PackageManagerService pm) {
+        mPm = pm;
+    }
+
+    /**
+     * First part of init dir scanning
+     */
+    // TODO(b/197876467): consolidate this with cleanupSystemPackagesAndInstallStubs
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    public void scanSystemDirs(List<ScanPartition>  dirsToScanAsSystem,
+            boolean isUpgrade, PackageParser2 packageParser,
+            ExecutorService executorService, AndroidPackage platformPackage,
+            boolean isPreNMR1Upgrade, int systemParseFlags, int systemScanFlags) {
+        File frameworkDir = new File(Environment.getRootDirectory(), "framework");
+
+        // Collect vendor/product/system_ext overlay packages. (Do this before scanning
+        // any apps.)
+        // For security and version matching reason, only consider overlay packages if they
+        // reside in the right directory.
+        for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
+            final ScanPartition partition = dirsToScanAsSystem.get(i);
+            if (partition.getOverlayFolder() == null) {
+                continue;
+            }
+            scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
+                    systemScanFlags | partition.scanFlag, 0,
+                    packageParser, executorService, platformPackage, isUpgrade,
+                    isPreNMR1Upgrade);
+        }
+
+        scanDirTracedLI(frameworkDir, systemParseFlags,
+                systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
+                packageParser, executorService, platformPackage, isUpgrade, isPreNMR1Upgrade);
+        if (!mPm.mPackages.containsKey("android")) {
+            throw new IllegalStateException(
+                    "Failed to load frameworks package; check log for warnings");
+        }
+
+        for (int i = 0, size = dirsToScanAsSystem.size(); i < size; i++) {
+            final ScanPartition partition = dirsToScanAsSystem.get(i);
+            if (partition.getPrivAppFolder() != null) {
+                scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
+                        systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
+                        packageParser, executorService, platformPackage, isUpgrade,
+                        isPreNMR1Upgrade);
+            }
+            scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
+                    systemScanFlags | partition.scanFlag, 0,
+                    packageParser, executorService, platformPackage, isUpgrade,
+                    isPreNMR1Upgrade);
+        }
+    }
+
+    /**
+     * Second part of init dir scanning
+     */
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    public void cleanupSystemPackagesAndInstallStubs(List<ScanPartition> dirsToScanAsSystem,
+            boolean isUpgrade, PackageParser2 packageParser,
+            ExecutorService executorService, boolean onlyCore,
+            WatchedArrayMap<String, PackageSetting> packageSettings,
+            long startTime, File appInstallDir, AndroidPackage platformPackage,
+            boolean isPreNMR1Upgrade, int scanFlags, int systemParseFlags, int systemScanFlags,
+            int[] userIds) {
+        // Prune any system packages that no longer exist.
+        final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
+        // Stub packages must either be replaced with full versions in the /data
+        // partition or be disabled.
+        final List<String> stubSystemApps = new ArrayList<>();
+
+        if (!onlyCore) {
+            // do this first before mucking with mPackages for the "expecting better" case
+            final int numPackages = mPm.mPackages.size();
+            for (int index = 0; index < numPackages; index++) {
+                final AndroidPackage pkg = mPm.mPackages.valueAt(index);
+                if (pkg.isStub()) {
+                    stubSystemApps.add(pkg.getPackageName());
+                }
+            }
+
+            // Iterates PackageSettings in reversed order because the item could be removed
+            // during the iteration.
+            for (int index = packageSettings.size() - 1; index >= 0; index--) {
+                final PackageSetting ps = packageSettings.valueAt(index);
+
+                /*
+                 * If this is not a system app, it can't be a
+                 * disable system app.
+                 */
+                if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+                    continue;
+                }
+
+                /*
+                 * If the package is scanned, it's not erased.
+                 */
+                final AndroidPackage scannedPkg = mPm.mPackages.get(ps.name);
+                if (scannedPkg != null) {
+                    /*
+                     * If the system app is both scanned and in the
+                     * disabled packages list, then it must have been
+                     * added via OTA. Remove it from the currently
+                     * scanned package so the previously user-installed
+                     * application can be scanned.
+                     */
+                    if (mPm.mSettings.isDisabledSystemPackageLPr(ps.name)) {
+                        logCriticalInfo(Log.WARN,
+                                "Expecting better updated system app for " + ps.name
+                                        + "; removing system app.  Last known"
+                                        + " codePath=" + ps.getPathString()
+                                        + ", versionCode=" + ps.versionCode
+                                        + "; scanned versionCode="
+                                        + scannedPkg.getLongVersionCode());
+                        mPm.removePackageLI(scannedPkg, true);
+                        mPm.mExpectingBetter.put(ps.name, ps.getPath());
+                    }
+
+                    continue;
+                }
+
+                if (!mPm.mSettings.isDisabledSystemPackageLPr(ps.name)) {
+                    logCriticalInfo(Log.WARN, "System package " + ps.name
+                            + " no longer exists; its data will be wiped");
+                    mPm.removePackageDataLIF(ps, userIds, null, 0, false);
+                } else {
+                    // we still have a disabled system package, but, it still might have
+                    // been removed. check the code path still exists and check there's
+                    // still a package. the latter can happen if an OTA keeps the same
+                    // code path, but, changes the package name.
+                    final PackageSetting disabledPs =
+                            mPm.mSettings.getDisabledSystemPkgLPr(ps.name);
+                    if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
+                            || disabledPs.pkg == null) {
+                        possiblyDeletedUpdatedSystemApps.add(ps.name);
+                    } else {
+                        // We're expecting that the system app should remain disabled, but add
+                        // it to expecting better to recover in case the data version cannot
+                        // be scanned.
+                        // TODO(b/197869066): mExpectingBetter should be able to pulled out into
+                        // this class and used only by the PMS initialization
+                        mPm.mExpectingBetter.put(disabledPs.name, disabledPs.getPath());
+                    }
+                }
+            }
+        }
+
+        final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
+
+        // Remove any shared userIDs that have no associated packages
+        mPm.mSettings.pruneSharedUsersLPw();
+        final long systemScanTime = SystemClock.uptimeMillis() - startTime;
+        final int systemPackagesCount = mPm.mPackages.size();
+        Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
+                + " ms, packageCount: " + systemPackagesCount
+                + " , timePerPackage: "
+                + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
+                + " , cached: " + cachedSystemApps);
+        if (isUpgrade && systemPackagesCount > 0) {
+            //CHECKSTYLE:OFF IndentationCheck
+            FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+                    BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
+                    systemScanTime / systemPackagesCount);
+            //CHECKSTYLE:ON IndentationCheck
+        }
+        if (!onlyCore) {
+            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
+                    SystemClock.uptimeMillis());
+            scanDirTracedLI(appInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
+                    packageParser, executorService, platformPackage, isUpgrade,
+                    isPreNMR1Upgrade);
+
+        }
+
+        List<Runnable> unfinishedTasks = executorService.shutdownNow();
+        if (!unfinishedTasks.isEmpty()) {
+            throw new IllegalStateException("Not all tasks finished before calling close: "
+                    + unfinishedTasks);
+        }
+
+        if (!onlyCore) {
+            // Remove disable package settings for updated system apps that were
+            // removed via an OTA. If the update is no longer present, remove the
+            // app completely. Otherwise, revoke their system privileges.
+            for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
+                final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
+                final AndroidPackage pkg = mPm.mPackages.get(packageName);
+                final String msg;
+
+                // remove from the disabled system list; do this first so any future
+                // scans of this package are performed without this state
+                mPm.mSettings.removeDisabledSystemPackageLPw(packageName);
+
+                if (pkg == null) {
+                    // should have found an update, but, we didn't; remove everything
+                    msg = "Updated system package " + packageName
+                            + " no longer exists; removing its data";
+                    // Actual deletion of code and data will be handled by later
+                    // reconciliation step
+                } else {
+                    // found an update; revoke system privileges
+                    msg = "Updated system package " + packageName
+                            + " no longer exists; rescanning package on data";
+
+                    // NOTE: We don't do anything special if a stub is removed from the
+                    // system image. But, if we were [like removing the uncompressed
+                    // version from the /data partition], this is where it'd be done.
+
+                    // remove the package from the system and re-scan it without any
+                    // special privileges
+                    mPm.removePackageLI(pkg, true);
+                    try {
+                        final File codePath = new File(pkg.getPath());
+                        mPm.scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
+                    } catch (PackageManagerException e) {
+                        Slog.e(TAG, "Failed to parse updated, ex-system package: "
+                                + e.getMessage());
+                    }
+                }
+
+                // one final check. if we still have a package setting [ie. it was
+                // previously scanned and known to the system], but, we don't have
+                // a package [ie. there was an error scanning it from the /data
+                // partition], completely remove the package data.
+                final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+                if (ps != null && mPm.mPackages.get(packageName) == null) {
+                    mPm.removePackageDataLIF(ps, userIds, null, 0, false);
+
+                }
+                logCriticalInfo(Log.WARN, msg);
+            }
+
+            /*
+             * Make sure all system apps that we expected to appear on
+             * the userdata partition actually showed up. If they never
+             * appeared, crawl back and revive the system version.
+             */
+            for (int i = 0; i < mPm.mExpectingBetter.size(); i++) {
+                final String packageName = mPm.mExpectingBetter.keyAt(i);
+                if (!mPm.mPackages.containsKey(packageName)) {
+                    final File scanFile = mPm.mExpectingBetter.valueAt(i);
+
+                    logCriticalInfo(Log.WARN, "Expected better " + packageName
+                            + " but never showed up; reverting to system");
+
+                    @ParsingPackageUtils.ParseFlags int reparseFlags = 0;
+                    @PackageManagerService.ScanFlags int rescanFlags = 0;
+                    for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
+                        final ScanPartition partition = dirsToScanAsSystem.get(i1);
+                        if (partition.containsPrivApp(scanFile)) {
+                            reparseFlags = systemParseFlags;
+                            rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
+                                    | partition.scanFlag;
+                            break;
+                        }
+                        if (partition.containsApp(scanFile)) {
+                            reparseFlags = systemParseFlags;
+                            rescanFlags = systemScanFlags | partition.scanFlag;
+                            break;
+                        }
+                    }
+                    if (rescanFlags == 0) {
+                        Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
+                        continue;
+                    }
+                    mPm.mSettings.enableSystemPackageLPw(packageName);
+
+                    try {
+                        final AndroidPackage newPkg = mPm.scanPackageTracedLI(
+                                scanFile, reparseFlags, rescanFlags, 0, null);
+                        // We rescanned a stub, add it to the list of stubbed system packages
+                        if (newPkg.isStub()) {
+                            stubSystemApps.add(packageName);
+                        }
+                    } catch (PackageManagerException e) {
+                        Slog.e(TAG, "Failed to parse original system package: "
+                                + e.getMessage());
+                    }
+                }
+            }
+
+            // Uncompress and install any stubbed system applications.
+            // This must be done last to ensure all stubs are replaced or disabled.
+            installSystemStubPackages(stubSystemApps, scanFlags);
+
+            final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
+                    - cachedSystemApps;
+
+            final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
+            final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount;
+            Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+                    + " ms, packageCount: " + dataPackagesCount
+                    + " , timePerPackage: "
+                    + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+                    + " , cached: " + cachedNonSystemApps);
+            if (isUpgrade && dataPackagesCount > 0) {
+                //CHECKSTYLE:OFF IndentationCheck
+                FrameworkStatsLog.write(
+                        FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+                        BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
+                        dataScanTime / dataPackagesCount);
+                //CHECKSTYLE:OFF IndentationCheck
+            }
+        }
+        mPm.mExpectingBetter.clear();
+
+        mPm.mSettings.pruneRenamedPackagesLPw();
+    }
+
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
+            long currentTime, PackageParser2 packageParser, ExecutorService executorService,
+            AndroidPackage platformPackage, boolean isUpgrade, boolean isPreNMR1Upgrade) {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
+        try {
+            scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService,
+                    platformPackage, isUpgrade, isPreNMR1Upgrade);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
+            PackageParser2 packageParser, ExecutorService executorService,
+            AndroidPackage platformPackage, boolean isUpgrade, boolean isPreNMR1Upgrade) {
+        final File[] files = scanDir.listFiles();
+        if (ArrayUtils.isEmpty(files)) {
+            Log.d(TAG, "No files in app dir " + scanDir);
+            return;
+        }
+
+        if (DEBUG_PACKAGE_SCANNING) {
+            Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
+                    + " flags=0x" + Integer.toHexString(parseFlags));
+        }
+
+        ParallelPackageParser parallelPackageParser =
+                new ParallelPackageParser(packageParser, executorService);
+
+        // Submit files for parsing in parallel
+        int fileCount = 0;
+        for (File file : files) {
+            final boolean isPackage = (isApkFile(file) || file.isDirectory())
+                    && !PackageInstallerService.isStageName(file.getName());
+            if (!isPackage) {
+                // Ignore entries which are not packages
+                continue;
+            }
+            parallelPackageParser.submit(file, parseFlags);
+            fileCount++;
+        }
+
+        // Process results one by one
+        for (; fileCount > 0; fileCount--) {
+            ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
+            Throwable throwable = parseResult.throwable;
+            int errorCode = PackageManager.INSTALL_SUCCEEDED;
+            String errorMsg = null;
+
+            if (throwable == null) {
+                // TODO(b/194319951): move lower in the scan chain
+                // Static shared libraries have synthetic package names
+                if (parseResult.parsedPackage.isStaticSharedLibrary()) {
+                    PackageManagerService.renameStaticSharedLibraryPackage(
+                            parseResult.parsedPackage);
+                }
+                try {
+                    addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
+                            currentTime, null, platformPackage, isUpgrade,
+                            isPreNMR1Upgrade);
+                } catch (PackageManagerException e) {
+                    errorCode = e.error;
+                    errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
+                    Slog.w(TAG, errorMsg);
+                }
+            } else if (throwable instanceof PackageManagerException) {
+                PackageManagerException e = (PackageManagerException) throwable;
+                errorCode = e.error;
+                errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
+                Slog.w(TAG, errorMsg);
+            } else {
+                throw new IllegalStateException("Unexpected exception occurred while parsing "
+                        + parseResult.scanFile, throwable);
+            }
+
+            if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
+                mPm.mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
+            }
+
+            // Delete invalid userdata apps
+            if ((scanFlags & SCAN_AS_SYSTEM) == 0
+                    && errorCode != PackageManager.INSTALL_SUCCEEDED) {
+                logCriticalInfo(Log.WARN,
+                        "Deleting invalid package at " + parseResult.scanFile);
+                mPm.removeCodePathLI(parseResult.scanFile);
+            }
+        }
+    }
+
+    /**
+     * Uncompress and install stub applications.
+     * <p>In order to save space on the system partition, some applications are shipped in a
+     * compressed form. In addition the compressed bits for the full application, the
+     * system image contains a tiny stub comprised of only the Android manifest.
+     * <p>During the first boot, attempt to uncompress and install the full application. If
+     * the application can't be installed for any reason, disable the stub and prevent
+     * uncompressing the full application during future boots.
+     * <p>In order to forcefully attempt an installation of a full application, go to app
+     * settings and enable the application.
+     */
+    @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+    private void installSystemStubPackages(@NonNull List<String> systemStubPackageNames,
+            @PackageManagerService.ScanFlags int scanFlags) {
+        for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
+            final String packageName = systemStubPackageNames.get(i);
+            // skip if the system package is already disabled
+            if (mPm.mSettings.isDisabledSystemPackageLPr(packageName)) {
+                systemStubPackageNames.remove(i);
+                continue;
+            }
+            // skip if the package isn't installed (?!); this should never happen
+            final AndroidPackage pkg = mPm.mPackages.get(packageName);
+            if (pkg == null) {
+                systemStubPackageNames.remove(i);
+                continue;
+            }
+            // skip if the package has been disabled by the user
+            final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+            if (ps != null) {
+                final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
+                if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+                    systemStubPackageNames.remove(i);
+                    continue;
+                }
+            }
+
+            // install the package to replace the stub on /system
+            try {
+                installStubPackageLI(pkg, 0, scanFlags);
+                ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+                        UserHandle.USER_SYSTEM, "android");
+                systemStubPackageNames.remove(i);
+            } catch (PackageManagerException e) {
+                Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
+            }
+
+            // any failed attempt to install the package will be cleaned up later
+        }
+
+        // disable any stub still left; these failed to install the full application
+        for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
+            final String pkgName = systemStubPackageNames.get(i);
+            final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
+            ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                    UserHandle.USER_SYSTEM, "android");
+            logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
+        }
+    }
+
+    /**
+     * Extract, install and enable a stub package.
+     * <p>If the compressed file can not be extracted / installed for any reason, the stub
+     * APK will be installed and the package will be disabled. To recover from this situation,
+     * the user will need to go into system settings and re-enable the package.
+     */
+    @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+    public boolean enableCompressedPackage(AndroidPackage stubPkg,
+            @NonNull PackageSetting stubPkgSetting, int defParseFlags,
+            List<ScanPartition> dirsToScanAsSystem) {
+        final int parseFlags = defParseFlags | ParsingPackageUtils.PARSE_CHATTY
+                | ParsingPackageUtils.PARSE_ENFORCE_CODE;
+        synchronized (mPm.mInstallLock) {
+            final AndroidPackage pkg;
+            try (PackageFreezer freezer =
+                         mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
+                pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
+                synchronized (mPm.mLock) {
+                    mPm.prepareAppDataAfterInstallLIF(pkg);
+                    try {
+                        mPm.updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null,
+                                Collections.unmodifiableMap(mPm.mPackages));
+                    } catch (PackageManagerException e) {
+                        Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e);
+                    }
+                    mPm.mPermissionManager.onPackageInstalled(pkg,
+                            PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
+                            UserHandle.USER_ALL);
+                    mPm.writeSettingsLPrTEMP();
+                }
+            } catch (PackageManagerException e) {
+                // Whoops! Something went very wrong; roll back to the stub and disable the package
+                try (PackageFreezer freezer =
+                             mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
+                    synchronized (mPm.mLock) {
+                        // NOTE: Ensure the system package is enabled; even for a compressed stub.
+                        // If we don't, installing the system package fails during scan
+                        enableSystemPackageLPw(stubPkg);
+                    }
+                    installPackageFromSystemLIF(stubPkg.getPath(),
+                            mPm.mUserManager.getUserIds() /*allUserHandles*/,
+                            null /*origUserHandles*/,
+                            true /*writeSettings*/, defParseFlags, dirsToScanAsSystem);
+                } catch (PackageManagerException pme) {
+                    // Serious WTF; we have to be able to install the stub
+                    Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
+                            pme);
+                } finally {
+                    // Disable the package; the stub by itself is not runnable
+                    synchronized (mPm.mLock) {
+                        final PackageSetting stubPs = mPm.mSettings.getPackageLPr(
+                                stubPkg.getPackageName());
+                        if (stubPs != null) {
+                            stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
+                                    UserHandle.USER_SYSTEM, "android");
+                        }
+                        mPm.writeSettingsLPrTEMP();
+                    }
+                }
+                return false;
+            }
+            mPm.clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
+                    | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+            mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
+                    pkg.getBaseApkPath(), pkg.getSplitCodePaths());
+        }
+        return true;
+    }
+
+    @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+    private AndroidPackage installStubPackageLI(AndroidPackage stubPkg,
+            @ParsingPackageUtils.ParseFlags int parseFlags,
+            @PackageManagerService.ScanFlags int scanFlags)
+            throws PackageManagerException {
+        if (DEBUG_COMPRESSION) {
+            Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName());
+        }
+        // uncompress the binary to its eventual destination on /data
+        final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getPath());
+        if (scanFile == null) {
+            throw new PackageManagerException(
+                    "Unable to decompress stub at " + stubPkg.getPath());
+        }
+        synchronized (mPm.mLock) {
+            mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
+        }
+        mPm.removePackageLI(stubPkg, true /*chatty*/);
+        try {
+            return mPm.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
+        } catch (PackageManagerException e) {
+            Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
+                    e);
+            // Remove the failed install
+            mPm.removeCodePathLI(scanFile);
+            throw e;
+        }
+    }
+
+    /**
+     * Decompresses the given package on the system image onto
+     * the /data partition.
+     * @return The directory the package was decompressed into. Otherwise, {@code null}.
+     */
+    @GuardedBy("mPm.mInstallLock")
+    private File decompressPackage(String packageName, String codePath) {
+        final File[] compressedFiles = getCompressedFiles(codePath);
+        if (compressedFiles == null || compressedFiles.length == 0) {
+            if (DEBUG_COMPRESSION) {
+                Slog.i(TAG, "No files to decompress: " + codePath);
+            }
+            return null;
+        }
+        final File dstCodePath =
+                PackageManagerService.getNextCodePath(Environment.getDataAppDirectory(null),
+                        packageName);
+        int ret = PackageManager.INSTALL_SUCCEEDED;
+        try {
+            makeDirRecursive(dstCodePath, 0755);
+            for (File srcFile : compressedFiles) {
+                final String srcFileName = srcFile.getName();
+                final String dstFileName = srcFileName.substring(
+                        0, srcFileName.length() - COMPRESSED_EXTENSION.length());
+                final File dstFile = new File(dstCodePath, dstFileName);
+                ret = decompressFile(srcFile, dstFile);
+                if (ret != PackageManager.INSTALL_SUCCEEDED) {
+                    logCriticalInfo(Log.ERROR, "Failed to decompress"
+                            + "; pkg: " + packageName
+                            + ", file: " + dstFileName);
+                    break;
+                }
+            }
+        } catch (ErrnoException e) {
+            logCriticalInfo(Log.ERROR, "Failed to decompress"
+                    + "; pkg: " + packageName
+                    + ", err: " + e.errno);
+        }
+        if (ret == PackageManager.INSTALL_SUCCEEDED) {
+            final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
+            NativeLibraryHelper.Handle handle = null;
+            try {
+                handle = NativeLibraryHelper.Handle.create(dstCodePath);
+                ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
+                        null /*abiOverride*/, false /*isIncremental*/);
+            } catch (IOException e) {
+                logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
+                        + "; pkg: " + packageName);
+                ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+            } finally {
+                IoUtils.closeQuietly(handle);
+            }
+        }
+        if (ret == PackageManager.INSTALL_SUCCEEDED) {
+            // NOTE: During boot, we have to delay releasing cblocks for no other reason than
+            // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}.
+            // When we no longer need to read that setting, cblock release can occur always
+            // occur here directly
+            if (!mPm.mSystemReady) {
+                if (mPm.mReleaseOnSystemReady == null) {
+                    mPm.mReleaseOnSystemReady = new ArrayList<>();
+                }
+                mPm.mReleaseOnSystemReady.add(dstCodePath);
+            } else {
+                final ContentResolver resolver = mPm.mContext.getContentResolver();
+                F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
+            }
+        }
+        if (ret != PackageManager.INSTALL_SUCCEEDED) {
+            if (!dstCodePath.exists()) {
+                return null;
+            }
+            mPm.removeCodePathLI(dstCodePath);
+            return null;
+        }
+
+        return dstCodePath;
+    }
+
+
+    @GuardedBy("mPm.mLock")
+    private void enableSystemPackageLPw(AndroidPackage pkg) {
+        mPm.mSettings.enableSystemPackageLPw(pkg.getPackageName());
+    }
+
+    /**
+     * Tries to delete system package.
+     */
+    @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+    public void deleteSystemPackageLIF(DeletePackageAction action, PackageSetting deletedPs,
+            @NonNull int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo,
+            boolean writeSettings, int defParseFlags, List<ScanPartition> dirsToScanAsSystem)
+            throws SystemDeleteException {
+        final boolean applyUserRestrictions = outInfo != null && (outInfo.mOrigUsers != null);
+        final AndroidPackage deletedPkg = deletedPs.pkg;
+        // Confirm if the system package has been updated
+        // An updated system app can be deleted. This will also have to restore
+        // the system pkg from system partition
+        // reader
+        final PackageSetting disabledPs = action.mDisabledPs;
+        if (DEBUG_REMOVE) {
+            Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.getPackageName()
+                    + " disabledPs=" + disabledPs);
+        }
+        Slog.d(TAG, "Deleting system pkg from data partition");
+
+        if (DEBUG_REMOVE) {
+            if (applyUserRestrictions) {
+                Slog.d(TAG, "Remembering install states:");
+                for (int userId : allUserHandles) {
+                    final boolean finstalled = ArrayUtils.contains(outInfo.mOrigUsers, userId);
+                    Slog.d(TAG, "   u=" + userId + " inst=" + finstalled);
+                }
+            }
+        }
+
+        if (outInfo != null) {
+            // Delete the updated package
+            outInfo.mIsRemovedPackageSystemUpdate = true;
+        }
+
+        if (disabledPs.versionCode < deletedPs.versionCode) {
+            // Delete data for downgrades
+            flags &= ~PackageManager.DELETE_KEEP_DATA;
+        } else {
+            // Preserve data by setting flag
+            flags |= PackageManager.DELETE_KEEP_DATA;
+        }
+
+        mPm.deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
+                outInfo, writeSettings);
+
+        // writer
+        synchronized (mPm.mLock) {
+            // NOTE: The system package always needs to be enabled; even if it's for
+            // a compressed stub. If we don't, installing the system package fails
+            // during scan [scanning checks the disabled packages]. We will reverse
+            // this later, after we've "installed" the stub.
+            // Reinstate the old system package
+            enableSystemPackageLPw(disabledPs.pkg);
+            // Remove any native libraries from the upgraded package.
+            removeNativeBinariesLI(deletedPs);
+        }
+
+        // Install the system package
+        if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
+        try {
+            installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
+                    outInfo == null ? null : outInfo.mOrigUsers, writeSettings, defParseFlags,
+                    dirsToScanAsSystem);
+        } catch (PackageManagerException e) {
+            Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": "
+                    + e.getMessage());
+            // TODO(b/194319951): can we avoid this; throw would come from scan...
+            throw new SystemDeleteException(e);
+        } finally {
+            if (disabledPs.pkg.isStub()) {
+                // We've re-installed the stub; make sure it's disabled here. If package was
+                // originally enabled, we'll install the compressed version of the application
+                // and re-enable it afterward.
+                final PackageSetting stubPs = mPm.mSettings.getPackageLPr(
+                        deletedPkg.getPackageName());
+                if (stubPs != null) {
+                    int userId = action.mUser == null
+                            ? UserHandle.USER_ALL : action.mUser.getIdentifier();
+                    if (userId == UserHandle.USER_ALL) {
+                        for (int aUserId : allUserHandles) {
+                            stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
+                        }
+                    } else if (userId >= UserHandle.USER_SYSTEM) {
+                        stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android");
+                    }
+                }
+            }
+        }
+    }
+
+    private void removeNativeBinariesLI(PackageSetting ps) {
+        if (ps != null) {
+            NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
+        }
+    }
+
+    /**
+     * Installs a package that's already on the system partition.
+     */
+    @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+    private void installPackageFromSystemLIF(@NonNull String codePathString,
+            @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings,
+            int defParseFlags, List<ScanPartition> dirsToScanAsSystem)
+            throws PackageManagerException {
+        final File codePath = new File(codePathString);
+        @ParsingPackageUtils.ParseFlags int parseFlags =
+                defParseFlags
+                        | ParsingPackageUtils.PARSE_MUST_BE_APK
+                        | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
+        @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM;
+        for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
+            ScanPartition partition = dirsToScanAsSystem.get(i);
+            if (partition.containsFile(codePath)) {
+                scanFlags |= partition.scanFlag;
+                if (partition.containsPrivApp(codePath)) {
+                    scanFlags |= SCAN_AS_PRIVILEGED;
+                }
+                break;
+            }
+        }
+
+        final AndroidPackage pkg =
+                mPm.scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
+
+        PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+
+        try {
+            // update shared libraries for the newly re-installed system package
+            mPm.updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
+                    Collections.unmodifiableMap(mPm.mPackages));
+        } catch (PackageManagerException e) {
+            Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
+        }
+
+        mPm.prepareAppDataAfterInstallLIF(pkg);
+
+        // writer
+        synchronized (mPm.mLock) {
+            PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+
+            final boolean applyUserRestrictions = origUserHandles != null;
+            if (applyUserRestrictions) {
+                boolean installedStateChanged = false;
+                if (DEBUG_REMOVE) {
+                    Slog.d(TAG, "Propagating install state across reinstall");
+                }
+                for (int userId : allUserHandles) {
+                    final boolean installed = ArrayUtils.contains(origUserHandles, userId);
+                    if (DEBUG_REMOVE) {
+                        Slog.d(TAG, "    user " + userId + " => " + installed);
+                    }
+                    if (installed != ps.getInstalled(userId)) {
+                        installedStateChanged = true;
+                    }
+                    ps.setInstalled(installed, userId);
+                    if (installed) {
+                        ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
+                    }
+                }
+                // Regardless of writeSettings we need to ensure that this restriction
+                // state propagation is persisted
+                mPm.mSettings.writeAllUsersPackageRestrictionsLPr();
+                if (installedStateChanged) {
+                    mPm.mSettings.writeKernelMappingLPr(ps);
+                }
+            }
+
+            // The method below will take care of removing obsolete permissions and granting
+            // install permissions.
+            mPm.mPermissionManager.onPackageInstalled(pkg,
+                    PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
+                    UserHandle.USER_ALL);
+            for (final int userId : allUserHandles) {
+                if (applyUserRestrictions) {
+                    mPm.mSettings.writePermissionStateForUserLPr(userId, false);
+                }
+            }
+
+            // can downgrade to reader here
+            if (writeSettings) {
+                mPm.writeSettingsLPrTEMP();
+            }
+        }
+    }
+
+    /**
+     * Adds a new package to the internal data structures during platform initialization.
+     * <p>After adding, the package is known to the system and available for querying.
+     * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
+     * etc...], additional checks are performed. Basic verification [such as ensuring
+     * matching signatures, checking version codes, etc...] occurs if the package is
+     * identical to a previously known package. If the package fails a signature check,
+     * the version installed on /data will be removed. If the version of the new package
+     * is less than or equal than the version on /data, it will be ignored.
+     * <p>Regardless of the package location, the results are applied to the internal
+     * structures and the package is made available to the rest of the system.
+     * <p>NOTE: The return value should be removed. It's the passed in package object.
+     */
+    @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+    public AndroidPackage addForInitLI(ParsedPackage parsedPackage,
+            @ParsingPackageUtils.ParseFlags int parseFlags,
+            @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+            @Nullable UserHandle user, AndroidPackage platformPackage, boolean isUpgrade,
+            boolean isPreNMR1Upgrade) throws PackageManagerException {
+        final boolean scanSystemPartition =
+                (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
+        final String renamedPkgName;
+        final PackageSetting disabledPkgSetting;
+        final boolean isSystemPkgUpdated;
+        final boolean pkgAlreadyExists;
+        PackageSetting pkgSetting;
+
+        synchronized (mPm.mLock) {
+            renamedPkgName = mPm.mSettings.getRenamedPackageLPr(parsedPackage.getRealPackage());
+            final String realPkgName = PackageManagerService.getRealPackageName(parsedPackage,
+                    renamedPkgName);
+            if (realPkgName != null) {
+                PackageManagerService.ensurePackageRenamed(parsedPackage, renamedPkgName);
+            }
+            final PackageSetting originalPkgSetting = mPm.getOriginalPackageLocked(parsedPackage,
+                    renamedPkgName);
+            final PackageSetting installedPkgSetting = mPm.mSettings.getPackageLPr(
+                    parsedPackage.getPackageName());
+            pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
+            pkgAlreadyExists = pkgSetting != null;
+            final String disabledPkgName = pkgAlreadyExists
+                    ? pkgSetting.name : parsedPackage.getPackageName();
+            if (scanSystemPartition && !pkgAlreadyExists
+                    && mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
+                // The updated-package data for /system apk remains inconsistently
+                // after the package data for /data apk is lost accidentally.
+                // To recover it, enable /system apk and install it as non-updated system app.
+                Slog.w(TAG, "Inconsistent package setting of updated system app for "
+                        + disabledPkgName + ". To recover it, enable the system app"
+                        + "and install it as non-updated system app.");
+                mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
+            }
+            disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
+            isSystemPkgUpdated = disabledPkgSetting != null;
+
+            if (DEBUG_INSTALL && isSystemPkgUpdated) {
+                Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
+            }
+
+            final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
+                    ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
+                    0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
+                    : null;
+            if (DEBUG_PACKAGE_SCANNING
+                    && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
+                    && sharedUserSetting != null) {
+                Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+                        + " (uid=" + sharedUserSetting.userId + "):"
+                        + " packages=" + sharedUserSetting.packages);
+            }
+
+            if (scanSystemPartition) {
+                if (isSystemPkgUpdated) {
+                    // we're updating the disabled package, so, scan it as the package setting
+                    boolean isPlatformPackage = platformPackage != null
+                            && Objects.equals(platformPackage.getPackageName(),
+                            parsedPackage.getPackageName());
+                    final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
+                            null, disabledPkgSetting /* pkgSetting */,
+                            null /* disabledPkgSetting */, null /* originalPkgSetting */,
+                            null, parseFlags, scanFlags, isPlatformPackage, user, null);
+                    PackageManagerService.applyPolicy(parsedPackage, scanFlags,
+                            platformPackage, true);
+                    final ScanResult scanResult =
+                            mPm.scanPackageOnlyLI(request, mPm.mInjector,
+                                    mPm.mFactoryTest, -1L);
+                    if (scanResult.mExistingSettingCopied
+                            && scanResult.mRequest.mPkgSetting != null) {
+                        scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
+                    }
+                }
+            }
+        }
+
+        final boolean newPkgChangedPaths = pkgAlreadyExists
+                && !pkgSetting.getPathString().equals(parsedPackage.getPath());
+        final boolean newPkgVersionGreater =
+                pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
+        final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
+                && newPkgChangedPaths && newPkgVersionGreater;
+        if (isSystemPkgBetter) {
+            // The version of the application on /system is greater than the version on
+            // /data. Switch back to the application on /system.
+            // It's safe to assume the application on /system will correctly scan. If not,
+            // there won't be a working copy of the application.
+            synchronized (mPm.mLock) {
+                // just remove the loaded entries from package lists
+                mPm.mPackages.remove(pkgSetting.name);
+            }
+
+            logCriticalInfo(Log.WARN,
+                    "System package updated;"
+                            + " name: " + pkgSetting.name
+                            + "; " + pkgSetting.versionCode + " --> "
+                            + parsedPackage.getLongVersionCode()
+                            + "; " + pkgSetting.getPathString()
+                            + " --> " + parsedPackage.getPath());
+
+            final InstallArgs args = mPm.createInstallArgsForExisting(
+                    pkgSetting.getPathString(), getAppDexInstructionSets(
+                            pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
+            args.cleanUpResourcesLI();
+            synchronized (mPm.mLock) {
+                mPm.mSettings.enableSystemPackageLPw(pkgSetting.name);
+            }
+        }
+
+        // The version of the application on the /system partition is less than or
+        // equal to the version on the /data partition. Throw an exception and use
+        // the application already installed on the /data partition.
+        if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
+            // In the case of a skipped package, commitReconciledScanResultLocked is not called to
+            // add the object to the "live" data structures, so this is the final mutation step
+            // for the package. Which means it needs to be finalized here to cache derived fields.
+            // This is relevant for cases where the disabled system package is used for flags or
+            // other metadata.
+            parsedPackage.hideAsFinal();
+            throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+                    + " at " + parsedPackage.getPath() + " ignored: updated version "
+                    + (pkgAlreadyExists ? String.valueOf(pkgSetting.versionCode) : "unknown")
+                    + " better than this " + parsedPackage.getLongVersionCode());
+        }
+
+        // Verify certificates against what was last scanned. Force re-collecting certificate in two
+        // special cases:
+        // 1) when scanning system, force re-collect only if system is upgrading.
+        // 2) when scannning /data, force re-collect only if the app is privileged (updated from
+        // preinstall, or treated as privileged, e.g. due to shared user ID).
+        final boolean forceCollect = scanSystemPartition ? isUpgrade
+                : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
+        if (DEBUG_VERIFY && forceCollect) {
+            Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
+        }
+
+        // Full APK verification can be skipped during certificate collection, only if the file is
+        // in verified partition, or can be verified on access (when apk verity is enabled). In both
+        // cases, only data in Signing Block is verified instead of the whole file.
+        // TODO(b/136132412): skip for Incremental installation
+        final boolean skipVerify = scanSystemPartition
+                || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
+        collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify,
+                isPreNMR1Upgrade);
+
+        // Reset profile if the application version is changed
+        maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
+
+        /*
+         * A new system app appeared, but we already had a non-system one of the
+         * same name installed earlier.
+         */
+        boolean shouldHideSystemApp = false;
+        // A new application appeared on /system, but, we already have a copy of
+        // the application installed on /data.
+        if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
+                && !pkgSetting.isSystem()) {
+
+            if (!parsedPackage.getSigningDetails()
+                    .checkCapability(pkgSetting.signatures.mSigningDetails,
+                            SigningDetails.CertCapabilities.INSTALLED_DATA)
+                    && !pkgSetting.signatures.mSigningDetails.checkCapability(
+                    parsedPackage.getSigningDetails(),
+                    SigningDetails.CertCapabilities.ROLLBACK)) {
+                logCriticalInfo(Log.WARN,
+                        "System package signature mismatch;"
+                                + " name: " + pkgSetting.name);
+                try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
+                        parsedPackage.getPackageName(),
+                        "scanPackageInternalLI")) {
+                    mPm.deletePackageLIF(parsedPackage.getPackageName(), null, true,
+                            mPm.mUserManager.getUserIds(), 0, null, false);
+                }
+                pkgSetting = null;
+            } else if (newPkgVersionGreater) {
+                // The application on /system is newer than the application on /data.
+                // Simply remove the application on /data [keeping application data]
+                // and replace it with the version on /system.
+                logCriticalInfo(Log.WARN,
+                        "System package enabled;"
+                                + " name: " + pkgSetting.name
+                                + "; " + pkgSetting.versionCode + " --> "
+                                + parsedPackage.getLongVersionCode()
+                                + "; " + pkgSetting.getPathString() + " --> "
+                                + parsedPackage.getPath());
+                InstallArgs args = mPm.createInstallArgsForExisting(
+                        pkgSetting.getPathString(), getAppDexInstructionSets(
+                                pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
+                synchronized (mPm.mInstallLock) {
+                    args.cleanUpResourcesLI();
+                }
+            } else {
+                // The application on /system is older than the application on /data. Hide
+                // the application on /system and the version on /data will be scanned later
+                // and re-added like an update.
+                shouldHideSystemApp = true;
+                logCriticalInfo(Log.INFO,
+                        "System package disabled;"
+                                + " name: " + pkgSetting.name
+                                + "; old: " + pkgSetting.getPathString() + " @ "
+                                + pkgSetting.versionCode
+                                + "; new: " + parsedPackage.getPath() + " @ "
+                                + parsedPackage.getPath());
+            }
+        }
+
+        final ScanResult scanResult = mPm.scanPackageNewLI(parsedPackage, parseFlags, scanFlags
+                | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
+        if (scanResult.mSuccess) {
+            synchronized (mPm.mLock) {
+                boolean appIdCreated = false;
+                try {
+                    final String pkgName = scanResult.mPkgSetting.name;
+                    final Map<String, ReconciledPackage> reconcileResult =
+                            mPm.reconcilePackagesLocked(
+                            new ReconcileRequest(
+                                    Collections.singletonMap(pkgName, scanResult),
+                                    mPm.mSharedLibraries,
+                                    mPm.mPackages,
+                                    Collections.singletonMap(
+                                            pkgName,
+                                            mPm.getSettingsVersionForPackage(parsedPackage)),
+                                    Collections.singletonMap(pkgName,
+                                            mPm.getSharedLibLatestVersionSetting(scanResult))),
+                                    mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
+                    appIdCreated = mPm.optimisticallyRegisterAppId(scanResult);
+                    mPm.commitReconciledScanResultLocked(
+                            reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
+                } catch (PackageManagerException e) {
+                    if (appIdCreated) {
+                        mPm.cleanUpAppIdCreation(scanResult);
+                    }
+                    throw e;
+                }
+            }
+        }
+
+        if (shouldHideSystemApp) {
+            synchronized (mPm.mLock) {
+                mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
+            }
+        }
+        if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
+            if (pkgSetting != null && pkgSetting.isPackageLoading()) {
+                // Continue monitoring loading progress of active incremental packages
+                final IncrementalStatesCallback incrementalStatesCallback =
+                        new IncrementalStatesCallback(parsedPackage.getPackageName(), mPm);
+                pkgSetting.setIncrementalStatesCallback(incrementalStatesCallback);
+                mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
+                        new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
+            }
+        }
+        return scanResult.mPkgSetting.pkg;
+    }
+
+    /**
+     * Returns if forced apk verification can be skipped for the whole package, including splits.
+     */
+    private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
+        if (!canSkipForcedApkVerification(pkg.getBaseApkPath())) {
+            return false;
+        }
+        // TODO: Allow base and splits to be verified individually.
+        String[] splitCodePaths = pkg.getSplitCodePaths();
+        if (!ArrayUtils.isEmpty(splitCodePaths)) {
+            for (int i = 0; i < splitCodePaths.length; i++) {
+                if (!canSkipForcedApkVerification(splitCodePaths[i])) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
+     * whether the apk contains signed root hash.  Note that the signer's certificate still needs to
+     * match one in a trusted source, and should be done separately.
+     */
+    private boolean canSkipForcedApkVerification(String apkPath) {
+        if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
+            return VerityUtils.hasFsverity(apkPath);
+        }
+
+        try {
+            final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
+            if (rootHashObserved == null) {
+                return false;  // APK does not contain Merkle tree root hash.
+            }
+            synchronized (mPm.mInstallLock) {
+                // Returns whether the observed root hash matches what kernel has.
+                mPm.mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
+                return true;
+            }
+        } catch (Installer.InstallerException | IOException | DigestException
+                | NoSuchAlgorithmException e) {
+            Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
+        }
+        return false;
+    }
+
+    private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
+            boolean forceCollect, boolean skipVerify, boolean mIsPreNMR1Upgrade)
+            throws PackageManagerException {
+        // When upgrading from pre-N MR1, verify the package time stamp using the package
+        // directory and not the APK file.
+        final long lastModifiedTime = mIsPreNMR1Upgrade
+                ? new File(parsedPackage.getPath()).lastModified()
+                : getLastModifiedTime(parsedPackage);
+        final Settings.VersionInfo settingsVersionForPackage =
+                mPm.getSettingsVersionForPackage(parsedPackage);
+        if (ps != null && !forceCollect
+                && ps.getPathString().equals(parsedPackage.getPath())
+                && ps.timeStamp == lastModifiedTime
+                && !PackageManagerService.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
+                && !PackageManagerService.isRecoverSignatureUpdateNeeded(
+                        settingsVersionForPackage)) {
+            if (ps.signatures.mSigningDetails.getSignatures() != null
+                    && ps.signatures.mSigningDetails.getSignatures().length != 0
+                    && ps.signatures.mSigningDetails.getSignatureSchemeVersion()
+                    != SigningDetails.SignatureSchemeVersion.UNKNOWN) {
+                // Optimization: reuse the existing cached signing data
+                // if the package appears to be unchanged.
+                parsedPackage.setSigningDetails(
+                        new SigningDetails(ps.signatures.mSigningDetails));
+                return;
+            }
+
+            Slog.w(TAG, "PackageSetting for " + ps.name
+                    + " is missing signatures.  Collecting certs again to recover them.");
+        } else {
+            Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
+                    + (forceCollect ? " (forced)" : ""));
+        }
+
+        try {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+            final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+                    input, parsedPackage, skipVerify);
+            if (result.isError()) {
+                throw new PackageManagerException(
+                        result.getErrorCode(), result.getErrorMessage(), result.getException());
+            }
+            parsedPackage.setSigningDetails(result.getResult());
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    /**
+     * Clear the package profile if this was an upgrade and the package
+     * version was updated.
+     */
+    private void maybeClearProfilesForUpgradesLI(
+            @Nullable PackageSetting originalPkgSetting,
+            @NonNull AndroidPackage pkg) {
+        if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
+            return;
+        }
+        if (originalPkgSetting.versionCode == pkg.getVersionCode()) {
+            return;
+        }
+
+        mPm.clearAppProfilesLIF(pkg);
+        if (DEBUG_INSTALL) {
+            Slog.d(TAG, originalPkgSetting.name
+                    + " clear profile due to version change "
+                    + originalPkgSetting.versionCode + " != "
+                    + pkg.getVersionCode());
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index 45ce3980..934775a 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -163,14 +163,12 @@
     final int mDataLoaderType;
     final long mRequiredInstalledVersionCode;
     final PackageLite mPackageLite;
-    @NonNull final PackageManagerService mPm;
 
     InstallParams(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
             int installFlags, InstallSource installSource, String volumeUuid,
             UserHandle user, String packageAbiOverride, PackageLite packageLite,
             PackageManagerService pm) {
-        super(user);
-        mPm = pm;
+        super(user, pm);
         mOriginInfo = originInfo;
         mMoveInfo = moveInfo;
         mObserver = observer;
@@ -195,8 +193,7 @@
             PackageInstaller.SessionParams sessionParams, InstallSource installSource,
             UserHandle user, SigningDetails signingDetails, int installerUid,
             PackageLite packageLite, PackageManagerService pm) {
-        super(user);
-        mPm = pm;
+        super(user, pm);
         mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
         mMoveInfo = null;
         mInstallReason = fixUpInstallReason(
@@ -373,7 +370,7 @@
         // state can change within this delay and hence we need to re-verify certain conditions.
         boolean isStaged = (mInstallFlags & INSTALL_STAGED) != 0;
         if (isStaged) {
-            Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
+            Pair<Integer, String> ret = verifyReplacingVersionCode(
                     pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
             mRet = ret.first;
             if (mRet != INSTALL_SUCCEEDED) {
@@ -622,7 +619,7 @@
                 Map<String, ReconciledPackage> reconciledPackages;
                 try {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
-                    reconciledPackages = PackageManagerService.reconcilePackagesLocked(
+                    reconciledPackages = mPm.reconcilePackagesLocked(
                             reconcileRequest, mPm.mSettings.getKeySetManagerService(),
                             mPm.mInjector);
                 } catch (ReconcileFailure e) {
@@ -2075,7 +2072,7 @@
             throws PackageManagerException {
         final Message msg = mPm.mHandler.obtainMessage(INIT_COPY);
         final MultiPackageInstallParams params =
-                new MultiPackageInstallParams(this, children);
+                new MultiPackageInstallParams(this, children, mPm);
         params.setTraceMethod("installStageMultiPackage")
                 .setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
@@ -2107,9 +2104,10 @@
         private final List<InstallParams> mChildParams;
         private final Map<InstallArgs, Integer> mCurrentState;
 
-        MultiPackageInstallParams(InstallParams parent, List<InstallParams> childParams)
+        MultiPackageInstallParams(InstallParams parent, List<InstallParams> childParams,
+                PackageManagerService pm)
                 throws PackageManagerException {
-            super(parent.getUser());
+            super(parent.getUser(), pm);
             if (childParams.size() == 0) {
                 throw new PackageManagerException("No child sessions found!");
             }
@@ -2159,4 +2157,6 @@
                     installRequests);
         }
     }
+
+
 }
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
new file mode 100644
index 0000000..44a973e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -0,0 +1,783 @@
+/*
+ * 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.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
+import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION;
+import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.DEFAULT_VERIFICATION_RESPONSE;
+import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_INSTALL_OBSERVER;
+import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_POST_DELETE;
+import static com.android.server.pm.PackageManagerService.DOMAIN_VERIFICATION;
+import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
+import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_STATUS;
+import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_TIMEOUT;
+import static com.android.server.pm.PackageManagerService.INIT_COPY;
+import static com.android.server.pm.PackageManagerService.INSTANT_APP_RESOLUTION_PHASE_TWO;
+import static com.android.server.pm.PackageManagerService.INTEGRITY_VERIFICATION_COMPLETE;
+import static com.android.server.pm.PackageManagerService.PACKAGE_VERIFIED;
+import static com.android.server.pm.PackageManagerService.POST_INSTALL;
+import static com.android.server.pm.PackageManagerService.SEND_PENDING_BROADCAST;
+import static com.android.server.pm.PackageManagerService.SNAPSHOT_UNCORK;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerService.TRACE_SNAPSHOTS;
+import static com.android.server.pm.PackageManagerService.WRITE_PACKAGE_LIST;
+import static com.android.server.pm.PackageManagerService.WRITE_PACKAGE_RESTRICTIONS;
+import static com.android.server.pm.PackageManagerService.WRITE_SETTINGS;
+
+import android.content.Intent;
+import android.content.pm.IPackageInstallObserver2;
+import android.content.pm.InstantAppRequest;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.stats.storage.StorageEnums;
+import android.util.ArrayMap;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.EventLogTags;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import dalvik.system.VMRuntime;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * Part of PackageManagerService that handles events.
+ */
+public class PackageHandler extends Handler {
+    final PackageManagerService mPm;
+    PackageHandler(Looper looper, PackageManagerService pm) {
+        super(looper);
+        mPm = pm;
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        try {
+            doHandleMessage(msg);
+        } finally {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+        }
+    }
+
+    void doHandleMessage(Message msg) {
+        switch (msg.what) {
+            case INIT_COPY: {
+                HandlerParams params = (HandlerParams) msg.obj;
+                if (params != null) {
+                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
+                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+                            System.identityHashCode(params));
+                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
+                    params.startCopy();
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                }
+                break;
+            }
+            case SEND_PENDING_BROADCAST: {
+                String[] packages;
+                ArrayList<String>[] components;
+                int size = 0;
+                int[] uids;
+                Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+                synchronized (mPm.mLock) {
+                    size = mPm.mPendingBroadcasts.size();
+                    if (size <= 0) {
+                        // Nothing to be done. Just return
+                        return;
+                    }
+                    packages = new String[size];
+                    components = new ArrayList[size];
+                    uids = new int[size];
+                    int i = 0;  // filling out the above arrays
+
+                    for (int n = 0; n < mPm.mPendingBroadcasts.userIdCount(); n++) {
+                        final int packageUserId = mPm.mPendingBroadcasts.userIdAt(n);
+                        final ArrayMap<String, ArrayList<String>> componentsToBroadcast =
+                                mPm.mPendingBroadcasts.packagesForUserId(packageUserId);
+                        final int numComponents = componentsToBroadcast.size();
+                        for (int index = 0; i < size && index < numComponents; index++) {
+                            packages[i] = componentsToBroadcast.keyAt(index);
+                            components[i] = componentsToBroadcast.valueAt(index);
+                            final PackageSetting ps = mPm.mSettings.getPackageLPr(packages[i]);
+                            uids[i] = (ps != null)
+                                    ? UserHandle.getUid(packageUserId, ps.appId)
+                                    : -1;
+                            i++;
+                        }
+                    }
+                    size = i;
+                    mPm.mPendingBroadcasts.clear();
+                }
+                // Send broadcasts
+                for (int i = 0; i < size; i++) {
+                    mPm.sendPackageChangedBroadcast(packages[i], true /* dontKillApp */,
+                            components[i], uids[i], null /* reason */);
+                }
+                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                break;
+            }
+            case POST_INSTALL: {
+                if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
+
+                PackageManagerService.PostInstallData data = mPm.mRunningInstalls.get(msg.arg1);
+                final boolean didRestore = (msg.arg2 != 0);
+                mPm.mRunningInstalls.delete(msg.arg1);
+
+                if (data != null && data.res.mFreezer != null) {
+                    data.res.mFreezer.close();
+                }
+
+                if (data != null && data.mPostInstallRunnable != null) {
+                    data.mPostInstallRunnable.run();
+                } else if (data != null && data.args != null) {
+                    InstallArgs args = data.args;
+                    PackageInstalledInfo parentRes = data.res;
+
+                    final boolean killApp = (args.mInstallFlags
+                            & PackageManager.INSTALL_DONT_KILL_APP) == 0;
+                    final boolean virtualPreload = ((args.mInstallFlags
+                            & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+
+                    handlePackagePostInstall(parentRes, killApp, virtualPreload,
+                            didRestore, args.mInstallSource.installerPackageName,
+                            args.mObserver, args.mDataLoaderType);
+
+                    // Log tracing if needed
+                    if (args.mTraceMethod != null) {
+                        Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.mTraceMethod,
+                                args.mTraceCookie);
+                    }
+                } else if (DEBUG_INSTALL) {
+                    // No post-install when we run restore from installExistingPackageForUser
+                    Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
+                }
+
+                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
+            } break;
+            case DEFERRED_NO_KILL_POST_DELETE: {
+                synchronized (mPm.mInstallLock) {
+                    InstallArgs args = (InstallArgs) msg.obj;
+                    if (args != null) {
+                        args.doPostDeleteLI(true);
+                    }
+                }
+            } break;
+            case DEFERRED_NO_KILL_INSTALL_OBSERVER: {
+                String packageName = (String) msg.obj;
+                if (packageName != null) {
+                    mPm.notifyInstallObserver(packageName);
+                }
+            } break;
+            case WRITE_SETTINGS: {
+                Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+                synchronized (mPm.mLock) {
+                    removeMessages(WRITE_SETTINGS);
+                    removeMessages(WRITE_PACKAGE_RESTRICTIONS);
+                    mPm.writeSettingsLPrTEMP();
+                    mPm.mDirtyUsers.clear();
+                }
+                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+            } break;
+            case WRITE_PACKAGE_RESTRICTIONS: {
+                Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+                synchronized (mPm.mLock) {
+                    removeMessages(WRITE_PACKAGE_RESTRICTIONS);
+                    for (int userId : mPm.mDirtyUsers) {
+                        mPm.mSettings.writePackageRestrictionsLPr(userId);
+                    }
+                    mPm.mDirtyUsers.clear();
+                }
+                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+            } break;
+            case WRITE_PACKAGE_LIST: {
+                Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+                synchronized (mPm.mLock) {
+                    removeMessages(WRITE_PACKAGE_LIST);
+                    mPm.mSettings.writePackageListLPr(msg.arg1);
+                }
+                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+            } break;
+            case CHECK_PENDING_VERIFICATION: {
+                final int verificationId = msg.arg1;
+                final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
+
+                if ((state != null) && !state.isVerificationComplete()
+                        && !state.timeoutExtended()) {
+                    final VerificationParams params = state.getVerificationParams();
+                    final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+
+                    String errorMsg = "Verification timed out for " + originUri;
+                    Slog.i(TAG, errorMsg);
+
+                    final UserHandle user = params.getUser();
+                    if (getDefaultVerificationResponse(user)
+                            == PackageManager.VERIFICATION_ALLOW) {
+                        Slog.i(TAG, "Continuing with installation of " + originUri);
+                        state.setVerifierResponse(Binder.getCallingUid(),
+                                PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
+                        mPm.broadcastPackageVerified(verificationId, originUri,
+                                PackageManager.VERIFICATION_ALLOW, null, params.mDataLoaderType,
+                                user);
+                    } else {
+                        mPm.broadcastPackageVerified(verificationId, originUri,
+                                PackageManager.VERIFICATION_REJECT, null,
+                                params.mDataLoaderType, user);
+                        params.setReturnCode(
+                                PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
+                        state.setVerifierResponse(Binder.getCallingUid(),
+                                PackageManager.VERIFICATION_REJECT);
+                    }
+
+                    if (state.areAllVerificationsComplete()) {
+                        mPm.mPendingVerification.remove(verificationId);
+                    }
+
+                    Trace.asyncTraceEnd(
+                            TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
+
+                    params.handleVerificationFinished();
+
+                }
+                break;
+            }
+            case CHECK_PENDING_INTEGRITY_VERIFICATION: {
+                final int verificationId = msg.arg1;
+                final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
+
+                if (state != null && !state.isIntegrityVerificationComplete()) {
+                    final VerificationParams params = state.getVerificationParams();
+                    final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+
+                    String errorMsg = "Integrity verification timed out for " + originUri;
+                    Slog.i(TAG, errorMsg);
+
+                    state.setIntegrityVerificationResult(
+                            getDefaultIntegrityVerificationResponse());
+
+                    if (getDefaultIntegrityVerificationResponse()
+                            == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
+                        Slog.i(TAG, "Integrity check times out, continuing with " + originUri);
+                    } else {
+                        params.setReturnCode(
+                                PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+                                errorMsg);
+                    }
+
+                    if (state.areAllVerificationsComplete()) {
+                        mPm.mPendingVerification.remove(verificationId);
+                    }
+
+                    Trace.asyncTraceEnd(
+                            TRACE_TAG_PACKAGE_MANAGER,
+                            "integrity_verification",
+                            verificationId);
+
+                    params.handleIntegrityVerificationFinished();
+                }
+                break;
+            }
+            case PACKAGE_VERIFIED: {
+                final int verificationId = msg.arg1;
+
+                final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
+                if (state == null) {
+                    Slog.w(TAG, "Verification with id " + verificationId
+                            + " not found."
+                            + " It may be invalid or overridden by integrity verification");
+                    break;
+                }
+
+                final PackageVerificationResponse response = (PackageVerificationResponse) msg.obj;
+
+                state.setVerifierResponse(response.callerUid, response.code);
+
+                if (state.isVerificationComplete()) {
+                    final VerificationParams params = state.getVerificationParams();
+                    final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+
+                    if (state.isInstallAllowed()) {
+                        mPm.broadcastPackageVerified(verificationId, originUri,
+                                response.code, null, params.mDataLoaderType, params.getUser());
+                    } else {
+                        params.setReturnCode(
+                                PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+                                "Install not allowed");
+                    }
+
+                    if (state.areAllVerificationsComplete()) {
+                        mPm.mPendingVerification.remove(verificationId);
+                    }
+
+                    Trace.asyncTraceEnd(
+                            TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
+
+                    params.handleVerificationFinished();
+                }
+
+                break;
+            }
+            case INTEGRITY_VERIFICATION_COMPLETE: {
+                final int verificationId = msg.arg1;
+
+                final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
+                if (state == null) {
+                    Slog.w(TAG, "Integrity verification with id " + verificationId
+                            + " not found. It may be invalid or overridden by verifier");
+                    break;
+                }
+
+                final int response = (Integer) msg.obj;
+                final VerificationParams params = state.getVerificationParams();
+                final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+
+                state.setIntegrityVerificationResult(response);
+
+                if (response == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
+                    Slog.i(TAG, "Integrity check passed for " + originUri);
+                } else {
+                    params.setReturnCode(
+                            PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+                            "Integrity check failed for " + originUri);
+                }
+
+                if (state.areAllVerificationsComplete()) {
+                    mPm.mPendingVerification.remove(verificationId);
+                }
+
+                Trace.asyncTraceEnd(
+                        TRACE_TAG_PACKAGE_MANAGER,
+                        "integrity_verification",
+                        verificationId);
+
+                params.handleIntegrityVerificationFinished();
+                break;
+            }
+            case INSTANT_APP_RESOLUTION_PHASE_TWO: {
+                InstantAppResolver.doInstantAppResolutionPhaseTwo(mPm.mContext,
+                        mPm.mInstantAppResolverConnection,
+                        (InstantAppRequest) msg.obj,
+                        mPm.mInstantAppInstallerActivity,
+                        mPm.mHandler);
+                break;
+            }
+            case ENABLE_ROLLBACK_STATUS: {
+                final int enableRollbackToken = msg.arg1;
+                final int enableRollbackCode = msg.arg2;
+                final VerificationParams params =
+                        mPm.mPendingEnableRollback.get(enableRollbackToken);
+                if (params == null) {
+                    Slog.w(TAG, "Invalid rollback enabled token "
+                            + enableRollbackToken + " received");
+                    break;
+                }
+
+                mPm.mPendingEnableRollback.remove(enableRollbackToken);
+
+                if (enableRollbackCode != PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED) {
+                    final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+                    Slog.w(TAG, "Failed to enable rollback for " + originUri);
+                    Slog.w(TAG, "Continuing with installation of " + originUri);
+                }
+
+                Trace.asyncTraceEnd(
+                        TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
+
+                params.handleRollbackEnabled();
+                break;
+            }
+            case ENABLE_ROLLBACK_TIMEOUT: {
+                final int enableRollbackToken = msg.arg1;
+                final int sessionId = msg.arg2;
+                final VerificationParams params =
+                        mPm.mPendingEnableRollback.get(enableRollbackToken);
+                if (params != null) {
+                    final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+
+                    Slog.w(TAG, "Enable rollback timed out for " + originUri);
+                    mPm.mPendingEnableRollback.remove(enableRollbackToken);
+
+                    Slog.w(TAG, "Continuing with installation of " + originUri);
+                    Trace.asyncTraceEnd(
+                            TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
+                    params.handleRollbackEnabled();
+                    Intent rollbackTimeoutIntent = new Intent(
+                            Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
+                    rollbackTimeoutIntent.putExtra(
+                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
+                            sessionId);
+                    rollbackTimeoutIntent.addFlags(
+                            Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                    mPm.mContext.sendBroadcastAsUser(rollbackTimeoutIntent, UserHandle.SYSTEM,
+                            android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
+                }
+                break;
+            }
+            case DOMAIN_VERIFICATION: {
+                int messageCode = msg.arg1;
+                Object object = msg.obj;
+                mPm.mDomainVerificationManager.runMessage(messageCode, object);
+                break;
+            }
+            case SNAPSHOT_UNCORK: {
+                int corking = mPm.sSnapshotCorked.decrementAndGet();
+                if (TRACE_SNAPSHOTS && corking == 0) {
+                    Log.e(TAG, "snapshot: corking goes to zero in message handler");
+                }
+                break;
+            }
+        }
+    }
+
+    private void handlePackagePostInstall(PackageInstalledInfo res, boolean killApp,
+            boolean virtualPreload, boolean launchedForRestore, String installerPackage,
+            IPackageInstallObserver2 installObserver, int dataLoaderType) {
+        boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
+        final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
+        final String packageName = res.mName;
+        final PackageSetting pkgSetting = succeeded ? mPm.getPackageSetting(packageName) : null;
+        final boolean removedBeforeUpdate = (pkgSetting == null)
+                || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals(
+                res.mPkg.getPath()));
+        if (succeeded && removedBeforeUpdate) {
+            Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
+                    + "could be executed");
+            res.mReturnCode = INSTALL_FAILED_PACKAGE_CHANGED;
+            res.mReturnMsg = "Package was removed before install could complete.";
+
+            // Remove the update failed package's older resources safely now
+            InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
+            if (args != null) {
+                synchronized (mPm.mInstallLock) {
+                    args.doPostDeleteLI(true);
+                }
+            }
+            mPm.notifyInstallObserver(res, installObserver);
+            return;
+        }
+
+        if (succeeded) {
+            // Clear the uid cache after we installed a new package.
+            mPm.mPerUidReadTimeoutsCache = null;
+
+            // Send the removed broadcasts
+            if (res.mRemovedInfo != null) {
+                res.mRemovedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
+            }
+
+            final String installerPackageName =
+                    res.mInstallerPackageName != null
+                            ? res.mInstallerPackageName
+                            : res.mRemovedInfo != null
+                                    ? res.mRemovedInfo.mInstallerPackageName
+                                    : null;
+
+            synchronized (mPm.mLock) {
+                mPm.mInstantAppRegistry.onPackageInstalledLPw(res.mPkg, res.mNewUsers);
+            }
+
+            // Determine the set of users who are adding this package for
+            // the first time vs. those who are seeing an update.
+            int[] firstUserIds = EMPTY_INT_ARRAY;
+            int[] firstInstantUserIds = EMPTY_INT_ARRAY;
+            int[] updateUserIds = EMPTY_INT_ARRAY;
+            int[] instantUserIds = EMPTY_INT_ARRAY;
+            final boolean allNewUsers = res.mOrigUsers == null || res.mOrigUsers.length == 0;
+            for (int newUser : res.mNewUsers) {
+                final boolean isInstantApp = pkgSetting.getInstantApp(newUser);
+                if (allNewUsers) {
+                    if (isInstantApp) {
+                        firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
+                    } else {
+                        firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
+                    }
+                    continue;
+                }
+                boolean isNew = true;
+                for (int origUser : res.mOrigUsers) {
+                    if (origUser == newUser) {
+                        isNew = false;
+                        break;
+                    }
+                }
+                if (isNew) {
+                    if (isInstantApp) {
+                        firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
+                    } else {
+                        firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
+                    }
+                } else {
+                    if (isInstantApp) {
+                        instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);
+                    } else {
+                        updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);
+                    }
+                }
+            }
+
+            // Send installed broadcasts if the package is not a static shared lib.
+            if (res.mPkg.getStaticSharedLibName() == null) {
+                mPm.mProcessLoggingHandler.invalidateBaseApkHash(res.mPkg.getBaseApkPath());
+
+                // Send added for users that see the package for the first time
+                // sendPackageAddedForNewUsers also deals with system apps
+                int appId = UserHandle.getAppId(res.mUid);
+                boolean isSystem = res.mPkg.isSystem();
+                mPm.sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
+                        virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds,
+                        dataLoaderType);
+
+                // Send added for users that don't see the package for the first time
+                Bundle extras = new Bundle(1);
+                extras.putInt(Intent.EXTRA_UID, res.mUid);
+                if (update) {
+                    extras.putBoolean(Intent.EXTRA_REPLACING, true);
+                }
+                extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
+                // Send to all running apps.
+                final SparseArray<int[]> newBroadcastAllowList;
+
+                synchronized (mPm.mLock) {
+                    newBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
+                            mPm.getPackageSettingInternal(res.mName, Process.SYSTEM_UID),
+                            updateUserIds, mPm.mSettings.getPackagesLocked());
+                }
+                mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+                        extras, 0 /*flags*/,
+                        null /*targetPackage*/, null /*finishedReceiver*/,
+                        updateUserIds, instantUserIds, newBroadcastAllowList, null);
+                if (installerPackageName != null) {
+                    // Send to the installer, even if it's not running.
+                    mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+                            extras, 0 /*flags*/,
+                            installerPackageName, null /*finishedReceiver*/,
+                            updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+                }
+                // if the required verifier is defined, but, is not the installer of record
+                // for the package, it gets notified
+                final boolean notifyVerifier = mPm.mRequiredVerifierPackage != null
+                        && !mPm.mRequiredVerifierPackage.equals(installerPackageName);
+                if (notifyVerifier) {
+                    mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+                            extras, 0 /*flags*/,
+                            mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
+                            updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+                }
+                // If package installer is defined, notify package installer about new
+                // app installed
+                if (mPm.mRequiredInstallerPackage != null) {
+                    mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+                            extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
+                            mPm.mRequiredInstallerPackage, null /*finishedReceiver*/,
+                            firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
+                }
+
+                // Send replaced for users that don't see the package for the first time
+                if (update) {
+                    mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+                            packageName, extras, 0 /*flags*/,
+                            null /*targetPackage*/, null /*finishedReceiver*/,
+                            updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
+                            null);
+                    if (installerPackageName != null) {
+                        mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+                                extras, 0 /*flags*/,
+                                installerPackageName, null /*finishedReceiver*/,
+                                updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
+                    }
+                    if (notifyVerifier) {
+                        mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+                                extras, 0 /*flags*/,
+                                mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
+                                updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
+                    }
+                    mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
+                            null /*package*/, null /*extras*/, 0 /*flags*/,
+                            packageName /*targetPackage*/,
+                            null /*finishedReceiver*/, updateUserIds, instantUserIds,
+                            null /*broadcastAllowList*/,
+                            mPm.getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED)
+                                    .toBundle());
+                } else if (launchedForRestore && !res.mPkg.isSystem()) {
+                    // First-install and we did a restore, so we're responsible for the
+                    // first-launch broadcast.
+                    if (DEBUG_BACKUP) {
+                        Slog.i(TAG, "Post-restore of " + packageName
+                                + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
+                    }
+                    mPm.sendFirstLaunchBroadcast(packageName, installerPackage,
+                            firstUserIds, firstInstantUserIds);
+                }
+
+                // Send broadcast package appeared if external for all users
+                if (res.mPkg.isExternalStorage()) {
+                    if (!update) {
+                        final StorageManager storage = mPm.mInjector.getSystemService(
+                                StorageManager.class);
+                        VolumeInfo volume =
+                                storage.findVolumeByUuid(
+                                        res.mPkg.getStorageUuid().toString());
+                        int packageExternalStorageType =
+                                PackageManagerService.getPackageExternalStorageType(volume,
+                                        res.mPkg.isExternalStorage());
+                        // If the package was installed externally, log it.
+                        if (packageExternalStorageType != StorageEnums.UNKNOWN) {
+                            FrameworkStatsLog.write(
+                                    FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
+                                    packageExternalStorageType, packageName);
+                        }
+                    }
+                    if (DEBUG_INSTALL) {
+                        Slog.i(TAG, "upgrading pkg " + res.mPkg + " is external");
+                    }
+                    final int[] uidArray = new int[]{res.mPkg.getUid()};
+                    ArrayList<String> pkgList = new ArrayList<>(1);
+                    pkgList.add(packageName);
+                    mPm.sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
+                }
+            } else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib
+                for (int i = 0; i < res.mLibraryConsumers.size(); i++) {
+                    AndroidPackage pkg = res.mLibraryConsumers.get(i);
+                    // send broadcast that all consumers of the static shared library have changed
+                    mPm.sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */,
+                            new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
+                            pkg.getUid(), null);
+                }
+            }
+
+            // Work that needs to happen on first install within each user
+            if (firstUserIds != null && firstUserIds.length > 0) {
+                for (int userId : firstUserIds) {
+                    mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
+                            userId);
+                }
+            }
+
+            if (allNewUsers && !update) {
+                mPm.notifyPackageAdded(packageName, res.mUid);
+            } else {
+                mPm.notifyPackageChanged(packageName, res.mUid);
+            }
+
+            // Log current value of "unknown sources" setting
+            EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
+                    getUnknownSourcesSettings());
+
+            // Remove the replaced package's older resources safely now
+            InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
+            if (args != null) {
+                if (!killApp) {
+                    // If we didn't kill the app, defer the deletion of code/resource files, since
+                    // they may still be in use by the running application. This mitigates problems
+                    // in cases where resources or code is loaded by a new Activity before
+                    // ApplicationInfo changes have propagated to all application threads.
+                    mPm.scheduleDeferredNoKillPostDelete(args);
+                } else {
+                    synchronized (mPm.mInstallLock) {
+                        args.doPostDeleteLI(true);
+                    }
+                }
+            } else {
+                // Force a gc to clear up things. Ask for a background one, it's fine to go on
+                // and not block here.
+                VMRuntime.getRuntime().requestConcurrentGC();
+            }
+
+            // Notify DexManager that the package was installed for new users.
+            // The updated users should already be indexed and the package code paths
+            // should not change.
+            // Don't notify the manager for ephemeral apps as they are not expected to
+            // survive long enough to benefit of background optimizations.
+            for (int userId : firstUserIds) {
+                PackageInfo info = mPm.getPackageInfo(packageName, /*flags*/ 0, userId);
+                // There's a race currently where some install events may interleave with an
+                // uninstall. This can lead to package info being null (b/36642664).
+                if (info != null) {
+                    mPm.getDexManager().notifyPackageInstalled(info, userId);
+                }
+            }
+        }
+
+        final boolean deferInstallObserver = succeeded && update && !killApp;
+        if (deferInstallObserver) {
+            mPm.scheduleDeferredNoKillInstallObserver(res, installObserver);
+        } else {
+            mPm.notifyInstallObserver(res, installObserver);
+        }
+    }
+
+    /**
+     * Get the default verification agent response code.
+     *
+     * @return default verification response code
+     */
+    private int getDefaultVerificationResponse(UserHandle user) {
+        if (mPm.mUserManager.hasUserRestriction(UserManager.ENSURE_VERIFY_APPS,
+                user.getIdentifier())) {
+            return PackageManager.VERIFICATION_REJECT;
+        }
+        return android.provider.Settings.Global.getInt(mPm.mContext.getContentResolver(),
+                android.provider.Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
+                DEFAULT_VERIFICATION_RESPONSE);
+    }
+
+    /**
+     * Get the default integrity verification response code.
+     */
+    private int getDefaultIntegrityVerificationResponse() {
+        // We are not exposing this as a user-configurable setting because we don't want to provide
+        // an easy way to get around the integrity check.
+        return PackageManager.VERIFICATION_REJECT;
+    }
+
+    /**
+     * Get the "allow unknown sources" setting.
+     *
+     * @return the current "allow unknown sources" setting
+     */
+    private int getUnknownSourcesSettings() {
+        return android.provider.Settings.Secure.getIntForUser(mPm.mContext.getContentResolver(),
+                android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
+                -1, UserHandle.USER_SYSTEM);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 722c1f4..2c2884f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -39,11 +39,9 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
 import static android.content.pm.PackageManager.INSTALL_INTERNAL;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
 import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -74,7 +72,6 @@
 import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
 import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
 import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
-import static android.os.PowerWhitelistManager.REASON_PACKAGE_REPLACED;
 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.incremental.IncrementalManager.isIncrementalPath;
@@ -86,10 +83,7 @@
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
-import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
-import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
-import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
 import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
@@ -98,13 +92,10 @@
 import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
 import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
 import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
-import static com.android.server.pm.PackageManagerServiceUtils.decompressFile;
 import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
 import static com.android.server.pm.PackageManagerServiceUtils.dumpCriticalInfo;
-import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
 import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
-import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
 import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
 import static com.android.server.pm.parsing.PackageInfoUtils.checkUseInstalledOrHidden;
 
@@ -175,7 +166,6 @@
 import android.content.pm.ModuleInfo;
 import android.content.pm.PackageChangeEvent;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.ComponentEnabledSetting;
@@ -188,7 +178,6 @@
 import android.content.pm.PackageManagerInternal.PackageListObserver;
 import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
 import android.content.pm.PackagePartitions;
-import android.content.pm.PackagePartitions.SystemPartition;
 import android.content.pm.PackageStats;
 import android.content.pm.PackageUserState;
 import android.content.pm.ParceledListSlice;
@@ -202,7 +191,6 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
-import android.content.pm.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.SigningInfo;
 import android.content.pm.StagedApexInfo;
 import android.content.pm.SuspendDialogInfo;
@@ -275,7 +263,6 @@
 import android.security.KeyStore;
 import android.service.pm.PackageServiceDumpProto;
 import android.stats.storage.StorageEnums;
-import android.system.ErrnoException;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -311,13 +298,11 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.content.om.OverlayConfig;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.policy.AttributeCache;
-import com.android.internal.security.VerityUtils;
 import com.android.internal.telephony.CarrierAppUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
@@ -387,7 +372,6 @@
 
 import dalvik.system.VMRuntime;
 
-import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 import libcore.util.HexEncoding;
 
@@ -408,7 +392,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.nio.charset.StandardCharsets;
-import java.security.DigestException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
@@ -489,7 +472,7 @@
     static final boolean DEBUG_PREFERRED = false;
     static final boolean DEBUG_UPGRADE = false;
     static final boolean DEBUG_DOMAIN_VERIFICATION = false;
-    private static final boolean DEBUG_BACKUP = false;
+    static final boolean DEBUG_BACKUP = false;
     public static final boolean DEBUG_INSTALL = false;
     public static final boolean DEBUG_REMOVE = false;
     private static final boolean DEBUG_BROADCASTS = false;
@@ -622,7 +605,7 @@
     /** Suffix of stub packages on the system partition */
     public final static String STUB_SUFFIX = "-Stub";
 
-    private static final int[] EMPTY_INT_ARRAY = new int[0];
+    static final int[] EMPTY_INT_ARRAY = new int[0];
 
     /**
      * Timeout (in milliseconds) after which the watchdog should declare that
@@ -674,7 +657,7 @@
      * This can be either PackageManager.VERIFICATION_ALLOW or
      * PackageManager.VERIFICATION_REJECT.
      */
-    private static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW;
+    static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW;
 
     /**
      * Adding an installer package name to a package that does not have one set requires the
@@ -769,7 +752,7 @@
 
     final Handler mHandler;
 
-    private final ProcessLoggingHandler mProcessLoggingHandler;
+    final ProcessLoggingHandler mProcessLoggingHandler;
 
     private final boolean mEnableFreeCacheV2;
 
@@ -838,7 +821,7 @@
      * find updated user-installed versions. Keys are package name, values
      * are package location.
      */
-    final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
+    final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
 
     /**
      * Tracks existing packages prior to receiving an OTA. Keys are package name.
@@ -857,7 +840,7 @@
      * @see Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL
      * @see #systemReady()
      */
-    private @Nullable List<File> mReleaseOnSystemReady;
+    @Nullable List<File> mReleaseOnSystemReady;
 
     /**
      * Whether or not system app permissions should be promoted from install to runtime.
@@ -900,7 +883,7 @@
     final ArrayMap<String, FeatureInfo> mAvailableFeatures;
 
     @Watched
-    private final InstantAppRegistry mInstantAppRegistry;
+    final InstantAppRegistry mInstantAppRegistry;
 
     @GuardedBy("mLock")
     int mChangedPackagesSequenceNumber;
@@ -943,7 +926,7 @@
         new ArrayList<>();
 
     // Cached parsed flag value. Invalidated on each flag change.
-    private PerUidReadTimeouts[] mPerUidReadTimeoutsCache;
+    PerUidReadTimeouts[] mPerUidReadTimeoutsCache;
 
     private static final PerUidReadTimeouts[] EMPTY_PER_UID_READ_TIMEOUTS_ARRAY = {};
 
@@ -1441,7 +1424,7 @@
     boolean mResolverReplaced = false;
 
     @NonNull
-    private final DomainVerificationManagerInternal mDomainVerificationManager;
+    final DomainVerificationManagerInternal mDomainVerificationManager;
 
     /** The service connection to the ephemeral resolver */
     final InstantAppResolverConnection mInstantAppResolverConnection;
@@ -1585,7 +1568,7 @@
     final UserManagerService mUserManager;
 
     // Stores a list of users whose package restrictions file needs to be updated
-    private final ArraySet<Integer> mDirtyUsers = new ArraySet<>();
+    final ArraySet<Integer> mDirtyUsers = new ArraySet<>();
 
     // Recordkeeping of restore-after-install operations that are currently in flight
     // between the Package Manager and the Backup Manager
@@ -2050,6 +2033,13 @@
         boolean filterAppAccess(int uid, int callingUid);
         @LiveImplementation(override = LiveImplementation.MANDATORY)
         void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
+        FindPreferredActivityBodyResult findPreferredActivityInternal(Intent intent,
+                String resolvedType, int flags, List<ResolveInfo> query, boolean always,
+                boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
+        ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType, int flags,
+                List<ResolveInfo> query, boolean debug, int userId);
     }
 
     /**
@@ -2858,7 +2848,24 @@
             }
             allHomeCandidates.addAll(resolveInfos);
 
-            final String packageName = mDefaultAppProvider.getDefaultHome(userId);
+            String packageName = mDefaultAppProvider.getDefaultHome(userId);
+            if (packageName == null) {
+                // Role changes are not and cannot be atomic because its implementation lives inside
+                // a system app, so when the home role changes, there is a window when the previous
+                // role holder is removed and the new role holder is granted the preferred activity,
+                // but hasn't become the role holder yet. However, this case may be easily hit
+                // because the preferred activity change triggers a broadcast and receivers may try
+                // to get the default home activity there. So we need to fix it for this time
+                // window, and an easy workaround is to fallback to the current preferred activity.
+                final int appId = UserHandle.getAppId(Binder.getCallingUid());
+                final boolean filtered = appId >= Process.FIRST_APPLICATION_UID;
+                FindPreferredActivityBodyResult result = findPreferredActivityInternal(
+                        intent, null, 0, resolveInfos, true, false, false, userId, filtered);
+                ResolveInfo preferredResolveInfo =  result.mPreferredResolveInfo;
+                if (preferredResolveInfo != null && preferredResolveInfo.activityInfo != null) {
+                    packageName = preferredResolveInfo.activityInfo.packageName;
+                }
+            }
             if (packageName == null) {
                 return null;
             }
@@ -4610,8 +4617,14 @@
 
         public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
             final String packageName = dumpState.getTargetPackageName();
+            final PackageSetting setting = mSettings.getPackageLPr(packageName);
             final boolean checkin = dumpState.isCheckIn();
 
+            // Return if the package doesn't exist.
+            if (packageName != null && setting == null) {
+                return;
+            }
+
             switch (type) {
                 case DumpState.DUMP_VERSION:
                 {
@@ -4704,8 +4717,7 @@
 
                 case DumpState.DUMP_QUERIES:
                 {
-                    final PackageSetting setting = mSettings.getPackageLPr(packageName);
-                    Integer filteringAppId = setting == null ? null : setting.appId;
+                    final Integer filteringAppId = setting == null ? null : setting.appId;
                     mAppsFilter.dumpQueries(
                             pw, filteringAppId, dumpState, mUserManager.getUserIds(),
                             this::getPackagesForUidInternalBody);
@@ -4716,8 +4728,9 @@
                 {
                     final android.util.IndentingPrintWriter writer =
                             new android.util.IndentingPrintWriter(pw);
-                    if (dumpState.onTitlePrinted()) pw.println();
-
+                    if (dumpState.onTitlePrinted()) {
+                        pw.println();
+                    }
                     writer.println("Domain verification status:");
                     writer.increaseIndent();
                     try {
@@ -4734,18 +4747,14 @@
                 case DumpState.DUMP_DEXOPT:
                 {
                     final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
-                    ipw.println();
+                    if (dumpState.onTitlePrinted()) {
+                        pw.println();
+                    }
                     ipw.println("Dexopt state:");
                     ipw.increaseIndent();
                     Collection<PackageSetting> pkgSettings;
-                    if (packageName != null) {
-                        PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName);
-                        if (targetPkgSetting != null) {
-                            pkgSettings = Collections.singletonList(targetPkgSetting);
-                        } else {
-                            ipw.println("Unable to find package: " + packageName);
-                            return;
-                        }
+                    if (setting != null) {
+                        pkgSettings = Collections.singletonList(setting);
                     } else {
                         pkgSettings = mSettings.getPackagesLocked().values();
                     }
@@ -4755,10 +4764,12 @@
                         if (pkg == null) {
                             continue;
                         }
-                        ipw.println("[" + pkgSetting.name + "]");
+                        final String pkgName = pkg.getPackageName();
+                        ipw.println("[" + pkgName + "]");
                         ipw.increaseIndent();
+
                         mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting,
-                                mDexManager.getPackageUseInfoOrDefault(pkg.getPackageName()));
+                                mDexManager.getPackageUseInfoOrDefault(pkgName));
                         ipw.decreaseIndent();
                     }
                     break;
@@ -4767,28 +4778,29 @@
                 case DumpState.DUMP_COMPILER_STATS:
                 {
                     final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
-                    ipw.println();
+                    if (dumpState.onTitlePrinted()) {
+                        pw.println();
+                    }
                     ipw.println("Compiler stats:");
                     ipw.increaseIndent();
-                    Collection<AndroidPackage> packages;
-                    if (packageName != null) {
-                        AndroidPackage targetPackage = mPackages.get(packageName);
-                        if (targetPackage != null) {
-                            packages = Collections.singletonList(targetPackage);
-                        } else {
-                            ipw.println("Unable to find package: " + packageName);
-                            return;
-                        }
+                    Collection<PackageSetting> pkgSettings;
+                    if (setting != null) {
+                        pkgSettings = Collections.singletonList(setting);
                     } else {
-                        packages = mPackages.values();
+                        pkgSettings = mSettings.getPackagesLocked().values();
                     }
 
-                    for (AndroidPackage pkg : packages) {
+                    for (PackageSetting pkgSetting : pkgSettings) {
+                        final AndroidPackage pkg = pkgSetting.getPkg();
+                        if (pkg == null) {
+                            continue;
+                        }
                         final String pkgName = pkg.getPackageName();
                         ipw.println("[" + pkgName + "]");
                         ipw.increaseIndent();
 
-                        CompilerStats.PackageStats stats = mCompilerStats.getPackageStats(pkgName);
+                        final CompilerStats.PackageStats stats =
+                                mCompilerStats.getPackageStats(pkgName);
                         if (stats == null) {
                             ipw.println("(No recorded stats)");
                         } else {
@@ -4800,6 +4812,284 @@
                 }
             } // switch
         }
+
+        // The body of findPreferredActivity.
+        protected FindPreferredActivityBodyResult findPreferredActivityBody(
+                Intent intent, String resolvedType, int flags,
+                List<ResolveInfo> query, boolean always,
+                boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered,
+                int callingUid, boolean isDeviceProvisioned) {
+            FindPreferredActivityBodyResult result = new FindPreferredActivityBodyResult();
+
+            flags = updateFlagsForResolve(
+                    flags, userId, callingUid, false /*includeInstantApps*/,
+                    isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+                            resolvedType, flags));
+            intent = updateIntentForResolve(intent);
+
+            // Try to find a matching persistent preferred activity.
+            result.mPreferredResolveInfo = findPersistentPreferredActivityLP(intent,
+                    resolvedType, flags, query, debug, userId);
+
+            // If a persistent preferred activity matched, use it.
+            if (result.mPreferredResolveInfo != null) {
+                return result;
+            }
+
+            PreferredIntentResolver pir = mSettings.getPreferredActivities(userId);
+            // Get the list of preferred activities that handle the intent
+            if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
+            List<PreferredActivity> prefs = pir != null
+                    ? pir.queryIntent(intent, resolvedType,
+                            (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+                            userId)
+                    : null;
+            if (prefs != null && prefs.size() > 0) {
+
+                // First figure out how good the original match set is.
+                // We will only allow preferred activities that came
+                // from the same match quality.
+                int match = 0;
+
+                if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Figuring out best match...");
+
+                final int N = query.size();
+                for (int j = 0; j < N; j++) {
+                    final ResolveInfo ri = query.get(j);
+                    if (DEBUG_PREFERRED || debug) {
+                        Slog.v(TAG, "Match for " + ri.activityInfo
+                                + ": 0x" + Integer.toHexString(match));
+                    }
+                    if (ri.match > match) {
+                        match = ri.match;
+                    }
+                }
+
+                if (DEBUG_PREFERRED || debug) {
+                    Slog.v(TAG, "Best match: 0x" + Integer.toHexString(match));
+                }
+                match &= IntentFilter.MATCH_CATEGORY_MASK;
+                final int M = prefs.size();
+                for (int i = 0; i < M; i++) {
+                    final PreferredActivity pa = prefs.get(i);
+                    if (DEBUG_PREFERRED || debug) {
+                        Slog.v(TAG, "Checking PreferredActivity ds="
+                                + (pa.countDataSchemes() > 0 ? pa.getDataScheme(0) : "<none>")
+                                + "\n  component=" + pa.mPref.mComponent);
+                        pa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
+                    }
+                    if (pa.mPref.mMatch != match) {
+                        if (DEBUG_PREFERRED || debug) {
+                            Slog.v(TAG, "Skipping bad match "
+                                    + Integer.toHexString(pa.mPref.mMatch));
+                        }
+                        continue;
+                    }
+                    // If it's not an "always" type preferred activity and that's what we're
+                    // looking for, skip it.
+                    if (always && !pa.mPref.mAlways) {
+                        if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping mAlways=false entry");
+                        continue;
+                    }
+                    final ActivityInfo ai = getActivityInfo(
+                            pa.mPref.mComponent, flags | MATCH_DISABLED_COMPONENTS
+                                    | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                            userId);
+                    if (DEBUG_PREFERRED || debug) {
+                        Slog.v(TAG, "Found preferred activity:");
+                        if (ai != null) {
+                            ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
+                        } else {
+                            Slog.v(TAG, "  null");
+                        }
+                    }
+                    final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent)
+                            && !isDeviceProvisioned;
+                    final boolean allowSetMutation = !excludeSetupWizardHomeActivity
+                            && !queryMayBeFiltered;
+                    if (ai == null) {
+                        // Do not remove launcher's preferred activity during SetupWizard
+                        // due to it may not install yet
+                        if (!allowSetMutation) {
+                            continue;
+                        }
+
+                        // This previously registered preferred activity
+                        // component is no longer known.  Most likely an update
+                        // to the app was installed and in the new version this
+                        // component no longer exists.  Clean it up by removing
+                        // it from the preferred activities list, and skip it.
+                        Slog.w(TAG, "Removing dangling preferred activity: "
+                                + pa.mPref.mComponent);
+                        pir.removeFilter(pa);
+                        result.mChanged = true;
+                        continue;
+                    }
+                    for (int j = 0; j < N; j++) {
+                        final ResolveInfo ri = query.get(j);
+                        if (!ri.activityInfo.applicationInfo.packageName
+                                .equals(ai.applicationInfo.packageName)) {
+                            continue;
+                        }
+                        if (!ri.activityInfo.name.equals(ai.name)) {
+                            continue;
+                        }
+
+                        if (removeMatches && allowSetMutation) {
+                            pir.removeFilter(pa);
+                            result.mChanged = true;
+                            if (DEBUG_PREFERRED) {
+                                Slog.v(TAG, "Removing match " + pa.mPref.mComponent);
+                            }
+                            break;
+                        }
+
+                        // Okay we found a previously set preferred or last chosen app.
+                        // If the result set is different from when this
+                        // was created, and is not a subset of the preferred set, we need to
+                        // clear it and re-ask the user their preference, if we're looking for
+                        // an "always" type entry.
+
+                        if (always && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity)) {
+                            if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) {
+                                if (allowSetMutation) {
+                                    // some components of the set are no longer present in
+                                    // the query, but the preferred activity can still be reused
+                                    if (DEBUG_PREFERRED) {
+                                        Slog.i(TAG, "Result set changed, but PreferredActivity"
+                                                + " is still valid as only non-preferred"
+                                                + " components were removed for " + intent
+                                                + " type " + resolvedType);
+                                    }
+                                    // remove obsolete components and re-add the up-to-date
+                                    // filter
+                                    PreferredActivity freshPa = new PreferredActivity(pa,
+                                            pa.mPref.mMatch,
+                                            pa.mPref.discardObsoleteComponents(query),
+                                            pa.mPref.mComponent,
+                                            pa.mPref.mAlways);
+                                    pir.removeFilter(pa);
+                                    pir.addFilter(freshPa);
+                                    result.mChanged = true;
+                                } else {
+                                    if (DEBUG_PREFERRED) {
+                                        Slog.i(TAG, "Do not remove preferred activity");
+                                    }
+                                }
+                            } else {
+                                if (allowSetMutation) {
+                                    Slog.i(TAG,
+                                            "Result set changed, dropping preferred activity "
+                                                    + "for " + intent + " type "
+                                                    + resolvedType);
+                                    if (DEBUG_PREFERRED) {
+                                        Slog.v(TAG,
+                                                "Removing preferred activity since set changed "
+                                                        + pa.mPref.mComponent);
+                                    }
+                                    pir.removeFilter(pa);
+                                    // Re-add the filter as a "last chosen" entry (!always)
+                                    PreferredActivity lastChosen = new PreferredActivity(
+                                            pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
+                                            false);
+                                    pir.addFilter(lastChosen);
+                                    result.mChanged = true;
+                                }
+                                result.mPreferredResolveInfo = null;
+                                return result;
+                            }
+                        }
+
+                        // Yay! Either the set matched or we're looking for the last chosen
+                        if (DEBUG_PREFERRED || debug) {
+                            Slog.v(TAG, "Returning preferred activity: "
+                                    + ri.activityInfo.packageName + "/" + ri.activityInfo.name);
+                        }
+                        result.mPreferredResolveInfo = ri;
+                        return result;
+                    }
+                }
+            }
+            return result;
+        }
+
+        public final FindPreferredActivityBodyResult findPreferredActivityInternal(
+                Intent intent, String resolvedType, int flags,
+                List<ResolveInfo> query, boolean always,
+                boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
+
+            final int callingUid = Binder.getCallingUid();
+            // Do NOT hold the packages lock; this calls up into the settings provider which
+            // could cause a deadlock.
+            final boolean isDeviceProvisioned =
+                    android.provider.Settings.Global.getInt(mContext.getContentResolver(),
+                            android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
+            // Find the preferred activity - the lock is held inside the method.
+            return findPreferredActivityBody(
+                    intent, resolvedType, flags, query, always, removeMatches, debug,
+                    userId, queryMayBeFiltered, callingUid, isDeviceProvisioned);
+        }
+
+        public final ResolveInfo findPersistentPreferredActivityLP(Intent intent,
+                String resolvedType,
+                int flags, List<ResolveInfo> query, boolean debug, int userId) {
+            final int N = query.size();
+            PersistentPreferredIntentResolver ppir =
+                    mSettings.getPersistentPreferredActivities(userId);
+            // Get the list of persistent preferred activities that handle the intent
+            if (DEBUG_PREFERRED || debug) {
+                Slog.v(TAG, "Looking for persistent preferred activities...");
+            }
+            List<PersistentPreferredActivity> pprefs = ppir != null
+                    ? ppir.queryIntent(intent, resolvedType,
+                            (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
+                            userId)
+                    : null;
+            if (pprefs != null && pprefs.size() > 0) {
+                final int M = pprefs.size();
+                for (int i = 0; i < M; i++) {
+                    final PersistentPreferredActivity ppa = pprefs.get(i);
+                    if (DEBUG_PREFERRED || debug) {
+                        Slog.v(TAG, "Checking PersistentPreferredActivity ds="
+                                + (ppa.countDataSchemes() > 0 ? ppa.getDataScheme(0) : "<none>")
+                                + "\n  component=" + ppa.mComponent);
+                        ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
+                    }
+                    final ActivityInfo ai = getActivityInfo(ppa.mComponent,
+                            flags | MATCH_DISABLED_COMPONENTS, userId);
+                    if (DEBUG_PREFERRED || debug) {
+                        Slog.v(TAG, "Found persistent preferred activity:");
+                        if (ai != null) {
+                            ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
+                        } else {
+                            Slog.v(TAG, "  null");
+                        }
+                    }
+                    if (ai == null) {
+                        // This previously registered persistent preferred activity
+                        // component is no longer known. Ignore it and do NOT remove it.
+                        continue;
+                    }
+                    for (int j = 0; j < N; j++) {
+                        final ResolveInfo ri = query.get(j);
+                        if (!ri.activityInfo.applicationInfo.packageName
+                                .equals(ai.applicationInfo.packageName)) {
+                            continue;
+                        }
+                        if (!ri.activityInfo.name.equals(ai.name)) {
+                            continue;
+                        }
+                        //  Found a persistent preference that can handle the intent.
+                        if (DEBUG_PREFERRED || debug) {
+                            Slog.v(TAG, "Returning persistent preferred activity: "
+                                    + ri.activityInfo.packageName + "/" + ri.activityInfo.name);
+                        }
+                        return ri;
+                    }
+                }
+            }
+            return null;
+        }
     }
 
     /**
@@ -4964,6 +5254,16 @@
                 super.dump(type, fd, pw, dumpState);
             }
         }
+        public final FindPreferredActivityBodyResult findPreferredActivityBody(Intent intent,
+                String resolvedType, int flags, List<ResolveInfo> query, boolean always,
+                boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered,
+                int callingUid, boolean isDeviceProvisioned) {
+            synchronized (mLock) {
+                return super.findPreferredActivityBody(intent, resolvedType, flags, query, always,
+                        removeMatches, debug, userId, queryMayBeFiltered, callingUid,
+                        isDeviceProvisioned);
+            }
+        }
     }
 
     /**
@@ -5539,6 +5839,28 @@
                 current.release();
             }
         }
+        public final FindPreferredActivityBodyResult findPreferredActivityInternal(Intent intent,
+                String resolvedType, int flags, List<ResolveInfo> query, boolean always,
+                boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
+            ThreadComputer current = live();
+            try {
+                return current.mComputer.findPreferredActivityInternal(intent, resolvedType, flags,
+                        query, always, removeMatches, debug, userId, queryMayBeFiltered);
+            } finally {
+                current.release();
+            }
+        }
+        public final ResolveInfo findPersistentPreferredActivityLP(Intent intent,
+                String resolvedType, int flags, List<ResolveInfo> query, boolean debug,
+                int userId) {
+            ThreadComputer current = live();
+            try {
+                return current.mComputer.findPersistentPreferredActivityLP(intent, resolvedType,
+                        flags, query, debug, userId);
+            } finally {
+                current.release();
+            }
+        }
     }
 
 
@@ -5562,7 +5884,7 @@
     // If true, the snapshot is corked.  Do not create a new snapshot but use the live
     // computer.  This throttles snapshot creation during periods of churn in Package
     // Manager.
-    private static final AtomicInteger sSnapshotCorked = new AtomicInteger(0);
+    static final AtomicInteger sSnapshotCorked = new AtomicInteger(0);
 
     /**
      * This class records the Computer being used by a thread and the Computer's reference
@@ -5736,665 +6058,8 @@
         onChange(null);
     }
 
-    class PackageHandler extends Handler {
-
-        PackageHandler(Looper looper) {
-            super(looper);
-        }
-
-        public void handleMessage(Message msg) {
-            try {
-                doHandleMessage(msg);
-            } finally {
-                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-            }
-        }
-
-        void doHandleMessage(Message msg) {
-            switch (msg.what) {
-                case INIT_COPY: {
-                    HandlerParams params = (HandlerParams) msg.obj;
-                    if (params != null) {
-                        if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
-                        Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
-                                System.identityHashCode(params));
-                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
-                        params.startCopy();
-                        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-                    }
-                    break;
-                }
-                case SEND_PENDING_BROADCAST: {
-                    String[] packages;
-                    ArrayList<String>[] components;
-                    int size = 0;
-                    int[] uids;
-                    Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
-                    synchronized (mLock) {
-                        size = mPendingBroadcasts.size();
-                        if (size <= 0) {
-                            // Nothing to be done. Just return
-                            return;
-                        }
-                        packages = new String[size];
-                        components = new ArrayList[size];
-                        uids = new int[size];
-                        int i = 0;  // filling out the above arrays
-
-                        for (int n = 0; n < mPendingBroadcasts.userIdCount(); n++) {
-                            final int packageUserId = mPendingBroadcasts.userIdAt(n);
-                            final ArrayMap<String, ArrayList<String>> componentsToBroadcast =
-                                    mPendingBroadcasts.packagesForUserId(packageUserId);
-                            final int numComponents = componentsToBroadcast.size();
-                            for (int index = 0; i < size && index < numComponents; index++) {
-                                packages[i] = componentsToBroadcast.keyAt(index);
-                                components[i] = componentsToBroadcast.valueAt(index);
-                                final PackageSetting ps = mSettings.getPackageLPr(packages[i]);
-                                uids[i] = (ps != null)
-                                        ? UserHandle.getUid(packageUserId, ps.appId)
-                                        : -1;
-                                i++;
-                            }
-                        }
-                        size = i;
-                        mPendingBroadcasts.clear();
-                    }
-                    // Send broadcasts
-                    for (int i = 0; i < size; i++) {
-                        sendPackageChangedBroadcast(packages[i], true /* dontKillApp */,
-                                components[i], uids[i], null /* reason */);
-                    }
-                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                    break;
-                }
-                case POST_INSTALL: {
-                    if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
-
-                    PostInstallData data = mRunningInstalls.get(msg.arg1);
-                    final boolean didRestore = (msg.arg2 != 0);
-                    mRunningInstalls.delete(msg.arg1);
-
-                    if (data != null && data.res.mFreezer != null) {
-                        data.res.mFreezer.close();
-                    }
-
-                    if (data != null && data.mPostInstallRunnable != null) {
-                        data.mPostInstallRunnable.run();
-                    } else if (data != null && data.args != null) {
-                        InstallArgs args = data.args;
-                        PackageInstalledInfo parentRes = data.res;
-
-                        final boolean killApp = (args.mInstallFlags
-                                & PackageManager.INSTALL_DONT_KILL_APP) == 0;
-                        final boolean virtualPreload = ((args.mInstallFlags
-                                & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
-
-                        handlePackagePostInstall(parentRes, killApp, virtualPreload,
-                                didRestore, args.mInstallSource.installerPackageName,
-                                args.mObserver, args.mDataLoaderType);
-
-                        // Log tracing if needed
-                        if (args.mTraceMethod != null) {
-                            Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.mTraceMethod,
-                                    args.mTraceCookie);
-                        }
-                    } else if (DEBUG_INSTALL) {
-                        // No post-install when we run restore from installExistingPackageForUser
-                        Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
-                    }
-
-                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
-                } break;
-                case DEFERRED_NO_KILL_POST_DELETE: {
-                    synchronized (mInstallLock) {
-                        InstallArgs args = (InstallArgs) msg.obj;
-                        if (args != null) {
-                            args.doPostDeleteLI(true);
-                        }
-                    }
-                } break;
-                case DEFERRED_NO_KILL_INSTALL_OBSERVER: {
-                    String packageName = (String) msg.obj;
-                    if (packageName != null) {
-                        notifyInstallObserver(packageName);
-                    }
-                } break;
-                case WRITE_SETTINGS: {
-                    Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
-                    synchronized (mLock) {
-                        removeMessages(WRITE_SETTINGS);
-                        removeMessages(WRITE_PACKAGE_RESTRICTIONS);
-                        writeSettingsLPrTEMP();
-                        mDirtyUsers.clear();
-                    }
-                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                } break;
-                case WRITE_PACKAGE_RESTRICTIONS: {
-                    Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
-                    synchronized (mLock) {
-                        removeMessages(WRITE_PACKAGE_RESTRICTIONS);
-                        for (int userId : mDirtyUsers) {
-                            mSettings.writePackageRestrictionsLPr(userId);
-                        }
-                        mDirtyUsers.clear();
-                    }
-                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                } break;
-                case WRITE_PACKAGE_LIST: {
-                    Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
-                    synchronized (mLock) {
-                        removeMessages(WRITE_PACKAGE_LIST);
-                        mSettings.writePackageListLPr(msg.arg1);
-                    }
-                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                } break;
-                case CHECK_PENDING_VERIFICATION: {
-                    final int verificationId = msg.arg1;
-                    final PackageVerificationState state = mPendingVerification.get(verificationId);
-
-                    if ((state != null) && !state.isVerificationComplete()
-                            && !state.timeoutExtended()) {
-                        final VerificationParams params = state.getVerificationParams();
-                        final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
-
-                        String errorMsg = "Verification timed out for " + originUri;
-                        Slog.i(TAG, errorMsg);
-
-                        final UserHandle user = params.getUser();
-                        if (getDefaultVerificationResponse(user)
-                                == PackageManager.VERIFICATION_ALLOW) {
-                            Slog.i(TAG, "Continuing with installation of " + originUri);
-                            state.setVerifierResponse(Binder.getCallingUid(),
-                                    PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
-                            broadcastPackageVerified(verificationId, originUri,
-                                    PackageManager.VERIFICATION_ALLOW, null, params.mDataLoaderType,
-                                    user);
-                        } else {
-                            broadcastPackageVerified(verificationId, originUri,
-                                    PackageManager.VERIFICATION_REJECT, null,
-                                    params.mDataLoaderType, user);
-                            params.setReturnCode(
-                                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
-                            state.setVerifierResponse(Binder.getCallingUid(),
-                                    PackageManager.VERIFICATION_REJECT);
-                        }
-
-                        if (state.areAllVerificationsComplete()) {
-                            mPendingVerification.remove(verificationId);
-                        }
-
-                        Trace.asyncTraceEnd(
-                                TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
-
-                        params.handleVerificationFinished();
-
-                    }
-                    break;
-                }
-                case CHECK_PENDING_INTEGRITY_VERIFICATION: {
-                    final int verificationId = msg.arg1;
-                    final PackageVerificationState state = mPendingVerification.get(verificationId);
-
-                    if (state != null && !state.isIntegrityVerificationComplete()) {
-                        final VerificationParams params = state.getVerificationParams();
-                        final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
-
-                        String errorMsg = "Integrity verification timed out for " + originUri;
-                        Slog.i(TAG, errorMsg);
-
-                        state.setIntegrityVerificationResult(
-                                getDefaultIntegrityVerificationResponse());
-
-                        if (getDefaultIntegrityVerificationResponse()
-                                == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
-                            Slog.i(TAG, "Integrity check times out, continuing with " + originUri);
-                        } else {
-                            params.setReturnCode(
-                                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
-                                    errorMsg);
-                        }
-
-                        if (state.areAllVerificationsComplete()) {
-                            mPendingVerification.remove(verificationId);
-                        }
-
-                        Trace.asyncTraceEnd(
-                                TRACE_TAG_PACKAGE_MANAGER,
-                                "integrity_verification",
-                                verificationId);
-
-                        params.handleIntegrityVerificationFinished();
-                    }
-                    break;
-                }
-                case PACKAGE_VERIFIED: {
-                    final int verificationId = msg.arg1;
-
-                    final PackageVerificationState state = mPendingVerification.get(verificationId);
-                    if (state == null) {
-                        Slog.w(TAG, "Verification with id " + verificationId
-                                + " not found."
-                                + " It may be invalid or overridden by integrity verification");
-                        break;
-                    }
-
-                    final PackageVerificationResponse response = (PackageVerificationResponse) msg.obj;
-
-                    state.setVerifierResponse(response.callerUid, response.code);
-
-                    if (state.isVerificationComplete()) {
-                        final VerificationParams params = state.getVerificationParams();
-                        final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
-
-                        if (state.isInstallAllowed()) {
-                            broadcastPackageVerified(verificationId, originUri,
-                                    response.code, null, params.mDataLoaderType, params.getUser());
-                        } else {
-                            params.setReturnCode(
-                                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
-                                    "Install not allowed");
-                        }
-
-                        if (state.areAllVerificationsComplete()) {
-                            mPendingVerification.remove(verificationId);
-                        }
-
-                        Trace.asyncTraceEnd(
-                                TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
-
-                        params.handleVerificationFinished();
-                    }
-
-                    break;
-                }
-                case INTEGRITY_VERIFICATION_COMPLETE: {
-                    final int verificationId = msg.arg1;
-
-                    final PackageVerificationState state = mPendingVerification.get(verificationId);
-                    if (state == null) {
-                        Slog.w(TAG, "Integrity verification with id " + verificationId
-                                + " not found. It may be invalid or overridden by verifier");
-                        break;
-                    }
-
-                    final int response = (Integer) msg.obj;
-                    final VerificationParams params = state.getVerificationParams();
-                    final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
-
-                    state.setIntegrityVerificationResult(response);
-
-                    if (response == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
-                        Slog.i(TAG, "Integrity check passed for " + originUri);
-                    } else {
-                        params.setReturnCode(
-                                PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
-                                "Integrity check failed for " + originUri);
-                    }
-
-                    if (state.areAllVerificationsComplete()) {
-                        mPendingVerification.remove(verificationId);
-                    }
-
-                    Trace.asyncTraceEnd(
-                            TRACE_TAG_PACKAGE_MANAGER,
-                            "integrity_verification",
-                            verificationId);
-
-                    params.handleIntegrityVerificationFinished();
-                    break;
-                }
-                case INSTANT_APP_RESOLUTION_PHASE_TWO: {
-                    InstantAppResolver.doInstantAppResolutionPhaseTwo(mContext,
-                            mInstantAppResolverConnection,
-                            (InstantAppRequest) msg.obj,
-                            mInstantAppInstallerActivity,
-                            mHandler);
-                    break;
-                }
-                case ENABLE_ROLLBACK_STATUS: {
-                    final int enableRollbackToken = msg.arg1;
-                    final int enableRollbackCode = msg.arg2;
-                    final VerificationParams params =
-                            mPendingEnableRollback.get(enableRollbackToken);
-                    if (params == null) {
-                        Slog.w(TAG, "Invalid rollback enabled token "
-                                + enableRollbackToken + " received");
-                        break;
-                    }
-
-                    mPendingEnableRollback.remove(enableRollbackToken);
-
-                    if (enableRollbackCode != PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED) {
-                        final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
-                        Slog.w(TAG, "Failed to enable rollback for " + originUri);
-                        Slog.w(TAG, "Continuing with installation of " + originUri);
-                    }
-
-                    Trace.asyncTraceEnd(
-                            TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
-
-                    params.handleRollbackEnabled();
-                    break;
-                }
-                case ENABLE_ROLLBACK_TIMEOUT: {
-                    final int enableRollbackToken = msg.arg1;
-                    final int sessionId = msg.arg2;
-                    final VerificationParams params =
-                            mPendingEnableRollback.get(enableRollbackToken);
-                    if (params != null) {
-                        final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
-
-                        Slog.w(TAG, "Enable rollback timed out for " + originUri);
-                        mPendingEnableRollback.remove(enableRollbackToken);
-
-                        Slog.w(TAG, "Continuing with installation of " + originUri);
-                        Trace.asyncTraceEnd(
-                                TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
-                        params.handleRollbackEnabled();
-                        Intent rollbackTimeoutIntent = new Intent(
-                                Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
-                        rollbackTimeoutIntent.putExtra(
-                                PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
-                                sessionId);
-                        rollbackTimeoutIntent.addFlags(
-                                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                        mContext.sendBroadcastAsUser(rollbackTimeoutIntent, UserHandle.SYSTEM,
-                                android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
-                    }
-                    break;
-                }
-                case DOMAIN_VERIFICATION: {
-                    int messageCode = msg.arg1;
-                    Object object = msg.obj;
-                    mDomainVerificationManager.runMessage(messageCode, object);
-                    break;
-                }
-                case SNAPSHOT_UNCORK: {
-                    int corking = sSnapshotCorked.decrementAndGet();
-                    if (TRACE_SNAPSHOTS && corking == 0) {
-                        Log.e(TAG, "snapshot: corking goes to zero in message handler");
-                    }
-                    break;
-                }
-            }
-        }
-    }
-
-    private void handlePackagePostInstall(PackageInstalledInfo res, boolean killApp,
-            boolean virtualPreload, boolean launchedForRestore, String installerPackage,
-            IPackageInstallObserver2 installObserver, int dataLoaderType) {
-        boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
-        final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
-        final String packageName = res.mName;
-        final PackageSetting pkgSetting = succeeded ? getPackageSetting(packageName) : null;
-        final boolean removedBeforeUpdate = (pkgSetting == null)
-                || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals(
-                        res.mPkg.getPath()));
-        if (succeeded && removedBeforeUpdate) {
-            Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
-                    + "could be executed");
-            res.mReturnCode = INSTALL_FAILED_PACKAGE_CHANGED;
-            res.mReturnMsg = "Package was removed before install could complete.";
-
-            // Remove the update failed package's older resources safely now
-            InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
-            if (args != null) {
-                synchronized (mInstallLock) {
-                    args.doPostDeleteLI(true);
-                }
-            }
-            notifyInstallObserver(res, installObserver);
-            return;
-        }
-
-        if (succeeded) {
-            // Clear the uid cache after we installed a new package.
-            mPerUidReadTimeoutsCache = null;
-
-            // Send the removed broadcasts
-            if (res.mRemovedInfo != null) {
-                res.mRemovedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
-            }
-
-            final String installerPackageName =
-                    res.mInstallerPackageName != null
-                            ? res.mInstallerPackageName
-                            : res.mRemovedInfo != null
-                                    ? res.mRemovedInfo.mInstallerPackageName
-                                    : null;
-
-            synchronized (mLock) {
-                mInstantAppRegistry.onPackageInstalledLPw(res.mPkg, res.mNewUsers);
-            }
-
-            // Determine the set of users who are adding this package for
-            // the first time vs. those who are seeing an update.
-            int[] firstUserIds = EMPTY_INT_ARRAY;
-            int[] firstInstantUserIds = EMPTY_INT_ARRAY;
-            int[] updateUserIds = EMPTY_INT_ARRAY;
-            int[] instantUserIds = EMPTY_INT_ARRAY;
-            final boolean allNewUsers = res.mOrigUsers == null || res.mOrigUsers.length == 0;
-            for (int newUser : res.mNewUsers) {
-                final boolean isInstantApp = pkgSetting.getInstantApp(newUser);
-                if (allNewUsers) {
-                    if (isInstantApp) {
-                        firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
-                    } else {
-                        firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
-                    }
-                    continue;
-                }
-                boolean isNew = true;
-                for (int origUser : res.mOrigUsers) {
-                    if (origUser == newUser) {
-                        isNew = false;
-                        break;
-                    }
-                }
-                if (isNew) {
-                    if (isInstantApp) {
-                        firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
-                    } else {
-                        firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
-                    }
-                } else {
-                    if (isInstantApp) {
-                        instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);
-                    } else {
-                        updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);
-                    }
-                }
-            }
-
-            // Send installed broadcasts if the package is not a static shared lib.
-            if (res.mPkg.getStaticSharedLibName() == null) {
-                mProcessLoggingHandler.invalidateBaseApkHash(res.mPkg.getBaseApkPath());
-
-                // Send added for users that see the package for the first time
-                // sendPackageAddedForNewUsers also deals with system apps
-                int appId = UserHandle.getAppId(res.mUid);
-                boolean isSystem = res.mPkg.isSystem();
-                sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
-                        virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds,
-                        dataLoaderType);
-
-                // Send added for users that don't see the package for the first time
-                Bundle extras = new Bundle(1);
-                extras.putInt(Intent.EXTRA_UID, res.mUid);
-                if (update) {
-                    extras.putBoolean(Intent.EXTRA_REPLACING, true);
-                }
-                extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
-                // Send to all running apps.
-                final SparseArray<int[]> newBroadcastAllowList;
-
-                synchronized (mLock) {
-                    newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
-                            getPackageSettingInternal(res.mName, Process.SYSTEM_UID),
-                            updateUserIds, mSettings.getPackagesLocked());
-                }
-                sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                        extras, 0 /*flags*/,
-                        null /*targetPackage*/, null /*finishedReceiver*/,
-                        updateUserIds, instantUserIds, newBroadcastAllowList, null);
-                if (installerPackageName != null) {
-                    // Send to the installer, even if it's not running.
-                    sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                            extras, 0 /*flags*/,
-                            installerPackageName, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
-                }
-                // if the required verifier is defined, but, is not the installer of record
-                // for the package, it gets notified
-                final boolean notifyVerifier = mRequiredVerifierPackage != null
-                        && !mRequiredVerifierPackage.equals(installerPackageName);
-                if (notifyVerifier) {
-                    sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                            extras, 0 /*flags*/,
-                            mRequiredVerifierPackage, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
-                }
-                // If package installer is defined, notify package installer about new
-                // app installed
-                if (mRequiredInstallerPackage != null) {
-                    sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                            extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
-                            mRequiredInstallerPackage, null /*finishedReceiver*/,
-                            firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
-                }
-
-                // Send replaced for users that don't see the package for the first time
-                if (update) {
-                    sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
-                            packageName, extras, 0 /*flags*/,
-                            null /*targetPackage*/, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
-                            null);
-                    if (installerPackageName != null) {
-                        sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
-                                extras, 0 /*flags*/,
-                                installerPackageName, null /*finishedReceiver*/,
-                                updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
-                    }
-                    if (notifyVerifier) {
-                        sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
-                                extras, 0 /*flags*/,
-                                mRequiredVerifierPackage, null /*finishedReceiver*/,
-                                updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
-                    }
-                    sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
-                            null /*package*/, null /*extras*/, 0 /*flags*/,
-                            packageName /*targetPackage*/,
-                            null /*finishedReceiver*/, updateUserIds, instantUserIds,
-                            null /*broadcastAllowList*/,
-                            getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED)
-                                    .toBundle());
-                } else if (launchedForRestore && !res.mPkg.isSystem()) {
-                    // First-install and we did a restore, so we're responsible for the
-                    // first-launch broadcast.
-                    if (DEBUG_BACKUP) {
-                        Slog.i(TAG, "Post-restore of " + packageName
-                                + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
-                    }
-                    sendFirstLaunchBroadcast(packageName, installerPackage,
-                            firstUserIds, firstInstantUserIds);
-                }
-
-                // Send broadcast package appeared if external for all users
-                if (res.mPkg.isExternalStorage()) {
-                    if (!update) {
-                        final StorageManager storage = mInjector.getSystemService(
-                                StorageManager.class);
-                        VolumeInfo volume =
-                                storage.findVolumeByUuid(
-                                        res.mPkg.getStorageUuid().toString());
-                        int packageExternalStorageType =
-                                getPackageExternalStorageType(volume, res.mPkg.isExternalStorage());
-                        // If the package was installed externally, log it.
-                        if (packageExternalStorageType != StorageEnums.UNKNOWN) {
-                            FrameworkStatsLog.write(
-                                    FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
-                                    packageExternalStorageType, packageName);
-                        }
-                    }
-                    if (DEBUG_INSTALL) {
-                        Slog.i(TAG, "upgrading pkg " + res.mPkg + " is external");
-                    }
-                    final int[] uidArray = new int[]{res.mPkg.getUid()};
-                    ArrayList<String> pkgList = new ArrayList<>(1);
-                    pkgList.add(packageName);
-                    sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
-                }
-            } else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib
-                for (int i = 0; i < res.mLibraryConsumers.size(); i++) {
-                    AndroidPackage pkg = res.mLibraryConsumers.get(i);
-                    // send broadcast that all consumers of the static shared library have changed
-                    sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */,
-                            new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
-                            pkg.getUid(), null);
-                }
-            }
-
-            // Work that needs to happen on first install within each user
-            if (firstUserIds != null && firstUserIds.length > 0) {
-                for (int userId : firstUserIds) {
-                    restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
-                            userId);
-                }
-            }
-
-            if (allNewUsers && !update) {
-                notifyPackageAdded(packageName, res.mUid);
-            } else {
-                notifyPackageChanged(packageName, res.mUid);
-            }
-
-            // Log current value of "unknown sources" setting
-            EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
-                    getUnknownSourcesSettings());
-
-            // Remove the replaced package's older resources safely now
-            InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
-            if (args != null) {
-                if (!killApp) {
-                    // If we didn't kill the app, defer the deletion of code/resource files, since
-                    // they may still be in use by the running application. This mitigates problems
-                    // in cases where resources or code is loaded by a new Activity before
-                    // ApplicationInfo changes have propagated to all application threads.
-                    scheduleDeferredNoKillPostDelete(args);
-                } else {
-                    synchronized (mInstallLock) {
-                        args.doPostDeleteLI(true);
-                    }
-                }
-            } else {
-                // Force a gc to clear up things. Ask for a background one, it's fine to go on
-                // and not block here.
-                VMRuntime.getRuntime().requestConcurrentGC();
-            }
 
-            // Notify DexManager that the package was installed for new users.
-            // The updated users should already be indexed and the package code paths
-            // should not change.
-            // Don't notify the manager for ephemeral apps as they are not expected to
-            // survive long enough to benefit of background optimizations.
-            for (int userId : firstUserIds) {
-                PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
-                // There's a race currently where some install events may interleave with an
-                // uninstall. This can lead to package info being null (b/36642664).
-                if (info != null) {
-                    mDexManager.notifyPackageInstalled(info, userId);
-                }
-            }
-        }
 
-        final boolean deferInstallObserver = succeeded && update && !killApp;
-        if (deferInstallObserver) {
-            scheduleDeferredNoKillInstallObserver(res, installObserver);
-        } else {
-            notifyInstallObserver(res, installObserver);
-        }
-    }
 
     @Override
     public void notifyPackagesReplacedReceived(String[] packages) {
@@ -6435,12 +6100,12 @@
         }
     }
 
-    private void scheduleDeferredNoKillPostDelete(InstallArgs args) {
+    void scheduleDeferredNoKillPostDelete(InstallArgs args) {
         Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
         mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS);
     }
 
-    private void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info,
+    void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info,
             IPackageInstallObserver2 observer) {
         String packageName = info.mPkg.getPackageName();
         mNoKillInstallObservers.put(packageName, Pair.create(info, observer));
@@ -6528,7 +6193,7 @@
      * corresponding {@link StorageEnums} storage type value if it is.
      * corresponding {@link StorageEnums} storage type value if it is.
      */
-    private static int getPackageExternalStorageType(VolumeInfo packageVolume,
+    static int getPackageExternalStorageType(VolumeInfo packageVolume,
             boolean packageIsExternal) {
         if (packageVolume != null) {
             DiskInfo disk = packageVolume.getDisk();
@@ -6723,7 +6388,7 @@
                     HandlerThread thread = new ServiceThread(TAG,
                             Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
                     thread.start();
-                    return pm.new PackageHandler(thread.getLooper());
+                    return new PackageHandler(thread.getLooper(), pm);
                 },
                 new DefaultSystemWrapper(),
                 LocalServices::getService,
@@ -6829,53 +6494,6 @@
         }
     }
 
-    @VisibleForTesting
-    public static class ScanPartition extends SystemPartition {
-        @ScanFlags
-        public final int scanFlag;
-
-        public ScanPartition(@NonNull SystemPartition partition) {
-            super(partition);
-            scanFlag = scanFlagForPartition(partition);
-        }
-
-        /**
-         * Creates a partition containing the same folders as the original partition but with a
-         * different root folder. The new partition will include the scan flags of the original
-         * partition along with any specified additional scan flags.
-         */
-        public ScanPartition(@NonNull File folder, @NonNull ScanPartition original,
-                @ScanFlags int additionalScanFlag) {
-            super(folder, original);
-            this.scanFlag = original.scanFlag | additionalScanFlag;
-        }
-
-        private static int scanFlagForPartition(PackagePartitions.SystemPartition partition) {
-            switch (partition.type) {
-                case PackagePartitions.PARTITION_SYSTEM:
-                    return 0;
-                case PackagePartitions.PARTITION_VENDOR:
-                    return SCAN_AS_VENDOR;
-                case PackagePartitions.PARTITION_ODM:
-                    return SCAN_AS_ODM;
-                case PackagePartitions.PARTITION_OEM:
-                    return SCAN_AS_OEM;
-                case PackagePartitions.PARTITION_PRODUCT:
-                    return SCAN_AS_PRODUCT;
-                case PackagePartitions.PARTITION_SYSTEM_EXT:
-                    return SCAN_AS_SYSTEM_EXT;
-                default:
-                    throw new IllegalStateException("Unable to determine scan flag for "
-                            + partition.getFolder());
-            }
-        }
-
-        @Override
-        public String toString() {
-            return getFolder().getAbsolutePath() + ":" + scanFlag;
-        }
-    }
-
     // Link watchables to the class
     private void registerObserver() {
         mPackages.registerObserver(mWatcher);
@@ -7215,7 +6833,6 @@
                 Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
             }
 
-            File frameworkDir = new File(Environment.getRootDirectory(), "framework");
 
             final VersionInfo ver = mSettings.getInternalVersion();
             mIsUpgrade =
@@ -7267,286 +6884,23 @@
             // Prepare apex package info before scanning APKs, these information are needed when
             // scanning apk in apex.
             mApexManager.scanApexPackagesTraced(packageParser, executorService);
-            // Collect vendor/product/system_ext overlay packages. (Do this before scanning
-            // any apps.)
-            // For security and version matching reason, only consider overlay packages if they
-            // reside in the right directory.
-            for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
-                final ScanPartition partition = mDirsToScanAsSystem.get(i);
-                if (partition.getOverlayFolder() == null) {
-                    continue;
-                }
-                scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
-                        systemScanFlags | partition.scanFlag, 0,
-                        packageParser, executorService);
-            }
 
-            scanDirTracedLI(frameworkDir, systemParseFlags,
-                    systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
-                    packageParser, executorService);
-            if (!mPackages.containsKey("android")) {
-                throw new IllegalStateException(
-                        "Failed to load frameworks package; check log for warnings");
-            }
-            for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
-                final ScanPartition partition = mDirsToScanAsSystem.get(i);
-                if (partition.getPrivAppFolder() != null) {
-                    scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
-                            systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
-                            packageParser, executorService);
-                }
-                scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
-                        systemScanFlags | partition.scanFlag, 0,
-                        packageParser, executorService);
-            }
-
+            final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
+            helper.scanSystemDirs(mDirsToScanAsSystem, mIsUpgrade,
+                    packageParser, executorService, mPlatformPackage, mIsPreNMR1Upgrade,
+                    systemParseFlags, systemScanFlags);
             // Parse overlay configuration files to set default enable state, mutability, and
             // priority of system overlays.
             mOverlayConfig = OverlayConfig.initializeSystemInstance(
                     consumer -> mPmInternal.forEachPackage(
                             pkg -> consumer.accept(pkg, pkg.isSystem())));
-
-            // Prune any system packages that no longer exist.
-            final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
-            // Stub packages must either be replaced with full versions in the /data
-            // partition or be disabled.
-            final List<String> stubSystemApps = new ArrayList<>();
             final int[] userIds = mUserManager.getUserIds();
-            if (!mOnlyCore) {
-                // do this first before mucking with mPackages for the "expecting better" case
-                final int numPackages = mPackages.size();
-                for (int index = 0; index < numPackages; index++) {
-                    final AndroidPackage pkg = mPackages.valueAt(index);
-                    if (pkg.isStub()) {
-                        stubSystemApps.add(pkg.getPackageName());
-                    }
-                }
-
-                // Iterates PackageSettings in reversed order because the item could be removed
-                // during the iteration.
-                for (int index = packageSettings.size() - 1; index >= 0; index--) {
-                    final PackageSetting ps = packageSettings.valueAt(index);
-
-                    /*
-                     * If this is not a system app, it can't be a
-                     * disable system app.
-                     */
-                    if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                        continue;
-                    }
-
-                    /*
-                     * If the package is scanned, it's not erased.
-                     */
-                    final AndroidPackage scannedPkg = mPackages.get(ps.name);
-                    if (scannedPkg != null) {
-                        /*
-                         * If the system app is both scanned and in the
-                         * disabled packages list, then it must have been
-                         * added via OTA. Remove it from the currently
-                         * scanned package so the previously user-installed
-                         * application can be scanned.
-                         */
-                        if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
-                            logCriticalInfo(Log.WARN,
-                                    "Expecting better updated system app for " + ps.name
-                                    + "; removing system app.  Last known"
-                                    + " codePath=" + ps.getPathString()
-                                    + ", versionCode=" + ps.versionCode
-                                    + "; scanned versionCode=" + scannedPkg.getLongVersionCode());
-                            removePackageLI(scannedPkg, true);
-                            mExpectingBetter.put(ps.name, ps.getPath());
-                        }
-
-                        continue;
-                    }
-
-                    if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
-                        logCriticalInfo(Log.WARN, "System package " + ps.name
-                                + " no longer exists; its data will be wiped");
-                        removePackageDataLIF(ps, userIds, null, 0, false);
-                    } else {
-                        // we still have a disabled system package, but, it still might have
-                        // been removed. check the code path still exists and check there's
-                        // still a package. the latter can happen if an OTA keeps the same
-                        // code path, but, changes the package name.
-                        final PackageSetting disabledPs =
-                                mSettings.getDisabledSystemPkgLPr(ps.name);
-                        if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
-                                || disabledPs.pkg == null) {
-                            possiblyDeletedUpdatedSystemApps.add(ps.name);
-                        } else {
-                            // We're expecting that the system app should remain disabled, but add
-                            // it to expecting better to recover in case the data version cannot
-                            // be scanned.
-                            mExpectingBetter.put(disabledPs.name, disabledPs.getPath());
-                        }
-                    }
-                }
-            }
-
-            final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
-
-            // Remove any shared userIDs that have no associated packages
-            mSettings.pruneSharedUsersLPw();
-            final long systemScanTime = SystemClock.uptimeMillis() - startTime;
-            final int systemPackagesCount = mPackages.size();
-            Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
-                    + " ms, packageCount: " + systemPackagesCount
-                    + " , timePerPackage: "
-                    + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
-                    + " , cached: " + cachedSystemApps);
-            if (mIsUpgrade && systemPackagesCount > 0) {
-                //CHECKSTYLE:OFF IndentationCheck
-                FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
-                    BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
-                    systemScanTime / systemPackagesCount);
-                //CHECKSTYLE:ON IndentationCheck
-            }
-            if (!mOnlyCore) {
-                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
-                        SystemClock.uptimeMillis());
-                scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
-                        packageParser, executorService);
-
-            }
-
+            helper.cleanupSystemPackagesAndInstallStubs(mDirsToScanAsSystem,
+                    mIsUpgrade, packageParser, executorService, mOnlyCore, packageSettings,
+                    startTime, mAppInstallDir, mPlatformPackage, mIsPreNMR1Upgrade,
+                    scanFlags, systemParseFlags, systemScanFlags, userIds);
             packageParser.close();
 
-            List<Runnable> unfinishedTasks = executorService.shutdownNow();
-            if (!unfinishedTasks.isEmpty()) {
-                throw new IllegalStateException("Not all tasks finished before calling close: "
-                        + unfinishedTasks);
-            }
-
-            if (!mOnlyCore) {
-                // Remove disable package settings for updated system apps that were
-                // removed via an OTA. If the update is no longer present, remove the
-                // app completely. Otherwise, revoke their system privileges.
-                for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
-                    final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
-                    final AndroidPackage pkg = mPackages.get(packageName);
-                    final String msg;
-
-                    // remove from the disabled system list; do this first so any future
-                    // scans of this package are performed without this state
-                    mSettings.removeDisabledSystemPackageLPw(packageName);
-
-                    if (pkg == null) {
-                        // should have found an update, but, we didn't; remove everything
-                        msg = "Updated system package " + packageName
-                                + " no longer exists; removing its data";
-                        // Actual deletion of code and data will be handled by later
-                        // reconciliation step
-                    } else {
-                        // found an update; revoke system privileges
-                        msg = "Updated system package " + packageName
-                                + " no longer exists; rescanning package on data";
-
-                        // NOTE: We don't do anything special if a stub is removed from the
-                        // system image. But, if we were [like removing the uncompressed
-                        // version from the /data partition], this is where it'd be done.
-
-                        // remove the package from the system and re-scan it without any
-                        // special privileges
-                        removePackageLI(pkg, true);
-                        try {
-                            final File codePath = new File(pkg.getPath());
-                            scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
-                        } catch (PackageManagerException e) {
-                            Slog.e(TAG, "Failed to parse updated, ex-system package: "
-                                    + e.getMessage());
-                        }
-                    }
-
-                    // one final check. if we still have a package setting [ie. it was
-                    // previously scanned and known to the system], but, we don't have
-                    // a package [ie. there was an error scanning it from the /data
-                    // partition], completely remove the package data.
-                    final PackageSetting ps = mSettings.getPackageLPr(packageName);
-                    if (ps != null && mPackages.get(packageName) == null) {
-                        removePackageDataLIF(ps, userIds, null, 0, false);
-
-                    }
-                    logCriticalInfo(Log.WARN, msg);
-                }
-
-                /*
-                 * Make sure all system apps that we expected to appear on
-                 * the userdata partition actually showed up. If they never
-                 * appeared, crawl back and revive the system version.
-                 */
-                for (int i = 0; i < mExpectingBetter.size(); i++) {
-                    final String packageName = mExpectingBetter.keyAt(i);
-                    if (!mPackages.containsKey(packageName)) {
-                        final File scanFile = mExpectingBetter.valueAt(i);
-
-                        logCriticalInfo(Log.WARN, "Expected better " + packageName
-                                + " but never showed up; reverting to system");
-
-                        @ParseFlags int reparseFlags = 0;
-                        @ScanFlags int rescanFlags = 0;
-                        for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
-                            final ScanPartition partition = mDirsToScanAsSystem.get(i1);
-                            if (partition.containsPrivApp(scanFile)) {
-                                reparseFlags = systemParseFlags;
-                                rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
-                                        | partition.scanFlag;
-                                break;
-                            }
-                            if (partition.containsApp(scanFile)) {
-                                reparseFlags = systemParseFlags;
-                                rescanFlags = systemScanFlags | partition.scanFlag;
-                                break;
-                            }
-                        }
-                        if (rescanFlags == 0) {
-                            Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
-                            continue;
-                        }
-                        mSettings.enableSystemPackageLPw(packageName);
-
-                        try {
-                            final AndroidPackage newPkg = scanPackageTracedLI(
-                                    scanFile, reparseFlags, rescanFlags, 0, null);
-                            // We rescanned a stub, add it to the list of stubbed system packages
-                            if (newPkg.isStub()) {
-                                stubSystemApps.add(packageName);
-                            }
-                        } catch (PackageManagerException e) {
-                            Slog.e(TAG, "Failed to parse original system package: "
-                                    + e.getMessage());
-                        }
-                    }
-                }
-
-                // Uncompress and install any stubbed system applications.
-                // This must be done last to ensure all stubs are replaced or disabled.
-                installSystemStubPackages(stubSystemApps, scanFlags);
-
-                final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
-                                - cachedSystemApps;
-
-                final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
-                final int dataPackagesCount = mPackages.size() - systemPackagesCount;
-                Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
-                        + " ms, packageCount: " + dataPackagesCount
-                        + " , timePerPackage: "
-                        + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
-                        + " , cached: " + cachedNonSystemApps);
-                if (mIsUpgrade && dataPackagesCount > 0) {
-                    //CHECKSTYLE:OFF IndentationCheck
-                    FrameworkStatsLog.write(
-                        FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
-                        BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
-                        dataScanTime / dataPackagesCount);
-                    //CHECKSTYLE:OFF IndentationCheck
-                }
-            }
-            mExpectingBetter.clear();
-
-            mSettings.pruneRenamedPackagesLPw();
-
             // Resolve the storage manager.
             mStorageManagerPackage = getStorageManagerPackageName();
 
@@ -7840,234 +7194,10 @@
         Slog.i(TAG, "Fix for b/169414761 is applied");
     }
 
-    /**
-     * Uncompress and install stub applications.
-     * <p>In order to save space on the system partition, some applications are shipped in a
-     * compressed form. In addition the compressed bits for the full application, the
-     * system image contains a tiny stub comprised of only the Android manifest.
-     * <p>During the first boot, attempt to uncompress and install the full application. If
-     * the application can't be installed for any reason, disable the stub and prevent
-     * uncompressing the full application during future boots.
-     * <p>In order to forcefully attempt an installation of a full application, go to app
-     * settings and enable the application.
-     */
-    private void installSystemStubPackages(@NonNull List<String> systemStubPackageNames,
-            @ScanFlags int scanFlags) {
-        for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
-            final String packageName = systemStubPackageNames.get(i);
-            // skip if the system package is already disabled
-            if (mSettings.isDisabledSystemPackageLPr(packageName)) {
-                systemStubPackageNames.remove(i);
-                continue;
-            }
-            // skip if the package isn't installed (?!); this should never happen
-            final AndroidPackage pkg = mPackages.get(packageName);
-            if (pkg == null) {
-                systemStubPackageNames.remove(i);
-                continue;
-            }
-            // skip if the package has been disabled by the user
-            final PackageSetting ps = mSettings.getPackageLPr(packageName);
-            if (ps != null) {
-                final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
-                if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
-                    systemStubPackageNames.remove(i);
-                    continue;
-                }
-            }
 
-            // install the package to replace the stub on /system
-            try {
-                installStubPackageLI(pkg, 0, scanFlags);
-                ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
-                        UserHandle.USER_SYSTEM, "android");
-                systemStubPackageNames.remove(i);
-            } catch (PackageManagerException e) {
-                Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
-            }
 
-            // any failed attempt to install the package will be cleaned up later
-        }
 
-        // disable any stub still left; these failed to install the full application
-        for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
-            final String pkgName = systemStubPackageNames.get(i);
-            final PackageSetting ps = mSettings.getPackageLPr(pkgName);
-            ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                    UserHandle.USER_SYSTEM, "android");
-            logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
-        }
-    }
 
-    /**
-     * Extract, install and enable a stub package.
-     * <p>If the compressed file can not be extracted / installed for any reason, the stub
-     * APK will be installed and the package will be disabled. To recover from this situation,
-     * the user will need to go into system settings and re-enable the package.
-     */
-    private boolean enableCompressedPackage(AndroidPackage stubPkg,
-            @NonNull PackageSetting stubPkgSetting) {
-        final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
-                | ParsingPackageUtils.PARSE_ENFORCE_CODE;
-        synchronized (mInstallLock) {
-            final AndroidPackage pkg;
-            try (PackageFreezer freezer =
-                    freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
-                pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
-                synchronized (mLock) {
-                    prepareAppDataAfterInstallLIF(pkg);
-                    try {
-                        updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null,
-                                Collections.unmodifiableMap(mPackages));
-                    } catch (PackageManagerException e) {
-                        Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e);
-                    }
-                    mPermissionManager.onPackageInstalled(pkg,
-                            PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
-                            UserHandle.USER_ALL);
-                    writeSettingsLPrTEMP();
-                }
-            } catch (PackageManagerException e) {
-                // Whoops! Something went very wrong; roll back to the stub and disable the package
-                try (PackageFreezer freezer =
-                        freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
-                    synchronized (mLock) {
-                        // NOTE: Ensure the system package is enabled; even for a compressed stub.
-                        // If we don't, installing the system package fails during scan
-                        enableSystemPackageLPw(stubPkg);
-                    }
-                    installPackageFromSystemLIF(stubPkg.getPath(),
-                            mUserManager.getUserIds() /*allUserHandles*/, null /*origUserHandles*/,
-                            true /*writeSettings*/);
-                } catch (PackageManagerException pme) {
-                    // Serious WTF; we have to be able to install the stub
-                    Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
-                            pme);
-                } finally {
-                    // Disable the package; the stub by itself is not runnable
-                    synchronized (mLock) {
-                        final PackageSetting stubPs = mSettings.getPackageLPr(
-                                stubPkg.getPackageName());
-                        if (stubPs != null) {
-                            stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
-                                    UserHandle.USER_SYSTEM, "android");
-                        }
-                        writeSettingsLPrTEMP();
-                    }
-                }
-                return false;
-            }
-            clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
-                    | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-            mDexManager.notifyPackageUpdated(pkg.getPackageName(),
-                    pkg.getBaseApkPath(), pkg.getSplitCodePaths());
-        }
-        return true;
-    }
-
-    private AndroidPackage installStubPackageLI(AndroidPackage stubPkg,
-            @ParseFlags int parseFlags, @ScanFlags int scanFlags)
-                    throws PackageManagerException {
-        if (DEBUG_COMPRESSION) {
-            Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName());
-        }
-        // uncompress the binary to its eventual destination on /data
-        final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getPath());
-        if (scanFile == null) {
-            throw new PackageManagerException(
-                    "Unable to decompress stub at " + stubPkg.getPath());
-        }
-        synchronized (mLock) {
-            mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
-        }
-        removePackageLI(stubPkg, true /*chatty*/);
-        try {
-            return scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
-        } catch (PackageManagerException e) {
-            Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
-                    e);
-            // Remove the failed install
-            removeCodePathLI(scanFile);
-            throw e;
-        }
-    }
-
-    /**
-     * Decompresses the given package on the system image onto
-     * the /data partition.
-     * @return The directory the package was decompressed into. Otherwise, {@code null}.
-     */
-    private File decompressPackage(String packageName, String codePath) {
-        final File[] compressedFiles = getCompressedFiles(codePath);
-        if (compressedFiles == null || compressedFiles.length == 0) {
-            if (DEBUG_COMPRESSION) {
-                Slog.i(TAG, "No files to decompress: " + codePath);
-            }
-            return null;
-        }
-        final File dstCodePath =
-                getNextCodePath(Environment.getDataAppDirectory(null), packageName);
-        int ret = PackageManager.INSTALL_SUCCEEDED;
-        try {
-            makeDirRecursive(dstCodePath, 0755);
-            for (File srcFile : compressedFiles) {
-                final String srcFileName = srcFile.getName();
-                final String dstFileName = srcFileName.substring(
-                        0, srcFileName.length() - COMPRESSED_EXTENSION.length());
-                final File dstFile = new File(dstCodePath, dstFileName);
-                ret = decompressFile(srcFile, dstFile);
-                if (ret != PackageManager.INSTALL_SUCCEEDED) {
-                    logCriticalInfo(Log.ERROR, "Failed to decompress"
-                            + "; pkg: " + packageName
-                            + ", file: " + dstFileName);
-                    break;
-                }
-            }
-        } catch (ErrnoException e) {
-            logCriticalInfo(Log.ERROR, "Failed to decompress"
-                    + "; pkg: " + packageName
-                    + ", err: " + e.errno);
-        }
-        if (ret == PackageManager.INSTALL_SUCCEEDED) {
-            final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
-            NativeLibraryHelper.Handle handle = null;
-            try {
-                handle = NativeLibraryHelper.Handle.create(dstCodePath);
-                ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
-                        null /*abiOverride*/, false /*isIncremental*/);
-            } catch (IOException e) {
-                logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
-                        + "; pkg: " + packageName);
-                ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
-            } finally {
-                IoUtils.closeQuietly(handle);
-            }
-        }
-        if (ret == PackageManager.INSTALL_SUCCEEDED) {
-            // NOTE: During boot, we have to delay releasing cblocks for no other reason than
-            // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}.
-            // When we no longer need to read that setting, cblock release can occur always
-            // occur here directly
-            if (!mSystemReady) {
-                if (mReleaseOnSystemReady == null) {
-                    mReleaseOnSystemReady = new ArrayList<>();
-                }
-                mReleaseOnSystemReady.add(dstCodePath);
-            } else {
-                final ContentResolver resolver = mContext.getContentResolver();
-                F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
-            }
-        }
-        if (ret != PackageManager.INSTALL_SUCCEEDED) {
-            if (!dstCodePath.exists()) {
-                return null;
-            }
-            removeCodePathLI(dstCodePath);
-            return null;
-        }
-
-        return dstCodePath;
-    }
 
     @GuardedBy("mLock")
     void updateInstantAppInstallerLocked(String modifiedPackage) {
@@ -9017,7 +8147,7 @@
     /**
      * Update given intent when being used to request {@link ResolveInfo}.
      */
-    private Intent updateIntentForResolve(Intent intent) {
+    private static Intent updateIntentForResolve(Intent intent) {
         if (intent.getSelector() != null) {
             intent = intent.getSelector();
         }
@@ -9822,7 +8952,7 @@
         return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
     }
 
-    private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) {
+    static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) {
         return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY;
     }
 
@@ -9830,7 +8960,7 @@
         return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
     }
 
-    private static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) {
+    static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) {
         return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
     }
 
@@ -10370,69 +9500,38 @@
     }
 
     @GuardedBy("mLock")
-    private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType,
+    private ResolveInfo findPersistentPreferredActivityLP(Intent intent,
+            String resolvedType,
             int flags, List<ResolveInfo> query, boolean debug, int userId) {
-        final int N = query.size();
-        PersistentPreferredIntentResolver ppir = mSettings.getPersistentPreferredActivities(userId);
-        // Get the list of persistent preferred activities that handle the intent
-        if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for presistent preferred activities...");
-        List<PersistentPreferredActivity> pprefs = ppir != null
-                ? ppir.queryIntent(intent, resolvedType,
-                        (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
-                        userId)
-                : null;
-        if (pprefs != null && pprefs.size() > 0) {
-            final int M = pprefs.size();
-            for (int i=0; i<M; i++) {
-                final PersistentPreferredActivity ppa = pprefs.get(i);
-                if (DEBUG_PREFERRED || debug) {
-                    Slog.v(TAG, "Checking PersistentPreferredActivity ds="
-                            + (ppa.countDataSchemes() > 0 ? ppa.getDataScheme(0) : "<none>")
-                            + "\n  component=" + ppa.mComponent);
-                    ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
-                }
-                final ActivityInfo ai = getActivityInfo(ppa.mComponent,
-                        flags | MATCH_DISABLED_COMPONENTS, userId);
-                if (DEBUG_PREFERRED || debug) {
-                    Slog.v(TAG, "Found persistent preferred activity:");
-                    if (ai != null) {
-                        ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
-                    } else {
-                        Slog.v(TAG, "  null");
-                    }
-                }
-                if (ai == null) {
-                    // This previously registered persistent preferred activity
-                    // component is no longer known. Ignore it and do NOT remove it.
-                    continue;
-                }
-                for (int j=0; j<N; j++) {
-                    final ResolveInfo ri = query.get(j);
-                    if (!ri.activityInfo.applicationInfo.packageName
-                            .equals(ai.applicationInfo.packageName)) {
-                        continue;
-                    }
-                    if (!ri.activityInfo.name.equals(ai.name)) {
-                        continue;
-                    }
-                    //  Found a persistent preference that can handle the intent.
-                    if (DEBUG_PREFERRED || debug) {
-                        Slog.v(TAG, "Returning persistent preferred activity: " +
-                                ri.activityInfo.packageName + "/" + ri.activityInfo.name);
-                    }
-                    return ri;
-                }
-            }
-        }
-        return null;
+        return mComputer.findPersistentPreferredActivityLP(intent,
+                resolvedType,
+                flags, query, debug, userId);
     }
 
-    private boolean isHomeIntent(Intent intent) {
+    private static boolean isHomeIntent(Intent intent) {
         return ACTION_MAIN.equals(intent.getAction())
                 && intent.hasCategory(CATEGORY_HOME)
                 && intent.hasCategory(CATEGORY_DEFAULT);
     }
 
+
+    // findPreferredActivityBody returns two items: a "things changed" flag and a
+    // ResolveInfo, which is the preferred activity itself.
+    private static class FindPreferredActivityBodyResult {
+        boolean mChanged;
+        ResolveInfo mPreferredResolveInfo;
+    }
+
+    private FindPreferredActivityBodyResult findPreferredActivityInternal(
+            Intent intent, String resolvedType, int flags,
+            List<ResolveInfo> query, boolean always,
+            boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
+        return mComputer.findPreferredActivityInternal(
+            intent, resolvedType, flags,
+            query, always,
+            removeMatches, debug, userId, queryMayBeFiltered);
+    }
+
     ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
             List<ResolveInfo> query, boolean always,
             boolean removeMatches, boolean debug, int userId) {
@@ -10451,206 +9550,22 @@
                     + " is holding mLock", new Throwable());
         }
         if (!mUserManager.exists(userId)) return null;
-        final int callingUid = Binder.getCallingUid();
-        // Do NOT hold the packages lock; this calls up into the settings provider which
-        // could cause a deadlock.
-        final boolean isDeviceProvisioned =
-                android.provider.Settings.Global.getInt(mContext.getContentResolver(),
-                        android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
-        flags = updateFlagsForResolve(
-                flags, userId, callingUid, false /*includeInstantApps*/,
-                isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
-                        flags));
-        intent = updateIntentForResolve(intent);
-        // writer
-        synchronized (mLock) {
-            // Try to find a matching persistent preferred activity.
-            ResolveInfo pri = findPersistentPreferredActivityLP(intent, resolvedType, flags, query,
-                    debug, userId);
 
-            // If a persistent preferred activity matched, use it.
-            if (pri != null) {
-                return pri;
+        FindPreferredActivityBodyResult body = findPreferredActivityInternal(
+                intent, resolvedType, flags, query, always,
+                removeMatches, debug, userId, queryMayBeFiltered);
+        if (body.mChanged) {
+            if (DEBUG_PREFERRED) {
+                Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions");
             }
-
-            PreferredIntentResolver pir = mSettings.getPreferredActivities(userId);
-            // Get the list of preferred activities that handle the intent
-            if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
-            List<PreferredActivity> prefs = pir != null
-                    ? pir.queryIntent(intent, resolvedType,
-                            (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
-                            userId)
-                    : null;
-            if (prefs != null && prefs.size() > 0) {
-                boolean changed = false;
-                try {
-                    // First figure out how good the original match set is.
-                    // We will only allow preferred activities that came
-                    // from the same match quality.
-                    int match = 0;
-
-                    if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Figuring out best match...");
-
-                    final int N = query.size();
-                    for (int j=0; j<N; j++) {
-                        final ResolveInfo ri = query.get(j);
-                        if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Match for " + ri.activityInfo
-                                + ": 0x" + Integer.toHexString(match));
-                        if (ri.match > match) {
-                            match = ri.match;
-                        }
-                    }
-
-                    if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Best match: 0x"
-                            + Integer.toHexString(match));
-
-                    match &= IntentFilter.MATCH_CATEGORY_MASK;
-                    final int M = prefs.size();
-                    for (int i=0; i<M; i++) {
-                        final PreferredActivity pa = prefs.get(i);
-                        if (DEBUG_PREFERRED || debug) {
-                            Slog.v(TAG, "Checking PreferredActivity ds="
-                                    + (pa.countDataSchemes() > 0 ? pa.getDataScheme(0) : "<none>")
-                                    + "\n  component=" + pa.mPref.mComponent);
-                            pa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
-                        }
-                        if (pa.mPref.mMatch != match) {
-                            if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping bad match "
-                                    + Integer.toHexString(pa.mPref.mMatch));
-                            continue;
-                        }
-                        // If it's not an "always" type preferred activity and that's what we're
-                        // looking for, skip it.
-                        if (always && !pa.mPref.mAlways) {
-                            if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping mAlways=false entry");
-                            continue;
-                        }
-                        final ActivityInfo ai = getActivityInfo(
-                                pa.mPref.mComponent, flags | MATCH_DISABLED_COMPONENTS
-                                        | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
-                                userId);
-                        if (DEBUG_PREFERRED || debug) {
-                            Slog.v(TAG, "Found preferred activity:");
-                            if (ai != null) {
-                                ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
-                            } else {
-                                Slog.v(TAG, "  null");
-                            }
-                        }
-                        final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent)
-                                && !isDeviceProvisioned;
-                        final boolean allowSetMutation = !excludeSetupWizardHomeActivity
-                                && !queryMayBeFiltered;
-                        if (ai == null) {
-                            // Do not remove launcher's preferred activity during SetupWizard
-                            // due to it may not install yet
-                            if (!allowSetMutation) {
-                                continue;
-                            }
-
-                            // This previously registered preferred activity
-                            // component is no longer known.  Most likely an update
-                            // to the app was installed and in the new version this
-                            // component no longer exists.  Clean it up by removing
-                            // it from the preferred activities list, and skip it.
-                            Slog.w(TAG, "Removing dangling preferred activity: "
-                                    + pa.mPref.mComponent);
-                            pir.removeFilter(pa);
-                            changed = true;
-                            continue;
-                        }
-                        for (int j=0; j<N; j++) {
-                            final ResolveInfo ri = query.get(j);
-                            if (!ri.activityInfo.applicationInfo.packageName
-                                    .equals(ai.applicationInfo.packageName)) {
-                                continue;
-                            }
-                            if (!ri.activityInfo.name.equals(ai.name)) {
-                                continue;
-                            }
-
-                            if (removeMatches && allowSetMutation) {
-                                pir.removeFilter(pa);
-                                changed = true;
-                                if (DEBUG_PREFERRED) {
-                                    Slog.v(TAG, "Removing match " + pa.mPref.mComponent);
-                                }
-                                break;
-                            }
-
-                            // Okay we found a previously set preferred or last chosen app.
-                            // If the result set is different from when this
-                            // was created, and is not a subset of the preferred set, we need to
-                            // clear it and re-ask the user their preference, if we're looking for
-                            // an "always" type entry.
-
-                            if (always && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity)) {
-                                if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) {
-                                    if (allowSetMutation) {
-                                        // some components of the set are no longer present in
-                                        // the query, but the preferred activity can still be reused
-                                        if (DEBUG_PREFERRED) {
-                                            Slog.i(TAG, "Result set changed, but PreferredActivity"
-                                                    + " is still valid as only non-preferred"
-                                                    + " components were removed for " + intent
-                                                    + " type " + resolvedType);
-                                        }
-                                        // remove obsolete components and re-add the up-to-date
-                                        // filter
-                                        PreferredActivity freshPa = new PreferredActivity(pa,
-                                                pa.mPref.mMatch,
-                                                pa.mPref.discardObsoleteComponents(query),
-                                                pa.mPref.mComponent,
-                                                pa.mPref.mAlways);
-                                        pir.removeFilter(pa);
-                                        pir.addFilter(freshPa);
-                                        changed = true;
-                                    } else {
-                                        if (DEBUG_PREFERRED) {
-                                            Slog.i(TAG, "Do not remove preferred activity");
-                                        }
-                                    }
-                                } else {
-                                    if (allowSetMutation) {
-                                        Slog.i(TAG,
-                                                "Result set changed, dropping preferred activity "
-                                                        + "for " + intent + " type "
-                                                        + resolvedType);
-                                        if (DEBUG_PREFERRED) {
-                                            Slog.v(TAG,
-                                                    "Removing preferred activity since set changed "
-                                                            + pa.mPref.mComponent);
-                                        }
-                                        pir.removeFilter(pa);
-                                        // Re-add the filter as a "last chosen" entry (!always)
-                                        PreferredActivity lastChosen = new PreferredActivity(
-                                                pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
-                                                false);
-                                        pir.addFilter(lastChosen);
-                                        changed = true;
-                                    }
-                                    return null;
-                                }
-                            }
-
-                            // Yay! Either the set matched or we're looking for the last chosen
-                            if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Returning preferred activity: "
-                                    + ri.activityInfo.packageName + "/" + ri.activityInfo.name);
-                            return ri;
-                        }
-                    }
-                } finally {
-                    if (changed) {
-                        if (DEBUG_PREFERRED) {
-                            Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions");
-                        }
-                        scheduleWritePackageRestrictionsLocked(userId);
-                    }
-                }
+            synchronized (mLock) {
+                scheduleWritePackageRestrictionsLocked(userId);
             }
         }
-        if (DEBUG_PREFERRED || debug) Slog.v(TAG, "No preferred activity to return");
-        return null;
+        if ((DEBUG_PREFERRED || debug) && body.mPreferredResolveInfo == null) {
+            Slog.v(TAG, "No preferred activity to return");
+        }
+        return body.mPreferredResolveInfo;
     }
 
     /*
@@ -11795,169 +10710,20 @@
         return finalList;
     }
 
-    private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
-            long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
-        try {
-            scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
-    }
-
-    private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
-            PackageParser2 packageParser, ExecutorService executorService) {
-        final File[] files = scanDir.listFiles();
-        if (ArrayUtils.isEmpty(files)) {
-            Log.d(TAG, "No files in app dir " + scanDir);
-            return;
-        }
-
-        if (DEBUG_PACKAGE_SCANNING) {
-            Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
-                    + " flags=0x" + Integer.toHexString(parseFlags));
-        }
-
-        ParallelPackageParser parallelPackageParser =
-                new ParallelPackageParser(packageParser, executorService);
-
-        // Submit files for parsing in parallel
-        int fileCount = 0;
-        for (File file : files) {
-            final boolean isPackage = (isApkFile(file) || file.isDirectory())
-                    && !PackageInstallerService.isStageName(file.getName());
-            if (!isPackage) {
-                // Ignore entries which are not packages
-                continue;
-            }
-            parallelPackageParser.submit(file, parseFlags);
-            fileCount++;
-        }
-
-        // Process results one by one
-        for (; fileCount > 0; fileCount--) {
-            ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
-            Throwable throwable = parseResult.throwable;
-            int errorCode = PackageManager.INSTALL_SUCCEEDED;
-            String errorMsg = null;
-
-            if (throwable == null) {
-                // TODO(toddke): move lower in the scan chain
-                // Static shared libraries have synthetic package names
-                if (parseResult.parsedPackage.isStaticSharedLibrary()) {
-                    renameStaticSharedLibraryPackage(parseResult.parsedPackage);
-                }
-                try {
-                    addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
-                            currentTime, null);
-                } catch (PackageManagerException e) {
-                    errorCode = e.error;
-                    errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
-                    Slog.w(TAG, errorMsg);
-                }
-            } else if (throwable instanceof PackageManagerException) {
-                PackageManagerException e = (PackageManagerException) throwable;
-                errorCode = e.error;
-                errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
-                Slog.w(TAG, errorMsg);
-            } else {
-                throw new IllegalStateException("Unexpected exception occurred while parsing "
-                        + parseResult.scanFile, throwable);
-            }
-
-            if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
-                mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
-            }
-
-            // Delete invalid userdata apps
-            if ((scanFlags & SCAN_AS_SYSTEM) == 0
-                    && errorCode != PackageManager.INSTALL_SUCCEEDED) {
-                logCriticalInfo(Log.WARN,
-                        "Deleting invalid package at " + parseResult.scanFile);
-                removeCodePathLI(parseResult.scanFile);
-            }
-        }
-    }
-
     public static void reportSettingsProblem(int priority, String msg) {
         logCriticalInfo(priority, msg);
     }
 
-    private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
-            boolean forceCollect, boolean skipVerify) throws PackageManagerException {
-        // When upgrading from pre-N MR1, verify the package time stamp using the package
-        // directory and not the APK file.
-        final long lastModifiedTime = mIsPreNMR1Upgrade
-                ? new File(parsedPackage.getPath()).lastModified()
-                : getLastModifiedTime(parsedPackage);
-        final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage);
-        if (ps != null && !forceCollect
-                && ps.getPathString().equals(parsedPackage.getPath())
-                && ps.timeStamp == lastModifiedTime
-                && !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
-                && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
-            if (ps.signatures.mSigningDetails.getSignatures() != null
-                    && ps.signatures.mSigningDetails.getSignatures().length != 0
-                    && ps.signatures.mSigningDetails.getSignatureSchemeVersion()
-                            != SignatureSchemeVersion.UNKNOWN) {
-                // Optimization: reuse the existing cached signing data
-                // if the package appears to be unchanged.
-                parsedPackage.setSigningDetails(
-                        new SigningDetails(ps.signatures.mSigningDetails));
-                return;
-            }
 
-            Slog.w(TAG, "PackageSetting for " + ps.name
-                    + " is missing signatures.  Collecting certs again to recover them.");
-        } else {
-            Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
-                    + (forceCollect ? " (forced)" : ""));
-        }
 
-        try {
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
-            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
-            final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
-                    input, parsedPackage, skipVerify);
-            if (result.isError()) {
-                throw new PackageManagerException(
-                        result.getErrorCode(), result.getErrorMessage(), result.getException());
-            }
-            parsedPackage.setSigningDetails(result.getResult());
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
-    }
 
-    /**
-     * Clear the package profile if this was an upgrade and the package
-     * version was updated.
-     */
-    private void maybeClearProfilesForUpgradesLI(
-            @Nullable PackageSetting originalPkgSetting,
-            @NonNull AndroidPackage pkg) {
-        if (originalPkgSetting == null || !isDeviceUpgrading()) {
-          return;
-        }
-        if (originalPkgSetting.versionCode == pkg.getVersionCode()) {
-          return;
-        }
-
-        clearAppProfilesLIF(pkg);
-        if (DEBUG_INSTALL) {
-            Slog.d(TAG, originalPkgSetting.name
-                  + " clear profile due to version change "
-                  + originalPkgSetting.versionCode + " != "
-                  + pkg.getVersionCode());
-        }
-    }
 
     /**
      *  Traces a package scan.
      *  @see #scanPackageLI(File, int, int, long, UserHandle)
      */
     @GuardedBy({"mInstallLock", "mLock"})
-    private AndroidPackage scanPackageTracedLI(File scanFile, final int parseFlags,
+    AndroidPackage scanPackageTracedLI(File scanFile, final int parseFlags,
             int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
         try {
@@ -11989,318 +10755,9 @@
             renameStaticSharedLibraryPackage(parsedPackage);
         }
 
-        return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
-    }
-
-    /**
-     * Returns if forced apk verification can be skipped for the whole package, including splits.
-     */
-    private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
-        if (!canSkipForcedApkVerification(pkg.getBaseApkPath())) {
-            return false;
-        }
-        // TODO: Allow base and splits to be verified individually.
-        String[] splitCodePaths = pkg.getSplitCodePaths();
-        if (!ArrayUtils.isEmpty(splitCodePaths)) {
-            for (int i = 0; i < splitCodePaths.length; i++) {
-                if (!canSkipForcedApkVerification(splitCodePaths[i])) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
-     * whether the apk contains signed root hash.  Note that the signer's certificate still needs to
-     * match one in a trusted source, and should be done separately.
-     */
-    private boolean canSkipForcedApkVerification(String apkPath) {
-        if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
-            return VerityUtils.hasFsverity(apkPath);
-        }
-
-        try {
-            final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
-            if (rootHashObserved == null) {
-                return false;  // APK does not contain Merkle tree root hash.
-            }
-            synchronized (mInstallLock) {
-                // Returns whether the observed root hash matches what kernel has.
-                mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
-                return true;
-            }
-        } catch (InstallerException | IOException | DigestException |
-                NoSuchAlgorithmException e) {
-            Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
-        }
-        return false;
-    }
-
-    /**
-     * Adds a new package to the internal data structures during platform initialization.
-     * <p>After adding, the package is known to the system and available for querying.
-     * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
-     * etc...], additional checks are performed. Basic verification [such as ensuring
-     * matching signatures, checking version codes, etc...] occurs if the package is
-     * identical to a previously known package. If the package fails a signature check,
-     * the version installed on /data will be removed. If the version of the new package
-     * is less than or equal than the version on /data, it will be ignored.
-     * <p>Regardless of the package location, the results are applied to the internal
-     * structures and the package is made available to the rest of the system.
-     * <p>NOTE: The return value should be removed. It's the passed in package object.
-     */
-    @GuardedBy({"mInstallLock", "mLock"})
-    private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
-            @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
-            @Nullable UserHandle user)
-                    throws PackageManagerException {
-        final boolean scanSystemPartition =
-                (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
-        final String renamedPkgName;
-        final PackageSetting disabledPkgSetting;
-        final boolean isSystemPkgUpdated;
-        final boolean pkgAlreadyExists;
-        PackageSetting pkgSetting;
-
-        synchronized (mLock) {
-            renamedPkgName = mSettings.getRenamedPackageLPr(parsedPackage.getRealPackage());
-            final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
-            if (realPkgName != null) {
-                ensurePackageRenamed(parsedPackage, renamedPkgName);
-            }
-            final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
-                    renamedPkgName);
-            final PackageSetting installedPkgSetting = mSettings.getPackageLPr(
-                    parsedPackage.getPackageName());
-            pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
-            pkgAlreadyExists = pkgSetting != null;
-            final String disabledPkgName = pkgAlreadyExists
-                    ? pkgSetting.name : parsedPackage.getPackageName();
-            if (scanSystemPartition && !pkgAlreadyExists
-                    && mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
-                // The updated-package data for /system apk remains inconsistently
-                // after the package data for /data apk is lost accidentally.
-                // To recover it, enable /system apk and install it as non-updated system app.
-                Slog.w(TAG, "Inconsistent package setting of updated system app for "
-                        + disabledPkgName + ". To recover it, enable the system app"
-                        + "and install it as non-updated system app.");
-                mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
-            }
-            disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
-            isSystemPkgUpdated = disabledPkgSetting != null;
-
-            if (DEBUG_INSTALL && isSystemPkgUpdated) {
-                Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
-            }
-
-            final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
-                    ? mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
-                            0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
-                    : null;
-            if (DEBUG_PACKAGE_SCANNING
-                    && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
-                    && sharedUserSetting != null) {
-                Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
-                        + " (uid=" + sharedUserSetting.userId + "):"
-                        + " packages=" + sharedUserSetting.packages);
-            }
-
-            if (scanSystemPartition) {
-                if (isSystemPkgUpdated) {
-                    // we're updating the disabled package, so, scan it as the package setting
-                    boolean isPlatformPackage = mPlatformPackage != null
-                            && Objects.equals(mPlatformPackage.getPackageName(),
-                            parsedPackage.getPackageName());
-                    final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
-                            null, disabledPkgSetting /* pkgSetting */,
-                            null /* disabledPkgSetting */, null /* originalPkgSetting */,
-                            null, parseFlags, scanFlags, isPlatformPackage, user, null);
-                    applyPolicy(parsedPackage, scanFlags, mPlatformPackage, true);
-                    final ScanResult scanResult =
-                            scanPackageOnlyLI(request, mInjector, mFactoryTest, -1L);
-                    if (scanResult.mExistingSettingCopied
-                            && scanResult.mRequest.mPkgSetting != null) {
-                        scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
-                    }
-                }
-            }
-        }
-
-        final boolean newPkgChangedPaths = pkgAlreadyExists
-                && !pkgSetting.getPathString().equals(parsedPackage.getPath());
-        final boolean newPkgVersionGreater =
-                pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
-        final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
-                && newPkgChangedPaths && newPkgVersionGreater;
-        if (isSystemPkgBetter) {
-            // The version of the application on /system is greater than the version on
-            // /data. Switch back to the application on /system.
-            // It's safe to assume the application on /system will correctly scan. If not,
-            // there won't be a working copy of the application.
-            synchronized (mLock) {
-                // just remove the loaded entries from package lists
-                mPackages.remove(pkgSetting.name);
-            }
-
-            logCriticalInfo(Log.WARN,
-                    "System package updated;"
-                    + " name: " + pkgSetting.name
-                    + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode()
-                    + "; " + pkgSetting.getPathString()
-                            + " --> " + parsedPackage.getPath());
-
-            final InstallArgs args = createInstallArgsForExisting(
-                    pkgSetting.getPathString(), getAppDexInstructionSets(
-                            pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
-            args.cleanUpResourcesLI();
-            synchronized (mLock) {
-                mSettings.enableSystemPackageLPw(pkgSetting.name);
-            }
-        }
-
-        // The version of the application on the /system partition is less than or
-        // equal to the version on the /data partition. Throw an exception and use
-        // the application already installed on the /data partition.
-        if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
-            // In the case of a skipped package, commitReconciledScanResultLocked is not called to
-            // add the object to the "live" data structures, so this is the final mutation step
-            // for the package. Which means it needs to be finalized here to cache derived fields.
-            // This is relevant for cases where the disabled system package is used for flags or
-            // other metadata.
-            parsedPackage.hideAsFinal();
-            throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
-                    + " at " + parsedPackage.getPath() + " ignored: updated version "
-                    + (pkgAlreadyExists ? String.valueOf(pkgSetting.versionCode) : "unknown")
-                    + " better than this " + parsedPackage.getLongVersionCode());
-        }
-
-        // Verify certificates against what was last scanned. Force re-collecting certificate in two
-        // special cases:
-        // 1) when scanning system, force re-collect only if system is upgrading.
-        // 2) when scannning /data, force re-collect only if the app is privileged (updated from
-        // preinstall, or treated as privileged, e.g. due to shared user ID).
-        final boolean forceCollect = scanSystemPartition ? mIsUpgrade
-                : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
-        if (DEBUG_VERIFY && forceCollect) {
-            Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
-        }
-
-        // Full APK verification can be skipped during certificate collection, only if the file is
-        // in verified partition, or can be verified on access (when apk verity is enabled). In both
-        // cases, only data in Signing Block is verified instead of the whole file.
-        // TODO(b/136132412): skip for Incremental installation
-        final boolean skipVerify = scanSystemPartition
-                || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
-        collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);
-
-        // Reset profile if the application version is changed
-        maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
-
-        /*
-         * A new system app appeared, but we already had a non-system one of the
-         * same name installed earlier.
-         */
-        boolean shouldHideSystemApp = false;
-        // A new application appeared on /system, but, we already have a copy of
-        // the application installed on /data.
-        if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
-                && !pkgSetting.isSystem()) {
-
-            if (!parsedPackage.getSigningDetails()
-                    .checkCapability(pkgSetting.signatures.mSigningDetails,
-                    SigningDetails.CertCapabilities.INSTALLED_DATA)
-                            && !pkgSetting.signatures.mSigningDetails.checkCapability(
-                                    parsedPackage.getSigningDetails(),
-                                    SigningDetails.CertCapabilities.ROLLBACK)) {
-                logCriticalInfo(Log.WARN,
-                        "System package signature mismatch;"
-                        + " name: " + pkgSetting.name);
-                try (@SuppressWarnings("unused") PackageFreezer freezer = freezePackage(
-                        parsedPackage.getPackageName(),
-                        "scanPackageInternalLI")) {
-                    deletePackageLIF(parsedPackage.getPackageName(), null, true,
-                            mUserManager.getUserIds(), 0, null, false);
-                }
-                pkgSetting = null;
-            } else if (newPkgVersionGreater) {
-                // The application on /system is newer than the application on /data.
-                // Simply remove the application on /data [keeping application data]
-                // and replace it with the version on /system.
-                logCriticalInfo(Log.WARN,
-                        "System package enabled;"
-                                + " name: " + pkgSetting.name
-                                + "; " + pkgSetting.versionCode + " --> "
-                                + parsedPackage.getLongVersionCode()
-                                + "; " + pkgSetting.getPathString() + " --> "
-                                + parsedPackage.getPath());
-                InstallArgs args = createInstallArgsForExisting(
-                        pkgSetting.getPathString(), getAppDexInstructionSets(
-                                pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
-                synchronized (mInstallLock) {
-                    args.cleanUpResourcesLI();
-                }
-            } else {
-                // The application on /system is older than the application on /data. Hide
-                // the application on /system and the version on /data will be scanned later
-                // and re-added like an update.
-                shouldHideSystemApp = true;
-                logCriticalInfo(Log.INFO,
-                        "System package disabled;"
-                                + " name: " + pkgSetting.name
-                                + "; old: " + pkgSetting.getPathString() + " @ "
-                                + pkgSetting.versionCode
-                                + "; new: " + parsedPackage.getPath() + " @ "
-                                + parsedPackage.getPath());
-            }
-        }
-
-        final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
-                | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
-        if (scanResult.mSuccess) {
-            synchronized (mLock) {
-                boolean appIdCreated = false;
-                try {
-                    final String pkgName = scanResult.mPkgSetting.name;
-                    final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
-                            new ReconcileRequest(
-                                    Collections.singletonMap(pkgName, scanResult),
-                                    mSharedLibraries,
-                                    mPackages,
-                                    Collections.singletonMap(
-                                            pkgName, getSettingsVersionForPackage(parsedPackage)),
-                                    Collections.singletonMap(pkgName,
-                                            getSharedLibLatestVersionSetting(scanResult))),
-                            mSettings.getKeySetManagerService(), mInjector);
-                    appIdCreated = optimisticallyRegisterAppId(scanResult);
-                    commitReconciledScanResultLocked(
-                            reconcileResult.get(pkgName), mUserManager.getUserIds());
-                } catch (PackageManagerException e) {
-                    if (appIdCreated) {
-                        cleanUpAppIdCreation(scanResult);
-                    }
-                    throw e;
-                }
-            }
-        }
-
-        if (shouldHideSystemApp) {
-            synchronized (mLock) {
-                mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
-            }
-        }
-        if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
-            if (pkgSetting != null && pkgSetting.isPackageLoading()) {
-                // Continue monitoring loading progress of active incremental packages
-                final IncrementalStatesCallback incrementalStatesCallback =
-                        new IncrementalStatesCallback(parsedPackage.getPackageName(), this);
-                pkgSetting.setIncrementalStatesCallback(incrementalStatesCallback);
-                mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
-                        new IncrementalProgressListener(parsedPackage.getPackageName(), this));
-            }
-        }
-        return scanResult.mPkgSetting.pkg;
+        final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
+        return helper.addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
+                mPlatformPackage, mIsUpgrade, mIsPreNMR1Upgrade);
     }
 
     // TODO:(b/135203078): Move to parsing
@@ -13264,7 +11721,7 @@
         }
     }
 
-    private void clearAppProfilesLIF(AndroidPackage pkg) {
+    void clearAppProfilesLIF(AndroidPackage pkg) {
         if (pkg == null) {
             Slog.wtf(TAG, "Package was null!", new Throwable());
             return;
@@ -13333,7 +11790,7 @@
     }
 
     @GuardedBy("mLock")
-    private void updateSharedLibrariesLocked(AndroidPackage pkg, PackageSetting pkgSetting,
+    void updateSharedLibrariesLocked(AndroidPackage pkg, PackageSetting pkgSetting,
             @Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting,
             Map<String, AndroidPackage> availablePackages)
             throws PackageManagerException {
@@ -13743,7 +12200,7 @@
     // method. Also, we need to solve the problem of potentially creating a new shared user
     // setting. That can probably be done later and patch things up after the fact.
     @GuardedBy({"mInstallLock", "mLock"})
-    private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
+    ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
             final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
 
@@ -13956,7 +12413,7 @@
      * <p>This may differ from the package's actual name if the application has already
      * been installed under one of this package's original names.
      */
-    private static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
+    static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
             @Nullable String renamedPkgName) {
         if (isPackageRenamed(pkg, renamedPkgName)) {
             return pkg.getRealPackage();
@@ -13978,7 +12435,7 @@
      * shared user [if any].
      */
     @GuardedBy("mLock")
-    private @Nullable PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
+    @Nullable PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
             @Nullable String renamedPkgName) {
         if (isPackageRenamed(pkg, renamedPkgName)) {
             return null;
@@ -14017,7 +12474,7 @@
      * <p>When we've already installed the package under an original name, update
      * the new package so we can continue to have the old name.
      */
-    private static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
+    static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
             @NonNull String renamedPackageName) {
         if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
                 || parsedPackage.getPackageName().equals(renamedPackageName)) {
@@ -14130,7 +12587,7 @@
     @GuardedBy("mInstallLock")
     @VisibleForTesting
     @NonNull
-    static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+    ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
             Injector injector,
             boolean isUnderFactoryTest, long currentTime)
             throws PackageManagerException {
@@ -14533,7 +12990,7 @@
      * Implementation detail: This method must NOT have any side effect. It would
      * ideally be static, but, it requires locks to read system state.
      */
-    private static void applyPolicy(ParsedPackage parsedPackage,
+    static void applyPolicy(ParsedPackage parsedPackage,
             final @ScanFlags int scanFlags, AndroidPackage platformPkg,
             boolean isUpdatedSystemApp) {
         if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
@@ -15819,8 +14276,7 @@
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
-            boolean suspended) {
+    void sendPackagesSuspendedForUser(String intent, String[] pkgList, int[] uidList, int userId) {
         final List<List<String>> pkgsToSend = new ArrayList(pkgList.length);
         final List<IntArray> uidsToSend = new ArrayList(pkgList.length);
         final List<SparseArray<int[]>> allowListsToSend = new ArrayList(pkgList.length);
@@ -15861,11 +14317,8 @@
             extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidsToSend.get(i).toArray());
             final SparseArray<int[]> allowList = allowListsToSend.get(i).size() == 0
                     ? null : allowListsToSend.get(i);
-            sendPackageBroadcast(
-                    suspended ? Intent.ACTION_PACKAGES_SUSPENDED
-                            : Intent.ACTION_PACKAGES_UNSUSPENDED,
-                    null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
-                    userIds, null, allowList, null);
+            sendPackageBroadcast(intent, null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null,
+                    null, userIds, null, allowList, null);
         }
     }
 
@@ -16179,6 +14632,8 @@
 
         final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
         final IntArray changedUids = new IntArray(packageNames.length);
+        final List<String> modifiedPackagesList = new ArrayList<>(packageNames.length);
+        final IntArray modifiedUids = new IntArray(packageNames.length);
         final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
         final boolean[] canSuspend = suspended ? canSuspendPackageForUserInternal(packageNames,
                 userId) : null;
@@ -16206,13 +14661,14 @@
                 unactionedPackages.add(packageName);
                 continue;
             }
-            boolean packageUnsuspended;
+            final boolean packageUnsuspended;
+            final boolean packageModified;
             synchronized (mLock) {
                 if (suspended) {
-                    pkgSetting.addOrUpdateSuspension(callingPackage, dialogInfo, appExtras,
-                            launcherExtras, userId);
+                    packageModified = pkgSetting.addOrUpdateSuspension(callingPackage,
+                            dialogInfo, appExtras, launcherExtras, userId);
                 } else {
-                    pkgSetting.removeSuspension(callingPackage, userId);
+                    packageModified = pkgSetting.removeSuspension(callingPackage, userId);
                 }
                 packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
             }
@@ -16220,18 +14676,29 @@
                 changedPackagesList.add(packageName);
                 changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
             }
+            if (packageModified) {
+                modifiedPackagesList.add(packageName);
+                modifiedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+            }
         }
 
         if (!changedPackagesList.isEmpty()) {
-            final String[] changedPackages = changedPackagesList.toArray(
-                    new String[changedPackagesList.size()]);
-            sendPackagesSuspendedForUser(changedPackages, changedUids.toArray(), userId, suspended);
+            final String[] changedPackages = changedPackagesList.toArray(new String[0]);
+            sendPackagesSuspendedForUser(
+                    suspended ? Intent.ACTION_PACKAGES_SUSPENDED
+                              : Intent.ACTION_PACKAGES_UNSUSPENDED,
+                    changedPackages, changedUids.toArray(), userId);
             sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
             synchronized (mLock) {
                 scheduleWritePackageRestrictionsLocked(userId);
             }
         }
-        return unactionedPackages.toArray(new String[unactionedPackages.size()]);
+        // Send the suspension changed broadcast to ensure suspension state is not stale.
+        if (!modifiedPackagesList.isEmpty()) {
+            sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+                    modifiedPackagesList.toArray(new String[0]), modifiedUids.toArray(), userId);
+        }
+        return unactionedPackages.toArray(new String[0]);
     }
 
     @Override
@@ -16360,7 +14827,8 @@
             final String[] packageArray = unsuspendedPackages.toArray(
                     new String[unsuspendedPackages.size()]);
             sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
-            sendPackagesSuspendedForUser(packageArray, unsuspendedUids.toArray(), userId, false);
+            sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
+                    packageArray, unsuspendedUids.toArray(), userId);
         }
     }
 
@@ -16639,30 +15107,6 @@
         return Math.max(timeout, DEFAULT_VERIFICATION_TIMEOUT);
     }
 
-
-    /**
-     * Get the default verification agent response code.
-     *
-     * @return default verification response code
-     */
-    private int getDefaultVerificationResponse(UserHandle user) {
-        if (mUserManager.hasUserRestriction(UserManager.ENSURE_VERIFY_APPS, user.getIdentifier())) {
-            return PackageManager.VERIFICATION_REJECT;
-        }
-        return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
-                android.provider.Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
-                DEFAULT_VERIFICATION_RESPONSE);
-    }
-
-    /**
-     * Get the default integrity verification response code.
-     */
-    private int getDefaultIntegrityVerificationResponse() {
-        // We are not exposing this as a user-configurable setting because we don't want to provide
-        // an easy way to get around the integrity check.
-        return PackageManager.VERIFICATION_REJECT;
-    }
-
     @Deprecated
     @Override
     public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) {
@@ -16733,17 +15177,6 @@
         }
     }
 
-    /**
-     * Get the "allow unknown sources" setting.
-     *
-     * @return the current "allow unknown sources" setting
-     */
-    private int getUnknownSourcesSettings() {
-        return android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
-                android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
-                -1);
-    }
-
     @Override
     public void setInstallerPackageName(String targetPackage, String installerPackageName) {
         final int callingUid = Binder.getCallingUid();
@@ -17060,7 +15493,7 @@
         });
     }
 
-    private void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
+    void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
             int[] userIds, int[] instantUserIds) {
         sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
                 installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */, null);
@@ -17099,19 +15532,8 @@
         return new File(firstLevelDir, packageName + "-" + suffix);
     }
 
-    private void removeNativeBinariesLI(PackageSetting ps) {
-        if (ps != null) {
-            NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
-        }
-    }
-
     @GuardedBy("mLock")
-    private void enableSystemPackageLPw(AndroidPackage pkg) {
-        mSettings.enableSystemPackageLPw(pkg.getPackageName());
-    }
-
-    @GuardedBy("mLock")
-    static Map<String, ReconciledPackage> reconcilePackagesLocked(
+    Map<String, ReconciledPackage> reconcilePackagesLocked(
             final ReconcileRequest request, KeySetManagerService ksms, Injector injector)
             throws ReconcileFailure {
         final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
@@ -17987,7 +16409,10 @@
                             Slog.i(TAG, "Enabling system stub after removal; pkg: "
                                     + stubPkg.getPackageName());
                         }
-                        enableCompressedPackage(stubPkg, stubPs);
+                        final InitAndSystemPackageHelper helper =
+                                new InitAndSystemPackageHelper(this);
+                        helper.enableCompressedPackage(stubPkg, stubPs, mDefParseFlags,
+                                mDirsToScanAsSystem);
                     } else if (DEBUG_COMPRESSION) {
                         Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
                                 + "after removal; pkg: " + stubPkg.getPackageName());
@@ -18006,7 +16431,7 @@
      * make sure this flag is set for partially installed apps. If not its meaningless to
      * delete a partially installed application.
      */
-    private void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles,
+    void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles,
             PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
         String packageName = deletedPs.name;
         if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
@@ -18130,181 +16555,7 @@
         return null;
     }
 
-    /*
-     * Tries to delete system package.
-     */
-    private void deleteSystemPackageLIF(DeletePackageAction action, PackageSetting deletedPs,
-            @NonNull int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo,
-            boolean writeSettings)
-            throws SystemDeleteException {
-        final boolean applyUserRestrictions = outInfo != null && (outInfo.mOrigUsers != null);
-        final AndroidPackage deletedPkg = deletedPs.pkg;
-        // Confirm if the system package has been updated
-        // An updated system app can be deleted. This will also have to restore
-        // the system pkg from system partition
-        // reader
-        final PackageSetting disabledPs = action.mDisabledPs;
-        if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.getPackageName()
-                + " disabledPs=" + disabledPs);
-        Slog.d(TAG, "Deleting system pkg from data partition");
-
-        if (DEBUG_REMOVE) {
-            if (applyUserRestrictions) {
-                Slog.d(TAG, "Remembering install states:");
-                for (int userId : allUserHandles) {
-                    final boolean finstalled = ArrayUtils.contains(outInfo.mOrigUsers, userId);
-                    Slog.d(TAG, "   u=" + userId + " inst=" + finstalled);
-                }
-            }
-        }
-
-        if (outInfo != null) {
-            // Delete the updated package
-            outInfo.mIsRemovedPackageSystemUpdate = true;
-        }
-
-        if (disabledPs.versionCode < deletedPs.versionCode) {
-            // Delete data for downgrades
-            flags &= ~PackageManager.DELETE_KEEP_DATA;
-        } else {
-            // Preserve data by setting flag
-            flags |= PackageManager.DELETE_KEEP_DATA;
-        }
-
-        deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
-                outInfo, writeSettings);
-
-        // writer
-        synchronized (mLock) {
-            // NOTE: The system package always needs to be enabled; even if it's for
-            // a compressed stub. If we don't, installing the system package fails
-            // during scan [scanning checks the disabled packages]. We will reverse
-            // this later, after we've "installed" the stub.
-            // Reinstate the old system package
-            enableSystemPackageLPw(disabledPs.pkg);
-            // Remove any native libraries from the upgraded package.
-            removeNativeBinariesLI(deletedPs);
-        }
-
-        // Install the system package
-        if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
-        try {
-            installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
-                    outInfo == null ? null : outInfo.mOrigUsers, writeSettings);
-        } catch (PackageManagerException e) {
-            Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": "
-                    + e.getMessage());
-            // TODO(patb): can we avoid this; throw would come from scan...
-            throw new SystemDeleteException(e);
-        } finally {
-            if (disabledPs.pkg.isStub()) {
-                // We've re-installed the stub; make sure it's disabled here. If package was
-                // originally enabled, we'll install the compressed version of the application
-                // and re-enable it afterward.
-                final PackageSetting stubPs = mSettings.getPackageLPr(deletedPkg.getPackageName());
-                if (stubPs != null) {
-                    int userId = action.mUser == null
-                            ? UserHandle.USER_ALL : action.mUser.getIdentifier();
-                    if (userId == UserHandle.USER_ALL) {
-                        for (int aUserId : allUserHandles) {
-                            stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
-                        }
-                    } else if (userId >= UserHandle.USER_SYSTEM) {
-                        stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android");
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Installs a package that's already on the system partition.
-     */
-    private void installPackageFromSystemLIF(@NonNull String codePathString,
-            @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings)
-            throws PackageManagerException {
-        final File codePath = new File(codePathString);
-        @ParseFlags int parseFlags =
-                mDefParseFlags
-                | ParsingPackageUtils.PARSE_MUST_BE_APK
-                | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
-        @ScanFlags int scanFlags = SCAN_AS_SYSTEM;
-        for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
-            ScanPartition partition = mDirsToScanAsSystem.get(i);
-            if (partition.containsFile(codePath)) {
-                scanFlags |= partition.scanFlag;
-                if (partition.containsPrivApp(codePath)) {
-                    scanFlags |= SCAN_AS_PRIVILEGED;
-                }
-                break;
-            }
-        }
-
-        final AndroidPackage pkg =
-                scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
-
-        PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.getPackageName());
-
-        try {
-            // update shared libraries for the newly re-installed system package
-            updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
-                    Collections.unmodifiableMap(mPackages));
-        } catch (PackageManagerException e) {
-            Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
-        }
-
-        prepareAppDataAfterInstallLIF(pkg);
-
-        // writer
-        synchronized (mLock) {
-            PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName());
-
-            final boolean applyUserRestrictions = origUserHandles != null;
-            if (applyUserRestrictions) {
-                boolean installedStateChanged = false;
-                if (DEBUG_REMOVE) {
-                    Slog.d(TAG, "Propagating install state across reinstall");
-                }
-                for (int userId : allUserHandles) {
-                    final boolean installed = ArrayUtils.contains(origUserHandles, userId);
-                    if (DEBUG_REMOVE) {
-                        Slog.d(TAG, "    user " + userId + " => " + installed);
-                    }
-                    if (installed != ps.getInstalled(userId)) {
-                        installedStateChanged = true;
-                    }
-                    ps.setInstalled(installed, userId);
-                    if (installed) {
-                        ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
-                    }
-                }
-                // Regardless of writeSettings we need to ensure that this restriction
-                // state propagation is persisted
-                mSettings.writeAllUsersPackageRestrictionsLPr();
-                if (installedStateChanged) {
-                    mSettings.writeKernelMappingLPr(ps);
-                }
-            }
-
-            // The method below will take care of removing obsolete permissions and granting
-            // install permissions.
-            mPermissionManager.onPackageInstalled(pkg,
-                    PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
-                    UserHandle.USER_ALL);
-            for (final int userId : allUserHandles) {
-                if (applyUserRestrictions) {
-                    mSettings.writePermissionStateForUserLPr(userId, false);
-                }
-            }
-
-            // can downgrade to reader here
-            if (writeSettings) {
-                writeSettingsLPrTEMP();
-            }
-        }
-    }
-
-    private void deleteInstalledPackageLIF(PackageSetting ps,
+    void deleteInstalledPackageLIF(PackageSetting ps,
             boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
             PackageRemovedInfo outInfo, boolean writeSettings) {
         synchronized (mLock) {
@@ -18409,7 +16660,7 @@
     /*
      * This method handles package deletion in general
      */
-    private boolean deletePackageLIF(@NonNull String packageName, UserHandle user,
+    boolean deletePackageLIF(@NonNull String packageName, UserHandle user,
             boolean deleteCodeAndResources, @NonNull int[] allUserHandles, int flags,
             PackageRemovedInfo outInfo, boolean writeSettings) {
         final DeletePackageAction action;
@@ -18504,7 +16755,9 @@
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
             // When an updated system application is deleted we delete the existing resources
             // as well and fall back to existing code in system partition
-            deleteSystemPackageLIF(action, ps, allUserHandles, flags, outInfo, writeSettings);
+            final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
+            helper.deleteSystemPackageLIF(action, ps, allUserHandles, flags, outInfo,
+                    writeSettings, mDefParseFlags, mDirsToScanAsSystem);
         } else {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
             deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
@@ -19112,7 +17365,7 @@
         mSettings.clearPackagePreferredActivities(packageName, outUserChanged, userId);
     }
 
-    private void restorePermissionsAndUpdateRolesForNewUserInstall(String packageName,
+    void restorePermissionsAndUpdateRolesForNewUserInstall(String packageName,
             @UserIdInt int userId) {
         // We may also need to apply pending (restored) runtime permission grants
         // within these users.
@@ -20190,7 +18443,9 @@
             if (isSystemStub
                     && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                     || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
-                if (!enableCompressedPackage(deletedPkg, pkgSetting)) {
+                final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
+                if (!helper.enableCompressedPackage(deletedPkg, pkgSetting, mDefParseFlags,
+                        mDirsToScanAsSystem)) {
                     Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
                             + "commpressed package " + setting.getPackageName());
                     updateAllowed[i] = false;
@@ -20373,7 +18628,7 @@
         }
     }
 
-    private void sendPackageChangedBroadcast(String packageName,
+    void sendPackageChangedBroadcast(String packageName,
             boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason) {
         if (DEBUG_INSTALL)
             Log.v(TAG, "Sending package changed: package=" + packageName + " components="
@@ -21090,15 +19345,24 @@
 
         final String packageName = dumpState.getTargetPackageName();
         final boolean checkin = dumpState.isCheckIn();
+
+        // Return if the package doesn't exist.
+        if (packageName != null
+                && getPackageSetting(packageName) == null
+                && !mApexManager.isApexPackage(packageName)) {
+            pw.println("Unable to find package: " + packageName);
+            return;
+        }
+
         if (checkin) {
             pw.println("vers,1");
         }
 
         // reader
-        if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
-            if (!checkin) {
-                dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
-            }
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_VERSION)
+                && packageName == null) {
+            dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
         }
 
         if (!checkin
@@ -21129,7 +19393,8 @@
             ipw.decreaseIndent();
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
+                && packageName == null) {
             final String requiredVerifierPackage = mRequiredVerifierPackage;
             if (!checkin) {
                 if (dumpState.onTitlePrinted()) {
@@ -21150,14 +19415,16 @@
             }
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER)
+                && packageName == null) {
             final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
             final ComponentName verifierComponent = proxy.getComponentName();
             if (verifierComponent != null) {
                 String verifierPackageName = verifierComponent.getPackageName();
                 if (!checkin) {
-                    if (dumpState.onTitlePrinted())
+                    if (dumpState.onTitlePrinted()) {
                         pw.println();
+                    }
                     pw.println("Domain Verifier:");
                     pw.print("  Using: ");
                     pw.print(verifierPackageName);
@@ -21177,11 +19444,13 @@
             }
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_LIBS)
+                && packageName == null) {
             dump(DumpState.DUMP_LIBS, fd, pw, dumpState);
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_FEATURES)
+                && packageName == null) {
             if (dumpState.onTitlePrinted()) {
                 pw.println();
             }
@@ -21191,12 +19460,7 @@
 
             synchronized (mAvailableFeatures) {
                 for (FeatureInfo feat : mAvailableFeatures.values()) {
-                    if (checkin) {
-                        pw.print("feat,");
-                        pw.print(feat.name);
-                        pw.print(",");
-                        pw.println(feat.version);
-                    } else {
+                    if (!checkin) {
                         pw.print("  ");
                         pw.print(feat.name);
                         if (feat.version > 0) {
@@ -21204,55 +19468,73 @@
                             pw.print(feat.version);
                         }
                         pw.println();
+                    } else {
+                        pw.print("feat,");
+                        pw.print(feat.name);
+                        pw.print(",");
+                        pw.println(feat.version);
                     }
                 }
             }
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
             }
         }
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
             }
         }
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
             }
         }
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
             }
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
             dump(DumpState.DUMP_PREFERRED, fd, pw, dumpState);
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)
+                && packageName == null) {
             dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
             dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
-            mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+            synchronized (mLock) {
+                mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
+            }
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
             }
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
             synchronized (mLock) {
                 mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
             }
@@ -21267,7 +19549,8 @@
             }
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_QUERIES)) {
             dump(DumpState.DUMP_QUERIES, fd, pw, dumpState);
         }
 
@@ -21279,8 +19562,12 @@
             }
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_CHANGES)) {
-            if (dumpState.onTitlePrinted()) pw.println();
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_CHANGES)
+                && packageName == null) {
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
             pw.println("Package Changes:");
             synchronized (mLock) {
                 pw.print("  Sequence number="); pw.println(mChangedPackagesSequenceNumber);
@@ -21306,11 +19593,14 @@
             }
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_FROZEN)
+                && packageName == null) {
             // XXX should handle packageName != null by dumping only install data that
             // the given package is involved with.
-            if (dumpState.onTitlePrinted()) pw.println();
-
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
             final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
             ipw.println();
             ipw.println("Frozen packages:");
@@ -21327,9 +19617,12 @@
             ipw.decreaseIndent();
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
-            if (dumpState.onTitlePrinted()) pw.println();
-
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_VOLUMES)
+                && packageName == null) {
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
             final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
             ipw.println();
             ipw.println("Loaded volumes:");
@@ -21346,52 +19639,65 @@
             ipw.decreaseIndent();
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
                 && packageName == null) {
             synchronized (mLock) {
                 mComponentResolver.dumpServicePermissions(pw, dumpState);
             }
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
-            if (dumpState.onTitlePrinted()) pw.println();
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
             dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState);
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
-            if (dumpState.onTitlePrinted()) pw.println();
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
             dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState);
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
-            if (dumpState.onTitlePrinted()) pw.println();
-            synchronized (mLock) {
-                mSettings.dumpReadMessagesLPr(pw, dumpState);
+        if (dumpState.isDumping(DumpState.DUMP_MESSAGES)
+                && packageName == null) {
+            if (!checkin) {
+                if (dumpState.onTitlePrinted()) {
+                    pw.println();
+                }
+                synchronized (mLock) {
+                    mSettings.dumpReadMessagesLPr(pw, dumpState);
+                }
+                pw.println();
+                pw.println("Package warning messages:");
+                dumpCriticalInfo(pw, null);
+            } else {
+                dumpCriticalInfo(pw, "msg,");
             }
-            pw.println();
-            pw.println("Package warning messages:");
-            dumpCriticalInfo(pw, null);
-        }
-
-        if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) {
-            dumpCriticalInfo(pw, "msg,");
         }
 
         // PackageInstaller should be called outside of mPackages lock
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_INSTALLS)
+                && packageName == null) {
             // XXX should handle packageName != null by dumping only install data that
             // the given package is involved with.
-            if (dumpState.onTitlePrinted()) pw.println();
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
             mInstallerService.dump(new IndentingPrintWriter(pw, "  ", 120));
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_APEX)
+                && (packageName == null || mApexManager.isApexPackage(packageName))) {
             mApexManager.dump(pw, packageName);
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
                 && packageName == null) {
-            pw.println();
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
             pw.println("Per UID read timeouts:");
             pw.println("    Default timeouts flag: " + getDefaultTimeouts());
             pw.println("    Known digesters list flag: " + getKnownDigestersList());
@@ -21408,7 +19714,12 @@
             }
         }
 
-        if (!checkin && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)) {
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)
+                && packageName == null) {
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
             pw.println("Snapshot statistics");
             if (!mSnapshotEnabled) {
                 pw.println("  Snapshots disabled");
@@ -22838,137 +21149,6 @@
         }
     }
 
-    Pair<Integer, String> verifyReplacingVersionCode(PackageInfoLite pkgLite,
-            long requiredInstalledVersionCode, int installFlags) {
-        if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
-            return verifyReplacingVersionCodeForApex(
-                    pkgLite, requiredInstalledVersionCode, installFlags);
-        }
-
-        String packageName = pkgLite.packageName;
-        synchronized (mLock) {
-            // Package which currently owns the data that the new package will own if installed.
-            // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg
-            // will be null whereas dataOwnerPkg will contain information about the package
-            // which was uninstalled while keeping its data.
-            AndroidPackage dataOwnerPkg = mPackages.get(packageName);
-            if (dataOwnerPkg  == null) {
-                PackageSetting ps = mSettings.getPackageLPr(packageName);
-                if (ps != null) {
-                    dataOwnerPkg = ps.pkg;
-                }
-            }
-
-            if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
-                if (dataOwnerPkg == null) {
-                    String errorMsg = "Required installed version code was "
-                            + requiredInstalledVersionCode
-                            + " but package is not installed";
-                    Slog.w(TAG, errorMsg);
-                    return Pair.create(
-                            PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
-                }
-
-                if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
-                    String errorMsg = "Required installed version code was "
-                            + requiredInstalledVersionCode
-                            + " but actual installed version is "
-                            + dataOwnerPkg.getLongVersionCode();
-                    Slog.w(TAG, errorMsg);
-                    return Pair.create(
-                            PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
-                }
-            }
-
-            if (dataOwnerPkg != null) {
-                if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
-                        dataOwnerPkg.isDebuggable())) {
-                    try {
-                        checkDowngrade(dataOwnerPkg, pkgLite);
-                    } catch (PackageManagerException e) {
-                        String errorMsg = "Downgrade detected: " + e.getMessage();
-                        Slog.w(TAG, errorMsg);
-                        return Pair.create(
-                                PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
-                    }
-                }
-            }
-        }
-        return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
-    }
-
-    private Pair<Integer, String> verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite,
-            long requiredInstalledVersionCode, int installFlags) {
-        String packageName = pkgLite.packageName;
-
-        final PackageInfo activePackage = mApexManager.getPackageInfo(packageName,
-                ApexManager.MATCH_ACTIVE_PACKAGE);
-        if (activePackage == null) {
-            String errorMsg = "Attempting to install new APEX package " + packageName;
-            Slog.w(TAG, errorMsg);
-            return Pair.create(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, errorMsg);
-        }
-
-        final long activeVersion = activePackage.getLongVersionCode();
-        if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST
-                && activeVersion != requiredInstalledVersionCode) {
-            String errorMsg = "Installed version of APEX package " + packageName
-                    + " does not match required. Active version: " + activeVersion
-                    + " required: " + requiredInstalledVersionCode;
-            Slog.w(TAG, errorMsg);
-            return Pair.create(PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
-        }
-
-        final boolean isAppDebuggable = (activePackage.applicationInfo.flags
-                & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-        final long newVersionCode = pkgLite.getLongVersionCode();
-        if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable)
-                && newVersionCode < activeVersion) {
-            String errorMsg = "Downgrade of APEX package " + packageName
-                    + " is not allowed. Active version: " + activeVersion
-                    + " attempted: " + newVersionCode;
-            Slog.w(TAG, errorMsg);
-            return Pair.create(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
-        }
-
-        return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
-    }
-
-    /**
-     * Check and throw if the given before/after packages would be considered a
-     * downgrade.
-     */
-    private static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
-            throws PackageManagerException {
-        if (after.getLongVersionCode() < before.getLongVersionCode()) {
-            throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
-                    "Update version code " + after.versionCode + " is older than current "
-                            + before.getLongVersionCode());
-        } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
-            if (after.baseRevisionCode < before.getBaseRevisionCode()) {
-                throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
-                        "Update base revision code " + after.baseRevisionCode
-                                + " is older than current " + before.getBaseRevisionCode());
-            }
-
-            if (!ArrayUtils.isEmpty(after.splitNames)) {
-                for (int i = 0; i < after.splitNames.length; i++) {
-                    final String splitName = after.splitNames[i];
-                    final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
-                    if (j != -1) {
-                        if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
-                            throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
-                                    "Update split " + splitName + " revision code "
-                                            + after.splitRevisionCodes[i]
-                                            + " is older than current "
-                                            + before.getSplitRevisionCodes()[j]);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     private static class MoveCallbacks extends Handler {
         private static final int MSG_CREATED = 1;
         private static final int MSG_STATUS_CHANGED = 2;
@@ -24580,7 +22760,7 @@
         return mComputer.getPackageSetting(packageName);
     }
 
-    private PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
+    PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
         return mComputer.getPackageSettingInternal(packageName, callingUid);
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 717f3d5..d9c4d316 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -455,7 +455,7 @@
         return state.suspendParams != null && state.suspendParams.containsKey(suspendingPackage);
     }
 
-    void addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
+    boolean addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
             PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
         final PackageUserState existingUserState = modifyUserState(userId);
         final PackageUserState.SuspendParams newSuspendParams =
@@ -464,21 +464,27 @@
         if (existingUserState.suspendParams == null) {
             existingUserState.suspendParams = new ArrayMap<>();
         }
-        existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
+        final PackageUserState.SuspendParams oldSuspendParams =
+                existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
         existingUserState.suspended = true;
         onChanged();
+        return !Objects.equals(oldSuspendParams, newSuspendParams);
     }
 
-    void removeSuspension(String suspendingPackage, int userId) {
+    boolean removeSuspension(String suspendingPackage, int userId) {
+        boolean wasModified = false;
         final PackageUserState existingUserState = modifyUserState(userId);
         if (existingUserState.suspendParams != null) {
-            existingUserState.suspendParams.remove(suspendingPackage);
+            if (existingUserState.suspendParams.remove(suspendingPackage) != null) {
+                wasModified = true;
+            }
             if (existingUserState.suspendParams.size() == 0) {
                 existingUserState.suspendParams = null;
             }
         }
         existingUserState.suspended = (existingUserState.suspendParams != null);
         onChanged();
+        return wasModified;
     }
 
     void removeSuspension(Predicate<String> suspendingPackagePredicate, int userId) {
diff --git a/services/core/java/com/android/server/pm/ScanPartition.java b/services/core/java/com/android/server/pm/ScanPartition.java
new file mode 100644
index 0000000..e1d2b3b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ScanPartition.java
@@ -0,0 +1,80 @@
+/*
+ * 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.pm;
+
+import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
+
+import android.annotation.NonNull;
+import android.content.pm.PackagePartitions;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+
+/**
+ * List of partitions to be scanned during system boot
+ */
+@VisibleForTesting
+public class ScanPartition extends PackagePartitions.SystemPartition {
+    @PackageManagerService.ScanFlags
+    public final int scanFlag;
+
+    public ScanPartition(@NonNull PackagePartitions.SystemPartition partition) {
+        super(partition);
+        scanFlag = scanFlagForPartition(partition);
+    }
+
+    /**
+     * Creates a partition containing the same folders as the original partition but with a
+     * different root folder. The new partition will include the scan flags of the original
+     * partition along with any specified additional scan flags.
+     */
+    public ScanPartition(@NonNull File folder, @NonNull ScanPartition original,
+            @PackageManagerService.ScanFlags int additionalScanFlag) {
+        super(folder, original);
+        this.scanFlag = original.scanFlag | additionalScanFlag;
+    }
+
+    private static int scanFlagForPartition(PackagePartitions.SystemPartition partition) {
+        switch (partition.type) {
+            case PackagePartitions.PARTITION_SYSTEM:
+                return 0;
+            case PackagePartitions.PARTITION_VENDOR:
+                return SCAN_AS_VENDOR;
+            case PackagePartitions.PARTITION_ODM:
+                return SCAN_AS_ODM;
+            case PackagePartitions.PARTITION_OEM:
+                return SCAN_AS_OEM;
+            case PackagePartitions.PARTITION_PRODUCT:
+                return SCAN_AS_PRODUCT;
+            case PackagePartitions.PARTITION_SYSTEM_EXT:
+                return SCAN_AS_SYSTEM_EXT;
+            default:
+                throw new IllegalStateException("Unable to determine scan flag for "
+                        + partition.getFolder());
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getFolder().getAbsolutePath() + ":" + scanFlag;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a854aa0..b111bbf 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3089,8 +3089,7 @@
         // Read preferred apps from .../etc/preferred-apps directories.
         int size = PackageManagerService.SYSTEM_PARTITIONS.size();
         for (int index = 0; index < size; index++) {
-            PackageManagerService.ScanPartition partition =
-                    PackageManagerService.SYSTEM_PARTITIONS.get(index);
+            ScanPartition partition = PackageManagerService.SYSTEM_PARTITIONS.get(index);
 
             File preferredDir = new File(partition.getFolder(), "etc/preferred-apps");
             if (!preferredDir.exists() || !preferredDir.isDirectory()) {
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
index 3c499de..dae4038 100644
--- a/services/core/java/com/android/server/pm/VerificationParams.java
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -131,13 +131,12 @@
     private String mErrorMessage = null;
 
     final PackageLite mPackageLite;
-    final PackageManagerService mPm;
 
     VerificationParams(UserHandle user, File stagedDir, IPackageInstallObserver2 observer,
             PackageInstaller.SessionParams sessionParams, InstallSource installSource,
             int installerUid, SigningDetails signingDetails, int sessionId, PackageLite lite,
             PackageManagerService pm) {
-        super(user);
+        super(user, pm);
         mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
         mObserver = observer;
         mInstallFlags = sessionParams.installFlags;
@@ -155,7 +154,6 @@
                 ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
         mSessionId = sessionId;
         mPackageLite = lite;
-        mPm = pm;
     }
 
     @Override
@@ -168,7 +166,7 @@
         PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
                 mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
 
-        Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
+        Pair<Integer, String> ret = verifyReplacingVersionCode(
                 pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
         setReturnCode(ret.first, ret.second);
         if (mRet != INSTALL_SUCCEEDED) {
@@ -707,7 +705,7 @@
     public void verifyStage(List<VerificationParams> children)
             throws PackageManagerException {
         final MultiPackageVerificationParams params =
-                new MultiPackageVerificationParams(this, children);
+                new MultiPackageVerificationParams(this, children, mPm);
         mPm.mHandler.post(params::startCopy);
     }
 
@@ -720,9 +718,9 @@
         private final List<VerificationParams> mChildParams;
         private final Map<VerificationParams, Integer> mVerificationState;
 
-        MultiPackageVerificationParams(VerificationParams parent, List<VerificationParams> children)
-                throws PackageManagerException {
-            super(parent.getUser());
+        MultiPackageVerificationParams(VerificationParams parent, List<VerificationParams> children,
+                PackageManagerService pm) throws PackageManagerException {
+            super(parent.getUser(), pm);
             if (children.size() == 0) {
                 throw new PackageManagerException("No child sessions found!");
             }
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index 11aed8d..75f3725 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -476,9 +476,10 @@
             r.append("DUP:");
             r.append(permissionInfo.name);
         }
-        if (permission.isRuntime() && (ownerChanged || wasNonRuntime)) {
-            // If this is a runtime permission and the owner has changed, or this wasn't a runtime
-            // permission, then permission state should be cleaned up
+        if ((permission.isInternal() && ownerChanged)
+                || (permission.isRuntime() && (ownerChanged || wasNonRuntime))) {
+            // If this is an internal/runtime permission and the owner has changed, or this wasn't a
+            // runtime permission, then permission state should be cleaned up.
             permission.mDefinitionChanged = true;
         }
         if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
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 74497f7..904e889 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1655,7 +1655,8 @@
             isRolePermission = permission.isRole();
         }
         final boolean mayRevokeRolePermission = isRolePermission
-                && mayManageRolePermission(callingUid);
+                // Allow ourselves to revoke role permissions due to definition changes.
+                && (callingUid == Process.myUid() || mayManageRolePermission(callingUid));
 
         final boolean isRuntimePermission;
         synchronized (mLock) {
@@ -2333,11 +2334,13 @@
 
         for (int permNum = 0; permNum < numPermissions; permNum++) {
             final String permName = permissionsToRevoke.get(permNum);
+            final boolean isInternalPermission;
             synchronized (mLock) {
                 final Permission bp = mRegistry.getPermission(permName);
-                if (bp == null || !bp.isRuntime()) {
+                if (bp == null || !(bp.isInternal() || bp.isRuntime())) {
                     continue;
                 }
+                isInternalPermission = bp.isInternal();
             }
             mPackageManagerInt.forEachPackage(pkg -> {
                 final String packageName = pkg.getPackageName();
@@ -2357,12 +2360,18 @@
                     if (permissionState == PackageManager.PERMISSION_GRANTED
                             && (flags & flagMask) == 0) {
                         final int uid = UserHandle.getUid(userId, appId);
-                        EventLog.writeEvent(0x534e4554, "154505240", uid,
-                                "Revoking permission " + permName + " from package "
-                                        + packageName + " due to definition change");
-                        EventLog.writeEvent(0x534e4554, "168319670", uid,
-                                "Revoking permission " + permName + " from package "
-                                        + packageName + " due to definition change");
+                        if (isInternalPermission) {
+                            EventLog.writeEvent(0x534e4554, "195338390", uid,
+                                    "Revoking permission " + permName + " from package "
+                                            + packageName + " due to definition change");
+                        } else {
+                            EventLog.writeEvent(0x534e4554, "154505240", uid,
+                                    "Revoking permission " + permName + " from package "
+                                            + packageName + " due to definition change");
+                            EventLog.writeEvent(0x534e4554, "168319670", uid,
+                                    "Revoking permission " + permName + " from package "
+                                            + packageName + " due to definition change");
+                        }
                         Slog.e(TAG, "Revoking permission " + permName + " from package "
                                 + packageName + " due to definition change");
                         try {
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index c170603..47bd72a 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -23,6 +23,7 @@
 import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
 import android.media.audio.common.AidlConversion;
 import android.media.audio.common.AudioConfig;
+import android.media.audio.common.AudioConfigBase;
 import android.media.audio.common.AudioOffloadInfo;
 import android.media.soundtrigger.AudioCapabilities;
 import android.media.soundtrigger.ConfidenceLevel;
@@ -351,14 +352,8 @@
     AudioConfig hidl2aidlAudioConfig(
             @NonNull android.hardware.audio.common.V2_0.AudioConfig hidlConfig, boolean isInput) {
         AudioConfig aidlConfig = new AudioConfig();
-        aidlConfig.sampleRateHz = hidlConfig.sampleRateHz;
-        // Relies on the fact that HIDL AudioChannelMask uses the same constant values as
-        // system/audio.h.
-        aidlConfig.channelMask = AidlConversion.legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
-                hidlConfig.channelMask, isInput);
-        // Relies on the fact that HIDL AudioFormat uses the same constant values as system/audio.h.
-        aidlConfig.format = AidlConversion.legacy2aidl_audio_format_t_AudioFormatDescription(
-                hidlConfig.format);
+        aidlConfig.base = hidl2aidlAudioConfigBase(hidlConfig.sampleRateHz, hidlConfig.channelMask,
+                hidlConfig.format, isInput);
         aidlConfig.offloadInfo = hidl2aidlOffloadInfo(hidlConfig.offloadInfo);
         aidlConfig.frameCount = hidlConfig.frameCount;
         return aidlConfig;
@@ -368,26 +363,36 @@
     AudioOffloadInfo hidl2aidlOffloadInfo(
             @NonNull android.hardware.audio.common.V2_0.AudioOffloadInfo hidlInfo) {
         AudioOffloadInfo aidlInfo = new AudioOffloadInfo();
-        aidlInfo.sampleRateHz = hidlInfo.sampleRateHz;
-        // Relies on the fact that HIDL AudioChannelMask uses the same constant values as
-        // system/audio.h.
-        aidlInfo.channelMask = AidlConversion.legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
-                hidlInfo.channelMask, false /*isInput*/);
-        // Relies on the fact that HIDL AudioFormat uses the same constant values as system/audio.h.
-        aidlInfo.format = AidlConversion.legacy2aidl_audio_format_t_AudioFormatDescription(
-                hidlInfo.format);
+        aidlInfo.base = hidl2aidlAudioConfigBase(hidlInfo.sampleRateHz, hidlInfo.channelMask,
+                hidlInfo.format, false /*isInput*/);
         aidlInfo.streamType = AidlConversion.legacy2aidl_audio_stream_type_t_AudioStreamType(
                 hidlInfo.streamType);
         aidlInfo.bitRatePerSecond = hidlInfo.bitRatePerSecond;
-        aidlInfo.durationMicroseconds = hidlInfo.durationMicroseconds;
+        aidlInfo.durationUs = hidlInfo.durationMicroseconds;
         aidlInfo.hasVideo = hidlInfo.hasVideo;
         aidlInfo.isStreaming = hidlInfo.isStreaming;
         aidlInfo.bitWidth = hidlInfo.bitWidth;
-        aidlInfo.bufferSize = hidlInfo.bufferSize;
+        aidlInfo.offloadBufferSize = hidlInfo.bufferSize;
         aidlInfo.usage = AidlConversion.legacy2aidl_audio_usage_t_AudioUsage(hidlInfo.usage);
         return aidlInfo;
     }
 
+    // Ideally we would want to convert AudioConfigBase as a unit, however
+    // HIDL V2 lacks this type, so convert by field instead.
+    static @NonNull
+    AudioConfigBase hidl2aidlAudioConfigBase(int sampleRateHz, int channelMask, int format,
+            boolean isInput) {
+        AudioConfigBase aidlBase = new AudioConfigBase();
+        aidlBase.sampleRate = sampleRateHz;
+        // Relies on the fact that HIDL AudioChannelMask uses the same constant values as
+        // system/audio.h.
+        aidlBase.channelMask = AidlConversion.legacy2aidl_audio_channel_mask_t_AudioChannelLayout(
+                channelMask, isInput);
+        // Relies on the fact that HIDL AudioFormat uses the same constant values as system/audio.h.
+        aidlBase.format = AidlConversion.legacy2aidl_audio_format_t_AudioFormatDescription(format);
+        return aidlBase;
+    }
+
     @Nullable
     static ModelParameterRange hidl2aidlModelParameterRange(
             android.hardware.soundtrigger.V2_3.ModelParameterRange hidlRange) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index ff7e903..b58ca1f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -785,13 +785,14 @@
     @Override
     public void showAuthenticationDialog(PromptInfo promptInfo, IBiometricSysuiReceiver receiver,
             int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
-            int userId, String opPackageName, long operationId,
+            int userId, long operationId, String opPackageName, long requestId,
             @BiometricMultiSensorMode int multiSensorConfig) {
         enforceBiometricDialog();
         if (mBar != null) {
             try {
                 mBar.showAuthenticationDialog(promptInfo, receiver, sensorIds, credentialAllowed,
-                        requireConfirmation, userId, opPackageName, operationId, multiSensorConfig);
+                        requireConfirmation, userId, operationId, opPackageName, requestId,
+                        multiSensorConfig);
             } catch (RemoteException ex) {
             }
         }
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 83085cc..1ebb722 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -114,6 +114,8 @@
     private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
     private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
 
+    private final List<Message> mPendingTvinputInfoEvents = new LinkedList<>();
+
     // Calls to mListener should happen here.
     private final Handler mHandler = new ListenerHandler();
 
@@ -229,7 +231,16 @@
                             connection.getInputStateLocked(), 0, inputId).sendToTarget();
                     }
                 }
-            }
+            } else {
+                Message msg = mHandler.obtainMessage(ListenerHandler.TVINPUT_INFO_ADDED,
+                    deviceId, cableConnectionStatus, connection);
+                for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext();) {
+                    if (it.next().arg1 == deviceId) {
+                    it.remove();
+                    }
+                }
+                mPendingTvinputInfoEvents.add(msg);
+           }
             ITvInputHardwareCallback callback = connection.getCallbackLocked();
             if (callback != null) {
                 try {
@@ -288,6 +299,8 @@
             }
             mHardwareInputIdMap.put(deviceId, info.getId());
             mInputMap.put(info.getId(), info);
+            processPendingTvInputInfoEventsLocked();
+            Slog.d(TAG,"deviceId ="+ deviceId+", tvinputinfo = "+info);
 
             // Process pending state changes
 
@@ -530,6 +543,20 @@
         }
     }
 
+
+    private void processPendingTvInputInfoEventsLocked() {
+        for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext(); ) {
+            Message msg = it.next();
+            int deviceId =  msg.arg1;
+            String inputId = mHardwareInputIdMap.get(deviceId);
+            if (inputId != null) {
+                msg.sendToTarget();
+                it.remove();
+            }
+        }
+    }
+
+
     private void updateVolume() {
         mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
@@ -1182,6 +1209,7 @@
         private static final int HDMI_DEVICE_ADDED = 4;
         private static final int HDMI_DEVICE_REMOVED = 5;
         private static final int HDMI_DEVICE_UPDATED = 6;
+        private static final int TVINPUT_INFO_ADDED = 7;
 
         @Override
         public final void handleMessage(Message msg) {
@@ -1226,6 +1254,29 @@
                     }
                     break;
                 }
+                case TVINPUT_INFO_ADDED: {
+                    int deviceId = msg.arg1;
+                    int cableConnectionStatus = msg.arg2;
+                    Connection connection =(Connection)msg.obj;
+
+                    int previousConfigsLength = connection.getConfigsLengthLocked();
+                    int previousCableConnectionStatus = connection.getInputStateLocked();
+                    String inputId = mHardwareInputIdMap.get(deviceId);
+
+                    if (inputId != null) {
+                        if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) {
+                            if (previousCableConnectionStatus != connection.getInputStateLocked()) {
+                                mListener.onStateChanged(inputId, connection.getInputStateLocked());
+                            }
+                        } else {
+                            if ((previousConfigsLength == 0)
+                                    != (connection.getConfigsLengthLocked() == 0)) {
+                                mListener.onStateChanged(inputId, connection.getInputStateLocked());
+                            }
+                        }
+                    }
+                    break;
+                }
                 default: {
                     Slog.w(TAG, "Unhandled message: " + msg);
                     break;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 36a854e..2894708 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2304,10 +2304,9 @@
         public void requestChannelBrowsable(Uri channelUri, int userId)
                 throws RemoteException {
             final String callingPackageName = getCallingPackageName();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "requestChannelBrowsable");
             final long identity = Binder.clearCallingIdentity();
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                userId, "requestChannelBrowsable");
             try {
                 Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED);
                 List<ResolveInfo> list = getContext().getPackageManager()
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 0efc940..c5c0325 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -1380,8 +1380,8 @@
                     // At this point we have an externally controlled vibration playing already.
                     // Since the interface defines that only one externally controlled vibration can
                     // play at a time, we need to first mute the ongoing vibration and then return
-                    // a scale from this function for the new one. Ee can be assured that the
-                    // ongoing it will be muted in favor of the new vibration.
+                    // a scale from this function for the new one, so we can be assured that the
+                    // ongoing will be muted in favor of the new vibration.
                     //
                     // Note that this doesn't support multiple concurrent external controls, as we
                     // would need to mute the old one still if it came from a different controller.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e0b6f8d..3d9d1f8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2595,6 +2595,13 @@
         return task != null ? task.getOrganizedTask() : null;
     }
 
+    /** Returns the organized parent {@link TaskFragment}. */
+    @Nullable
+    TaskFragment getOrganizedTaskFragment() {
+        final TaskFragment parent = getTaskFragment();
+        return parent != null ? parent.getOrganizedTaskFragment() : null;
+    }
+
     @Override
     @Nullable
     TaskDisplayArea getDisplayArea() {
@@ -8413,7 +8420,9 @@
                 startFreezingScreenLocked(globalChanges);
             }
             forceNewConfig = false;
-            preserveWindow &= isResizeOnlyChange(changes);
+            // Do not preserve window if it is freezing screen because the original window won't be
+            // able to update drawn state that causes freeze timeout.
+            preserveWindow &= isResizeOnlyChange(changes) && !mFreezingScreen;
             final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
             if (hasResizeChange) {
                 final boolean isDragResizing = task.isDragResizing();
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 544cade..7a42351 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -86,6 +86,7 @@
 import android.view.WindowManager.TransitionOldType;
 import android.view.WindowManager.TransitionType;
 import android.view.animation.Animation;
+import android.window.ITaskFragmentOrganizer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
@@ -233,7 +234,11 @@
         final ActivityRecord topChangingApp =
                 getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
         final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
-        overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
+
+        // Check if there is any override
+        if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {
+            overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
+        }
 
         final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
                 || containsVoiceInteraction(mDisplayContent.mOpeningApps);
@@ -483,6 +488,7 @@
         return TYPE_NONE;
     }
 
+    @Nullable
     private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) {
         final WindowState mainWindow = activity != null ? activity.findMainWindow() : null;
         return mainWindow != null ? mainWindow.mAttrs : null;
@@ -506,6 +512,61 @@
     }
 
     /**
+     * Overrides the pending transition with the remote animation defined by the
+     * {@link ITaskFragmentOrganizer} if all windows in the transition are children of
+     * {@link TaskFragment} that are organized by the same organizer.
+     *
+     * @return {@code true} if the transition is overridden.
+     */
+    @VisibleForTesting
+    boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
+            ArraySet<Integer> activityTypes) {
+        final ArrayList<WindowContainer> allWindows = new ArrayList<>();
+        allWindows.addAll(mDisplayContent.mClosingApps);
+        allWindows.addAll(mDisplayContent.mOpeningApps);
+        allWindows.addAll(mDisplayContent.mChangingContainers);
+
+        // Find the common TaskFragmentOrganizer of all windows.
+        ITaskFragmentOrganizer organizer = null;
+        for (int i = allWindows.size() - 1; i >= 0; i--) {
+            final ActivityRecord r = getAppFromContainer(allWindows.get(i));
+            if (r == null) {
+                return false;
+            }
+            final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
+            final ITaskFragmentOrganizer curOrganizer = organizedTaskFragment != null
+                    ? organizedTaskFragment.getTaskFragmentOrganizer()
+                    : null;
+            if (curOrganizer == null) {
+                // All windows must below an organized TaskFragment.
+                return false;
+            }
+            if (organizer == null) {
+                organizer = curOrganizer;
+            } else if (!organizer.asBinder().equals(curOrganizer.asBinder())) {
+                // They must be controlled by the same organizer.
+                return false;
+            }
+        }
+
+        final RemoteAnimationDefinition definition = organizer != null
+                ? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
+                    .getRemoteAnimationDefinition(organizer)
+                : null;
+        final RemoteAnimationAdapter adapter = definition != null
+                ? definition.getAdapter(transit, activityTypes)
+                : null;
+        if (adapter == null) {
+            return false;
+        }
+        mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter);
+        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+                "Override with TaskFragment remote animation for transit=%s",
+                AppTransition.appTransitionOldToString(transit));
+        return true;
+    }
+
+    /**
      * Overrides the pending transition with the remote animation defined for the transition in the
      * set of defined remote animations in the app window token.
      */
@@ -524,13 +585,14 @@
     }
 
     static ActivityRecord getAppFromContainer(WindowContainer wc) {
-        return wc.asTask() != null ? wc.asTask().getTopNonFinishingActivity()
+        return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity()
                 : wc.asActivityRecord();
     }
 
     /**
      * @return The window token that determines the animation theme.
      */
+    @Nullable
     private ActivityRecord findAnimLayoutParamsToken(@TransitionOldType int transit,
             ArraySet<Integer> activityTypes) {
         ActivityRecord result;
@@ -543,7 +605,7 @@
                 w -> w.getRemoteAnimationDefinition() != null
                         && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
         if (result != null) {
-            return getAppFromContainer(result);
+            return result;
         }
         result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
                 w -> w.fillsParent() && w.findMainWindow() != null);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3627ad1..db5e3d0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3133,6 +3133,12 @@
         return mScreenRotationAnimation;
     }
 
+    /** If the display is in transition, there should be a screenshot covering it. */
+    @Override
+    boolean inTransition() {
+        return mScreenRotationAnimation != null || super.inTransition();
+    }
+
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
             @WindowTraceLogLevel int logLevel) {
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
index 92baadf..5e8d795 100644
--- a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
+++ b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
@@ -22,12 +22,12 @@
 import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
 import android.util.ArraySet;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
-import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0b44c80..f2e6abc 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -580,9 +580,12 @@
             if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
                 if (recentsAnimationController.updateInputConsumerForApp(
                         mRecentsAnimationInputConsumer.mWindowHandle)) {
-                    mRecentsAnimationInputConsumer.show(mInputTransaction,
-                            recentsAnimationController.getHighestLayerWindow());
-                    mAddRecentsAnimationInputConsumerHandle = false;
+                    final WindowState highestLayerWindow =
+                            recentsAnimationController.getHighestLayerWindow();
+                    if (highestLayerWindow != null) {
+                        mRecentsAnimationInputConsumer.show(mInputTransaction, highestLayerWindow);
+                        mAddRecentsAnimationInputConsumerHandle = false;
+                    }
                 }
             }
 
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index d440a14..d1460f4 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -729,7 +729,12 @@
                 mScreenshotRotationAnimator = null;
                 mRotateScreenAnimator = null;
                 mService.mAnimator.mBulkUpdateParams |= WindowSurfacePlacer.SET_UPDATE_ROTATION;
-                kill();
+                if (mDisplayContent.getRotationAnimation() == ScreenRotationAnimation.this) {
+                    // It also invokes kill().
+                    mDisplayContent.setRotationAnimation(null);
+                } else {
+                    kill();
+                }
                 mService.updateRotation(false, false);
             }
         }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 242693b..1b72826 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2014,6 +2014,8 @@
                 || mDisplayContent == null
                 || mTaskFragmentOrganizer == null
                 || getSurfaceControl() == null
+                // The change transition will be covered by display.
+                || mDisplayContent.inTransition()
                 || !isVisible()) {
             return false;
         }
@@ -2095,7 +2097,6 @@
     }
 
     @Nullable
-    @VisibleForTesting
     ITaskFragmentOrganizer getTaskFragmentOrganizer() {
         return mTaskFragmentOrganizer;
     }
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 690f67c..30d2a32 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -30,6 +30,7 @@
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Slog;
+import android.view.RemoteAnimationDefinition;
 import android.view.SurfaceControl;
 import android.window.ITaskFragmentOrganizer;
 import android.window.ITaskFragmentOrganizerController;
@@ -81,6 +82,13 @@
         private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
                 new WeakHashMap<>();
 
+        /**
+         * @see android.window.TaskFragmentOrganizer#registerRemoteAnimations(
+         * RemoteAnimationDefinition)
+         */
+        @Nullable
+        private RemoteAnimationDefinition mRemoteAnimationDefinition;
+
         TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer) {
             mOrganizer = organizer;
             try {
@@ -245,6 +253,61 @@
         }
     }
 
+    @Override
+    public void registerRemoteAnimations(ITaskFragmentOrganizer organizer,
+            RemoteAnimationDefinition definition) {
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        synchronized (mGlobalLock) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+                    "Register remote animations for organizer=%s uid=%d pid=%d",
+                    organizer.asBinder(), uid, pid);
+            final TaskFragmentOrganizerState organizerState =
+                    mTaskFragmentOrganizerState.get(organizer.asBinder());
+            if (organizerState == null) {
+                throw new IllegalStateException("The organizer hasn't been registered.");
+            }
+            if (organizerState.mRemoteAnimationDefinition != null) {
+                throw new IllegalStateException(
+                        "The organizer has already registered remote animations="
+                                + organizerState.mRemoteAnimationDefinition);
+            }
+
+            definition.setCallingPidUid(pid, uid);
+            organizerState.mRemoteAnimationDefinition = definition;
+        }
+    }
+
+    @Override
+    public void unregisterRemoteAnimations(ITaskFragmentOrganizer organizer) {
+        final int pid = Binder.getCallingPid();
+        final long uid = Binder.getCallingUid();
+        synchronized (mGlobalLock) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+                    "Unregister remote animations for organizer=%s uid=%d pid=%d",
+                    organizer.asBinder(), uid, pid);
+            final TaskFragmentOrganizerState organizerState =
+                    mTaskFragmentOrganizerState.get(organizer.asBinder());
+            if (organizerState == null) {
+                Slog.e(TAG, "The organizer hasn't been registered.");
+                return;
+            }
+
+            organizerState.mRemoteAnimationDefinition = null;
+        }
+    }
+
+    /** Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. */
+    @Nullable
+    public RemoteAnimationDefinition getRemoteAnimationDefinition(
+            ITaskFragmentOrganizer organizer) {
+        synchronized (mGlobalLock) {
+            final TaskFragmentOrganizerState organizerState =
+                    mTaskFragmentOrganizerState.get(organizer.asBinder());
+            return organizerState != null ? organizerState.mRemoteAnimationDefinition : null;
+        }
+    }
+
     void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
         final TaskFragmentOrganizerState state = validateAndGetState(organizer);
         if (!state.addTaskFragment(taskFragment)) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index b6c8e13..aec7cab 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -987,6 +987,10 @@
         return mDisplayContent != null && mDisplayContent.mChangingContainers.contains(this);
     }
 
+    boolean inTransition() {
+        return mWmService.mAtmService.getTransitionController().inTransition(this);
+    }
+
     void sendAppVisibilityToClients() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index a5ebf9a..bdbcd16 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -21,9 +21,9 @@
 
 import android.provider.AndroidDeviceConfig;
 import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 import java.util.Objects;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f24d74a..a373d8d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -206,6 +206,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.provider.DeviceConfigInterface;
 import android.provider.Settings;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
@@ -300,7 +301,6 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
 import com.android.server.power.ShutdownThread;
-import com.android.server.utils.DeviceConfigInterface;
 import com.android.server.utils.PriorityDump;
 
 import java.io.BufferedWriter;
@@ -8113,9 +8113,8 @@
             boolean animateStarting = false;
             while (timeoutRemaining > 0) {
                 // Waiting until all starting windows has finished animating.
-                animateStarting = mRoot.forAllActivities(a -> {
-                    return a.hasStartingWindow();
-                });
+                animateStarting = !mAtmService.getTransitionController().isShellTransitionsEnabled()
+                        && mRoot.forAllActivities(ActivityRecord::hasStartingWindow);
                 boolean isAnimating = mAnimator.isAnimationScheduled()
                         || mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL)
                         || animateStarting;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a94ad4a..bb9740b 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -337,7 +337,7 @@
     void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override;
     bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override;
     void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
-    void setPointerCapture(bool enabled) override;
+    void setPointerCapture(const PointerCaptureRequest& request) override;
     void notifyDropWindow(const sp<IBinder>& token, float x, float y) override;
 
     /* --- PointerControllerPolicyInterface implementation --- */
@@ -372,8 +372,8 @@
         // Show touches feature enable/disable.
         bool showTouches;
 
-        // Pointer capture feature enable/disable.
-        bool pointerCapture;
+        // The latest request to enable or disable Pointer Capture.
+        PointerCaptureRequest pointerCaptureRequest;
 
         // Sprite controller singleton, created on first use.
         sp<SpriteController> spriteController;
@@ -417,7 +417,6 @@
         mLocked.pointerSpeed = 0;
         mLocked.pointerGesturesEnabled = true;
         mLocked.showTouches = false;
-        mLocked.pointerCapture = false;
         mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
     }
     mInteractive = true;
@@ -446,7 +445,9 @@
         dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n",
                 toString(mLocked.pointerGesturesEnabled));
         dump += StringPrintf(INDENT "Show Touches: %s\n", toString(mLocked.showTouches));
-        dump += StringPrintf(INDENT "Pointer Capture Enabled: %s\n", toString(mLocked.pointerCapture));
+        dump += StringPrintf(INDENT "Pointer Capture: %s, seq=%" PRIu32 "\n",
+                             mLocked.pointerCaptureRequest.enable ? "Enabled" : "Disabled",
+                             mLocked.pointerCaptureRequest.seq);
     }
     dump += "\n";
 
@@ -634,7 +635,7 @@
 
         outConfig->showTouches = mLocked.showTouches;
 
-        outConfig->pointerCapture = mLocked.pointerCapture;
+        outConfig->pointerCaptureRequest = mLocked.pointerCaptureRequest;
 
         outConfig->setDisplayViewports(mLocked.viewports);
 
@@ -1383,16 +1384,16 @@
     checkAndClearExceptionFromCallback(env, "onPointerDownOutsideFocus");
 }
 
-void NativeInputManager::setPointerCapture(bool enabled) {
+void NativeInputManager::setPointerCapture(const PointerCaptureRequest& request) {
     { // acquire lock
         AutoMutex _l(mLock);
 
-        if (mLocked.pointerCapture == enabled) {
+        if (mLocked.pointerCaptureRequest == request) {
             return;
         }
 
-        ALOGV("%s pointer capture.", enabled ? "Enabling" : "Disabling");
-        mLocked.pointerCapture = enabled;
+        ALOGV("%s pointer capture.", request.enable ? "Enabling" : "Disabling");
+        mLocked.pointerCaptureRequest = request;
     } // release lock
 
     mInputManager->getReader()->requestRefreshConfiguration(
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 0bd737b..957d7c3 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -65,7 +65,6 @@
         "libincremental_manager_aidl-cpp",
         "libprotobuf-cpp-lite",
         "service.incremental.proto",
-        "libutils",
         "libvold_binder",
         "libc++fs",
         "libziparchive_for_incfs",
@@ -78,6 +77,7 @@
         "libincfs",
         "liblog",
         "libpermission",
+        "libutils",
         "libz",
     ],
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 46adb32..fe3a77e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -440,7 +440,6 @@
     private static final String SYSPROP_START_UPTIME = "sys.system_server.start_uptime";
 
     private Future<?> mZygotePreload;
-    private Future<?> mBlobStoreServiceStart;
 
     private final SystemServerDumper mDumper = new SystemServerDumper();
 
@@ -2262,12 +2261,9 @@
                 t.traceEnd();
             }
 
-            mBlobStoreServiceStart = SystemServerInitThreadPool.submit(() -> {
-                final TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
-                traceLog.traceBegin(START_BLOB_STORE_SERVICE);
-                mSystemServiceManager.startService(BLOB_STORE_MANAGER_SERVICE_CLASS);
-                traceLog.traceEnd();
-            }, START_BLOB_STORE_SERVICE);
+            t.traceBegin(START_BLOB_STORE_SERVICE);
+            mSystemServiceManager.startService(BLOB_STORE_MANAGER_SERVICE_CLASS);
+            t.traceEnd();
 
             // Dreams (interactive idle-time views, a/k/a screen savers, and doze mode)
             t.traceBegin("StartDreamManager");
@@ -2673,9 +2669,6 @@
         mSystemServiceManager.startService(APP_COMPAT_OVERRIDES_SERVICE_CLASS);
         t.traceEnd();
 
-        ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart,
-                START_BLOB_STORE_SERVICE);
-
         // These are needed to propagate to the runnable below.
         final NetworkManagementService networkManagementF = networkManagement;
         final NetworkStatsService networkStatsF = networkStats;
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 3c97c95..9d89f12 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -103,7 +103,7 @@
     val dataAppDirectory: File =
             File(Files.createTempDirectory("data").toFile(), "app")
     val frameworkSignature: SigningDetails = SigningDetails(arrayOf(generateSpySignature()), 3)
-    val systemPartitions: List<PackageManagerService.ScanPartition> =
+    val systemPartitions: List<ScanPartition> =
             redirectScanPartitions(PackageManagerService.SYSTEM_PARTITIONS)
     val session: StaticMockitoSession
 
@@ -476,7 +476,7 @@
     }
 
     /** Finds the appropriate partition, if available, based on a scan flag unique to it.  */
-    fun getPartitionFromFlag(scanFlagMask: Int): PackageManagerService.ScanPartition =
+    fun getPartitionFromFlag(scanFlagMask: Int): ScanPartition =
             systemPartitions.first { (it.scanFlag and scanFlagMask) != 0 }
 
     @Throws(Exception::class)
@@ -630,11 +630,11 @@
     /** Override get*Folder methods to point to temporary local directories  */
 
     @Throws(IOException::class)
-    private fun redirectScanPartitions(partitions: List<PackageManagerService.ScanPartition>):
-            List<PackageManagerService.ScanPartition> {
-        val spiedPartitions: MutableList<PackageManagerService.ScanPartition> =
+    private fun redirectScanPartitions(partitions: List<ScanPartition>):
+            List<ScanPartition> {
+        val spiedPartitions: MutableList<ScanPartition> =
                 ArrayList(partitions.size)
-        for (partition: PackageManagerService.ScanPartition in partitions) {
+        for (partition: ScanPartition in partitions) {
             val spy = spy(partition)
             val newRoot = Files.createTempDirectory(partition.folder.name).toFile()
             whenever(spy.overlayFolder).thenReturn(File(newRoot, "overlay"))
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
index 7a6110b..f17fa62 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
@@ -79,8 +79,8 @@
         mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
         mockAllowList(packageSetting2, allowList(10001, 10002, 10003))
 
-        pms.sendPackagesSuspendedForUser(
-                packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
         verify(pms).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
                 anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
 
@@ -97,8 +97,8 @@
         mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
         mockAllowList(packageSetting2, allowList(10001, 10002, 10007))
 
-        pms.sendPackagesSuspendedForUser(
-                packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
         verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
                 anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
 
@@ -118,8 +118,8 @@
         mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
         mockAllowList(packageSetting2, null)
 
-        pms.sendPackagesSuspendedForUser(
-                packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
         verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
                 anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
 
@@ -133,6 +133,22 @@
         }
     }
 
+    @Test
+    @Throws(Exception::class)
+    fun sendPackagesSuspendModifiedForUser() {
+        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+        verify(pms).sendPackageBroadcast(
+                eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
+                anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
+
+        var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+        var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+        assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        assertThat(modifiedUids).asList().containsExactly(
+                packageSetting1.appId, packageSetting2.appId)
+    }
+
     private fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply {
         this.put(TEST_USER_ID, uids)
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
new file mode 100644
index 0000000..dd6b744
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.tare;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.AlarmManager;
+import android.content.Context;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/** Tests various aspects of the Agent. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AgentTest {
+    private MockitoSession mMockingSession;
+    @Mock
+    private CompleteEconomicPolicy mEconomicPolicy;
+    @Mock
+    private Context mContext;
+    @Mock
+    private InternalResourceService mIrs;
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .mockStatic(LocalServices.class)
+                .startMocking();
+        when(mIrs.getContext()).thenReturn(mContext);
+        when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mock(AlarmManager.class));
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    @Test
+    public void testRecordTransaction_UnderMax() {
+        Agent agent = new Agent(mIrs, mEconomicPolicy);
+        Ledger ledger = new Ledger();
+
+        doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
+        doReturn(1_000_000L).when(mEconomicPolicy).getMaxSatiatedBalance();
+
+        Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(5, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(1000, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(500, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(1_000_000L, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, -1_000_001L);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(-1, ledger.getCurrentBalance());
+    }
+
+    @Test
+    public void testRecordTransaction_MaxCirculation() {
+        Agent agent = new Agent(mIrs, mEconomicPolicy);
+        Ledger ledger = new Ledger();
+
+        doReturn(1000L).when(mIrs).getMaxCirculationLocked();
+        doReturn(1000L).when(mEconomicPolicy).getMaxSatiatedBalance();
+
+        Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(5, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(1000, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(500, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, 2000);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(1000, ledger.getCurrentBalance());
+
+        // MaxCirculation can change as the battery level changes. Any already allocated ARCSs
+        // shouldn't be removed by recordTransaction().
+        doReturn(900L).when(mIrs).getMaxCirculationLocked();
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, 100);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(1000, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, -50);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(950, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, -200);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(750, ledger.getCurrentBalance());
+
+        doReturn(800L).when(mIrs).getMaxCirculationLocked();
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, 100);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(800, ledger.getCurrentBalance());
+    }
+
+    @Test
+    public void testRecordTransaction_MaxSatiatedBalance() {
+        Agent agent = new Agent(mIrs, mEconomicPolicy);
+        Ledger ledger = new Ledger();
+
+        doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
+        doReturn(1000L).when(mEconomicPolicy).getMaxSatiatedBalance();
+
+        Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(5, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(1000, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(500, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(1_000, ledger.getCurrentBalance());
+
+        // Shouldn't change in normal operation, but adding test case in case it does.
+        doReturn(900L).when(mEconomicPolicy).getMaxSatiatedBalance();
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, 500);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(1_000, ledger.getCurrentBalance());
+
+        transaction = new Ledger.Transaction(0, 0, 0, null, -1001);
+        agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
+        assertEquals(-1, ledger.getCurrentBalance());
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS b/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS
new file mode 100644
index 0000000..217a5ed
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS
@@ -0,0 +1 @@
+include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS
\ No newline at end of file
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index c19155f..11e1230 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -144,7 +144,6 @@
         "utils/**/*.java",
         "utils/**/*.kt",
         "utils-mockito/**/*.kt",
-        ":services.core-sources-deviceconfig-interface",
     ],
     static_libs: [
         "junit",
@@ -161,7 +160,6 @@
         "utils/**/*.java",
         "utils/**/*.kt",
         "utils-mockito/**/*.kt",
-        ":services.core-sources-deviceconfig-interface",
     ],
     static_libs: [
         "junit",
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
index 0d475c0..91bf4d1 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -135,7 +135,8 @@
                         "schema1",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false,
-                /*schemaVersion=*/ 0);
+                /*schemaVersion=*/ 0,
+                /*setSchemaStatsBuilder=*/ null);
 
         // "schema1" is platform hidden now and package visible to package1
         assertThat(mVisibilityStore.isSchemaSearchableByCaller(
@@ -167,7 +168,8 @@
                         "schema1",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false,
-                /*schemaVersion=*/ 0);
+                /*schemaVersion=*/ 0,
+                /*setSchemaStatsBuilder=*/ null);
 
         // Check that "schema1" still has the same visibility settings
         SystemUtil.runWithShellPermissionIdentity(() -> assertThat(
@@ -241,7 +243,8 @@
                         "schema1",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false,
-                /*schemaVersion=*/ 0);
+                /*schemaVersion=*/ 0,
+                /*setSchemaStatsBuilder=*/ null);
 
         // "schema1" is platform hidden now and package accessible
         assertThat(mVisibilityStore.isSchemaSearchableByCaller(
@@ -269,7 +272,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ true,
-                /*schemaVersion=*/ 0);
+                /*schemaVersion=*/ 0,
+                /*setSchemaStatsBuilder=*/ null);
 
         // Check that "schema1" is no longer considered platform hidden or package accessible
         assertThat(mVisibilityStore.isSchemaSearchableByCaller(
@@ -298,7 +302,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*schemaVersion=*/ 0);
+                /*schemaVersion=*/ 0,
+                /*setSchemaStatsBuilder=*/ null);
 
         assertThat(mVisibilityStore.isSchemaSearchableByCaller(
                 "package",
@@ -333,7 +338,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*schemaVersion=*/ 0);
+                /*schemaVersion=*/ 0,
+                /*setSchemaStatsBuilder=*/ null);
 
         assertThat(mVisibilityStore.isSchemaSearchableByCaller(
                 "package",
@@ -361,7 +367,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.singletonList("Schema"),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*schemaVersion=*/ 0);
+                /*schemaVersion=*/ 0,
+                /*setSchemaStatsBuilder=*/ null);
 
         assertThat(mVisibilityStore.isSchemaSearchableByCaller(
                 "package",
@@ -390,7 +397,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*schemaVersion=*/ 0);
+                /*schemaVersion=*/ 0,
+                /*setSchemaStatsBuilder=*/ null);
         assertThat(mVisibilityStore
                                 .isSchemaSearchableByCaller(
                                         "package",
@@ -431,7 +439,8 @@
                         "Schema",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false,
-                /*schemaVersion=*/ 0);
+                /*schemaVersion=*/ 0,
+                /*setSchemaStatsBuilder=*/ null);
         assertThat(mVisibilityStore
                                 .isSchemaSearchableByCaller(
                                         "package",
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index f40a5ad..dd3b3ec 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -76,13 +76,14 @@
 import java.util.Set;
 
 public class AppSearchImplTest {
-    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-    private AppSearchImpl mAppSearchImpl;
     /**
      * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
      */
     private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true;
 
+    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+    private AppSearchImpl mAppSearchImpl;
+
     @Before
     public void setUp() throws Exception {
         mAppSearchImpl =
@@ -439,7 +440,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert a document and then remove it to generate garbage.
         GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
@@ -499,7 +501,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert a valid doc
         GenericDocument validDoc =
@@ -591,7 +594,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert a valid doc
         appSearchImpl.putDocument(
@@ -626,7 +630,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert document
         GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
@@ -660,7 +665,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         mAppSearchImpl.setSchema(
                 "package",
                 "database2",
@@ -669,7 +675,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert documents
         GenericDocument document1 =
@@ -714,7 +721,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert document
         GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
@@ -756,7 +764,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert package2 schema
         List<AppSearchSchema> schema2 =
@@ -769,7 +778,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert package1 document
         GenericDocument document =
@@ -812,7 +822,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert package2 schema
         List<AppSearchSchema> schema2 =
@@ -825,7 +836,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert package1 document
         GenericDocument document =
@@ -889,7 +901,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert two package1 documents
         GenericDocument document1 =
@@ -914,7 +927,8 @@
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
 
         long nextPageToken = searchResultPage.getNextPageToken();
-        searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+        searchResultPage =
+                mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
     }
@@ -932,7 +946,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert two package1 documents
         GenericDocument document1 =
@@ -962,14 +977,17 @@
         AppSearchException e =
                 assertThrows(
                         AppSearchException.class,
-                        () -> mAppSearchImpl.getNextPage("package2", nextPageToken));
+                        () ->
+                                mAppSearchImpl.getNextPage(
+                                        "package2", nextPageToken, /*statsBuilder=*/ null));
         assertThat(e)
                 .hasMessageThat()
                 .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
         assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
 
         // Can continue getting next page for package1
-        searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+        searchResultPage =
+                mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
     }
@@ -987,7 +1005,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert two package1 documents
         GenericDocument document1 =
@@ -1019,7 +1038,8 @@
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
 
         long nextPageToken = searchResultPage.getNextPageToken();
-        searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+        searchResultPage =
+                mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
     }
@@ -1037,7 +1057,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert two package1 documents
         GenericDocument document1 =
@@ -1074,14 +1095,17 @@
         AppSearchException e =
                 assertThrows(
                         AppSearchException.class,
-                        () -> mAppSearchImpl.getNextPage("package2", nextPageToken));
+                        () ->
+                                mAppSearchImpl.getNextPage(
+                                        "package2", nextPageToken, /*statsBuilder=*/ null));
         assertThat(e)
                 .hasMessageThat()
                 .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken);
         assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
 
         // Can continue getting next page for package1
-        searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+        searchResultPage =
+                mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
     }
@@ -1099,7 +1123,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert two package1 documents
         GenericDocument document1 =
@@ -1132,7 +1157,9 @@
         AppSearchException e =
                 assertThrows(
                         AppSearchException.class,
-                        () -> mAppSearchImpl.getNextPage("package1", nextPageToken));
+                        () ->
+                                mAppSearchImpl.getNextPage(
+                                        "package1", nextPageToken, /*statsBuilder=*/ null));
         assertThat(e)
                 .hasMessageThat()
                 .contains("Package \"package1\" cannot use nextPageToken: " + nextPageToken);
@@ -1152,7 +1179,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert two package1 documents
         GenericDocument document1 =
@@ -1189,7 +1217,8 @@
         assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
 
         // Can continue getting next page for package1
-        searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+        searchResultPage =
+                mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
     }
@@ -1207,7 +1236,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert two package1 documents
         GenericDocument document1 =
@@ -1247,7 +1277,9 @@
         AppSearchException e =
                 assertThrows(
                         AppSearchException.class,
-                        () -> mAppSearchImpl.getNextPage("package1", nextPageToken));
+                        () ->
+                                mAppSearchImpl.getNextPage(
+                                        "package1", nextPageToken, /*statsBuilder=*/ null));
         assertThat(e)
                 .hasMessageThat()
                 .contains("Package \"package1\" cannot use nextPageToken: " + nextPageToken);
@@ -1267,7 +1299,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert two package1 documents
         GenericDocument document1 =
@@ -1311,7 +1344,8 @@
         assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
 
         // Can continue getting next page for package1
-        searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken);
+        searchResultPage =
+                mAppSearchImpl.getNextPage("package1", nextPageToken, /*statsBuilder=*/ null);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
     }
@@ -1355,7 +1389,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Create expected schemaType proto.
         SchemaProto expectedProto =
@@ -1400,7 +1435,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Create incompatible schema
         List<AppSearchSchema> newSchemas =
@@ -1416,7 +1452,8 @@
                         /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                         /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                         /*forceOverride=*/ true,
-                        /*version=*/ 0);
+                        /*version=*/ 0,
+                        /* setSchemaStatsBuilder= */ null);
         assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Text");
         assertThat(setSchemaResponse.getIncompatibleTypes()).containsExactly("Email");
     }
@@ -1439,7 +1476,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Create expected schemaType proto.
         SchemaProto expectedProto =
@@ -1472,8 +1510,8 @@
                         /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                         /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                         /*forceOverride=*/ false,
-                        /*version=*/ 0);
-
+                        /*version=*/ 0,
+                        /* setSchemaStatsBuilder= */ null);
         // Check the Document type has been deleted.
         assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Document");
 
@@ -1486,7 +1524,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ true,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Check Document schema is removed.
         expectedProto =
@@ -1524,7 +1563,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         mAppSearchImpl.setSchema(
                 "package",
                 "database2",
@@ -1533,7 +1573,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Create expected schemaType proto.
         SchemaProto expectedProto =
@@ -1573,7 +1614,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ true,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Create expected schemaType list, database 1 should only contain Email but database 2
         // remains in same.
@@ -1618,7 +1660,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert package document
         GenericDocument document =
@@ -1680,7 +1723,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         mAppSearchImpl.setSchema(
                 "packageB",
                 "database",
@@ -1689,7 +1733,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Verify these two packages is stored in AppSearch
         SchemaProto expectedProto =
@@ -1735,7 +1780,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         assertThat(mAppSearchImpl.getPackageToDatabases())
                 .containsExactlyEntriesIn(expectedMapping);
 
@@ -1749,7 +1795,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         assertThat(mAppSearchImpl.getPackageToDatabases())
                 .containsExactlyEntriesIn(expectedMapping);
 
@@ -1763,7 +1810,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         assertThat(mAppSearchImpl.getPackageToDatabases())
                 .containsExactlyEntriesIn(expectedMapping);
     }
@@ -1822,7 +1870,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert two docs
         GenericDocument document1 =
@@ -1973,7 +2022,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Since "package1" doesn't have a document, it get any space attributed to it.
         StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForPackage("package1");
@@ -1996,7 +2046,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert document for "package1"
         GenericDocument document =
@@ -2012,7 +2063,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert two documents for "package2"
         document = new GenericDocument.Builder<>("namespace", "id1", "type").build();
@@ -2061,7 +2113,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // "package2" doesn't exist yet, so it shouldn't have any storage size
         StorageInfo storageInfo =
@@ -2084,7 +2137,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Since "package1", "database1" doesn't have a document, it get any space attributed to it.
         StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForDatabase("package1", "database1");
@@ -2106,7 +2160,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         mAppSearchImpl.setSchema(
                 "package1",
                 "database2",
@@ -2115,7 +2170,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Add a document for "package1", "database1"
         GenericDocument document =
@@ -2165,7 +2221,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         appSearchImpl.close();
 
@@ -2181,7 +2238,8 @@
                                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                                 /*forceOverride=*/ false,
-                                /*version=*/ 0));
+                                /*version=*/ 0,
+                                /* setSchemaStatsBuilder= */ null));
 
         assertThrows(
                 IllegalStateException.class, () -> appSearchImpl.getSchema("package", "database"));
@@ -2225,7 +2283,9 @@
 
         assertThrows(
                 IllegalStateException.class,
-                () -> appSearchImpl.getNextPage("package", /*nextPageToken=*/ 1L));
+                () ->
+                        appSearchImpl.getNextPage(
+                                "package", /*nextPageToken=*/ 1L, /*statsBuilder=*/ null));
 
         assertThrows(
                 IllegalStateException.class,
@@ -2296,7 +2356,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Add a document and persist it.
         GenericDocument document =
@@ -2343,7 +2404,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Add two documents and persist them.
         GenericDocument document1 =
@@ -2423,7 +2485,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Add two documents and persist them.
         GenericDocument document1 =
@@ -2511,7 +2574,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Add two documents
         GenericDocument document1 =
@@ -2562,7 +2626,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert a document which is too large
         GenericDocument document =
@@ -2636,7 +2701,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Index a document
         mAppSearchImpl.putDocument(
@@ -2723,7 +2789,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Index 3 documents
         mAppSearchImpl.putDocument(
@@ -2836,7 +2903,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         mAppSearchImpl.setSchema(
                 "package1",
                 "database2",
@@ -2845,7 +2913,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         mAppSearchImpl.setSchema(
                 "package2",
                 "database1",
@@ -2854,7 +2923,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         mAppSearchImpl.setSchema(
                 "package2",
                 "database2",
@@ -2863,7 +2933,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Index documents in package1/database1
         mAppSearchImpl.putDocument(
@@ -3002,7 +3073,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Index 3 documents
         mAppSearchImpl.putDocument(
@@ -3131,7 +3203,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Index a document
         mAppSearchImpl.putDocument(
@@ -3210,7 +3283,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Index a document
         mAppSearchImpl.putDocument(
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
index 7c97687..2ab5fd5 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
@@ -33,6 +33,7 @@
 import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
 import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
 import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
 import com.android.server.appsearch.icing.proto.DeleteStatsProto;
 import com.android.server.appsearch.icing.proto.DocumentProto;
 import com.android.server.appsearch.icing.proto.InitializeStatsProto;
@@ -41,6 +42,7 @@
 import com.android.server.appsearch.icing.proto.PutResultProto;
 import com.android.server.appsearch.icing.proto.QueryStatsProto;
 import com.android.server.appsearch.icing.proto.ScoringSpecProto;
+import com.android.server.appsearch.icing.proto.SetSchemaResultProto;
 import com.android.server.appsearch.icing.proto.StatusProto;
 import com.android.server.appsearch.icing.proto.TermMatchType;
 
@@ -57,14 +59,17 @@
 import java.util.List;
 
 public class AppSearchLoggerTest {
-    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-    private AppSearchImpl mAppSearchImpl;
-    private TestLogger mLogger;
+    private static final String PACKAGE_NAME = "packageName";
+    private static final String DATABASE = "database";
     /**
      * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
      */
     private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true;
 
+    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+    private AppSearchImpl mAppSearchImpl;
+    private TestLogger mLogger;
+
     @Before
     public void setUp() throws Exception {
         mAppSearchImpl =
@@ -84,6 +89,7 @@
         @Nullable SearchStats mSearchStats;
         @Nullable RemoveStats mRemoveStats;
         @Nullable OptimizeStats mOptimizeStats;
+        @Nullable SetSchemaStats mSetSchemaStats;
 
         @Override
         public void logStats(@NonNull CallStats stats) {
@@ -114,6 +120,11 @@
         public void logStats(@NonNull OptimizeStats stats) {
             mOptimizeStats = stats;
         }
+
+        @Override
+        public void logStats(@NonNull SetSchemaStats stats) {
+            mSetSchemaStats = stats;
+        }
     }
 
     @Test
@@ -194,7 +205,7 @@
                                         .setExceededMaxTokenNum(nativeExceededMaxNumTokens)
                                         .build())
                         .build();
-        PutDocumentStats.Builder pBuilder = new PutDocumentStats.Builder("packageName", "database");
+        PutDocumentStats.Builder pBuilder = new PutDocumentStats.Builder(PACKAGE_NAME, DATABASE);
 
         AppSearchLoggerHelper.copyNativeStats(nativePutDocumentStats, pBuilder);
 
@@ -248,8 +259,8 @@
                         .setDocumentRetrievalLatencyMs(nativeDocumentRetrievingLatencyMillis)
                         .build();
         SearchStats.Builder qBuilder =
-                new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, "packageName")
-                        .setDatabase("database");
+                new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, PACKAGE_NAME)
+                        .setDatabase(DATABASE);
 
         AppSearchLoggerHelper.copyNativeStats(nativeQueryStats, qBuilder);
 
@@ -336,6 +347,35 @@
                 .isEqualTo(nativeTimeSinceLastOptimizeMillis);
     }
 
+    @Test
+    public void testAppSearchLoggerHelper_testCopyNativeStats_setSchema() {
+        ImmutableList<String> newSchemaTypeChangeList = ImmutableList.of("new1");
+        ImmutableList<String> deletedSchemaTypesList = ImmutableList.of("deleted1", "deleted2");
+        ImmutableList<String> compatibleTypesList = ImmutableList.of("compatible1", "compatible2");
+        ImmutableList<String> indexIncompatibleTypeChangeList = ImmutableList.of("index1");
+        ImmutableList<String> backwardsIncompatibleTypeChangeList = ImmutableList.of("backwards1");
+        SetSchemaResultProto setSchemaResultProto =
+                SetSchemaResultProto.newBuilder()
+                        .addAllNewSchemaTypes(newSchemaTypeChangeList)
+                        .addAllDeletedSchemaTypes(deletedSchemaTypesList)
+                        .addAllFullyCompatibleChangedSchemaTypes(compatibleTypesList)
+                        .addAllIndexIncompatibleChangedSchemaTypes(indexIncompatibleTypeChangeList)
+                        .addAllIncompatibleSchemaTypes(backwardsIncompatibleTypeChangeList)
+                        .build();
+        SetSchemaStats.Builder sBuilder = new SetSchemaStats.Builder(PACKAGE_NAME, DATABASE);
+
+        AppSearchLoggerHelper.copyNativeStats(setSchemaResultProto, sBuilder);
+
+        SetSchemaStats sStats = sBuilder.build();
+        assertThat(sStats.getNewTypeCount()).isEqualTo(newSchemaTypeChangeList.size());
+        assertThat(sStats.getDeletedTypeCount()).isEqualTo(deletedSchemaTypesList.size());
+        assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(compatibleTypesList.size());
+        assertThat(sStats.getIndexIncompatibleTypeChangeCount())
+                .isEqualTo(indexIncompatibleTypeChangeList.size());
+        assertThat(sStats.getBackwardsIncompatibleTypeChangeCount())
+                .isEqualTo(backwardsIncompatibleTypeChangeList.size());
+    }
+
     //
     // Testing actual logging
     //
@@ -388,7 +428,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build();
         GenericDocument doc2 = new GenericDocument.Builder<>("namespace", "id2", "Type1").build();
         appSearchImpl.putDocument(testPackageName, testDatabase, doc1, mLogger);
@@ -439,7 +480,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         // Insert a valid doc
         GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build();
@@ -495,7 +537,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         GenericDocument document =
                 new GenericDocument.Builder<>("namespace", "id", "type")
@@ -542,7 +585,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         GenericDocument document =
                 new GenericDocument.Builder<>("namespace", "id", "type")
@@ -592,7 +636,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         GenericDocument document1 =
                 new GenericDocument.Builder<>("namespace", "id1", "type")
                         .setPropertyString("subject", "testPut example1")
@@ -661,7 +706,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         SearchSpec searchSpec =
                 new SearchSpec.Builder()
@@ -701,7 +747,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         GenericDocument document =
                 new GenericDocument.Builder<>(testNamespace, testId, "type").build();
         mAppSearchImpl.putDocument(testPackageName, testDatabase, document, /*logger=*/ null);
@@ -735,7 +782,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
 
         GenericDocument document =
                 new GenericDocument.Builder<>(testNamespace, testId, "type").build();
@@ -780,7 +828,8 @@
                 /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
                 /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
-                /*version=*/ 0);
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
         GenericDocument document1 =
                 new GenericDocument.Builder<>(testNamespace, "id1", "type").build();
         GenericDocument document2 =
@@ -800,7 +849,58 @@
         assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
         assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
         // delete by query
-        assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.QUERY_VALUE);
+        assertThat(rStats.getDeleteType())
+                .isEqualTo(DeleteStatsProto.DeleteType.Code.DEPRECATED_QUERY_VALUE);
         assertThat(rStats.getDeletedDocumentCount()).isEqualTo(2);
     }
+
+    @Test
+    public void testLoggingStats_setSchema() throws Exception {
+        AppSearchSchema schema1 =
+                new AppSearchSchema.Builder("testSchema")
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("subject")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+        mAppSearchImpl.setSchema(
+                PACKAGE_NAME,
+                DATABASE,
+                Collections.singletonList(schema1),
+                /*visibilityStore=*/ null,
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false,
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
+
+        // create a backwards incompatible schema
+        SetSchemaStats.Builder sStatsBuilder = new SetSchemaStats.Builder(PACKAGE_NAME, DATABASE);
+        AppSearchSchema schema2 = new AppSearchSchema.Builder("testSchema").build();
+        mAppSearchImpl.setSchema(
+                PACKAGE_NAME,
+                DATABASE,
+                Collections.singletonList(schema2),
+                /*visibilityStore=*/ null,
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false,
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ sStatsBuilder);
+
+        SetSchemaStats sStats = sStatsBuilder.build();
+        assertThat(sStats.getPackageName()).isEqualTo(PACKAGE_NAME);
+        assertThat(sStats.getDatabase()).isEqualTo(DATABASE);
+        assertThat(sStats.getNewTypeCount()).isEqualTo(0);
+        assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(0);
+        assertThat(sStats.getIndexIncompatibleTypeChangeCount()).isEqualTo(1);
+        assertThat(sStats.getBackwardsIncompatibleTypeChangeCount()).isEqualTo(1);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
index c1dc0e4..81aab41 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
@@ -264,17 +264,15 @@
                         .setMigratedDocumentCount(6)
                         .setSavedDocumentCount(7)
                         .build();
-        int nativeLatencyMillis = 1;
-        int newTypeCount = 2;
-        int compatibleTypeChangeCount = 3;
-        int indexIncompatibleTypeChangeCount = 4;
-        int backwardsIncompatibleTypeChangeCount = 5;
+        int newTypeCount = 1;
+        int compatibleTypeChangeCount = 2;
+        int indexIncompatibleTypeChangeCount = 3;
+        int backwardsIncompatibleTypeChangeCount = 4;
         SetSchemaStats sStats =
                 new SetSchemaStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
                         .setStatusCode(TEST_STATUS_CODE)
                         .setSchemaMigrationStats(schemaMigrationStats)
                         .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
-                        .setNativeLatencyMillis(nativeLatencyMillis)
                         .setNewTypeCount(newTypeCount)
                         .setCompatibleTypeChangeCount(compatibleTypeChangeCount)
                         .setIndexIncompatibleTypeChangeCount(indexIncompatibleTypeChangeCount)
@@ -287,7 +285,6 @@
         assertThat(sStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
         assertThat(sStats.getSchemaMigrationStats()).isEqualTo(schemaMigrationStats);
         assertThat(sStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
-        assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
         assertThat(sStats.getNewTypeCount()).isEqualTo(newTypeCount);
         assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(compatibleTypeChangeCount);
         assertThat(sStats.getIndexIncompatibleTypeChangeCount())
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 2892bf5..b3f7587 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -74,6 +74,7 @@
 public class AuthSessionTest {
 
     private static final String TEST_PACKAGE = "test_package";
+    private static final long TEST_REQUEST_ID = 22;
 
     @Mock private Context mContext;
     @Mock private ITrustManager mTrustManager;
@@ -112,6 +113,7 @@
         final AuthSession session = createAuthSession(mSensors,
                 false /* checkDevicePolicyManager */,
                 Authenticators.BIOMETRIC_STRONG,
+                TEST_REQUEST_ID,
                 0 /* operationId */,
                 0 /* userId */);
 
@@ -133,6 +135,7 @@
         final AuthSession session = createAuthSession(mSensors,
                 false /* checkDevicePolicyManager */,
                 Authenticators.BIOMETRIC_STRONG,
+                TEST_REQUEST_ID,
                 operationId,
                 userId);
         assertEquals(mSensors.size(), session.mPreAuthInfo.eligibleSensors.size());
@@ -153,6 +156,7 @@
                     eq(userId),
                     eq(mSensorReceiver),
                     eq(TEST_PACKAGE),
+                    eq(TEST_REQUEST_ID),
                     eq(sensor.getCookie()),
                     anyBoolean() /* allowBackgroundAuthentication */);
         }
@@ -185,6 +189,33 @@
     }
 
     @Test
+    public void testCancelReducesAppetiteForCookies() throws Exception {
+        setupFace(0 /* id */, false /* confirmationAlwaysRequired */,
+                mock(IBiometricAuthenticator.class));
+        setupFingerprint(1 /* id */, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
+
+        final AuthSession session = createAuthSession(mSensors,
+                false /* checkDevicePolicyManager */,
+                Authenticators.BIOMETRIC_STRONG,
+                TEST_REQUEST_ID,
+                44 /* operationId */,
+                2 /* userId */);
+
+        session.goToInitialState();
+
+        for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+            assertEquals(BiometricSensor.STATE_WAITING_FOR_COOKIE, sensor.getSensorState());
+        }
+
+        session.onCancelAuthSession(false /* force */);
+
+        for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+            session.onCookieReceived(sensor.getCookie());
+            assertEquals(BiometricSensor.STATE_CANCELING, sensor.getSensorState());
+        }
+    }
+
+    @Test
     public void testMultiAuth_singleSensor_fingerprintSensorStartsAfterDialogAnimationCompletes()
             throws Exception {
         setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
@@ -212,6 +243,7 @@
         final AuthSession session = createAuthSession(mSensors,
                 false /* checkDevicePolicyManager */,
                 Authenticators.BIOMETRIC_STRONG,
+                TEST_REQUEST_ID,
                 operationId,
                 userId);
         assertEquals(mSensors.size(), session.mPreAuthInfo.eligibleSensors.size());
@@ -238,7 +270,7 @@
         // fingerprint sensor does not start even if all cookies are received
         assertEquals(STATE_AUTH_STARTED, session.getState());
         verify(mStatusBarService).showAuthenticationDialog(any(), any(), any(),
-                anyBoolean(), anyBoolean(), anyInt(), any(), anyLong(), anyInt());
+                anyBoolean(), anyBoolean(), anyInt(), anyLong(), any(), anyLong(), anyInt());
 
         // Notify AuthSession that the UI is shown. Then, fingerprint sensor should be started.
         session.onDialogAnimatedIn();
@@ -277,6 +309,7 @@
         final AuthSession session = createAuthSession(mSensors,
                 false /* checkDevicePolicyManager */,
                 Authenticators.BIOMETRIC_STRONG,
+                TEST_REQUEST_ID,
                 0 /* operationId */,
                 0 /* userId */);
 
@@ -285,7 +318,8 @@
 
         sessionConsumer.accept(session);
 
-        verify(faceAuthenticator).cancelAuthenticationFromService(eq(mToken), eq(TEST_PACKAGE));
+        verify(faceAuthenticator).cancelAuthenticationFromService(
+                eq(mToken), eq(TEST_PACKAGE), eq(TEST_REQUEST_ID));
     }
 
     private PreAuthInfo createPreAuthInfo(List<BiometricSensor> sensors, int userId,
@@ -302,14 +336,14 @@
 
     private AuthSession createAuthSession(List<BiometricSensor> sensors,
             boolean checkDevicePolicyManager, @Authenticators.Types int authenticators,
-            long operationId, int userId) throws RemoteException {
+            long requestId, long operationId, int userId) throws RemoteException {
 
         final PromptInfo promptInfo = createPromptInfo(authenticators);
 
         final PreAuthInfo preAuthInfo = createPreAuthInfo(sensors, userId, promptInfo,
                 checkDevicePolicyManager);
         return new AuthSession(mContext, mStatusBarService, mSysuiReceiver, mKeyStore,
-                mRandom, mClientDeathReceiver, preAuthInfo, mToken, operationId, userId,
+                mRandom, mClientDeathReceiver, preAuthInfo, mToken, requestId, operationId, userId,
                 mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo,
                 false /* debugEnabled */, mFingerprintSensorProps);
     }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 7c7afb7..69d8e89 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -85,6 +85,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Random;
+import java.util.concurrent.atomic.AtomicLong;
 
 @Presubmit
 @SmallTest
@@ -93,6 +94,7 @@
     private static final String TAG = "BiometricServiceTest";
 
     private static final String TEST_PACKAGE_NAME = "test_package";
+    private static final long TEST_REQUEST_ID = 44;
 
     private static final String ERROR_HW_UNAVAILABLE = "hw_unavailable";
     private static final String ERROR_NOT_RECOGNIZED = "not_recognized";
@@ -151,6 +153,7 @@
                 .thenReturn(mock(BiometricStrengthController.class));
         when(mInjector.getTrustManager()).thenReturn(mTrustManager);
         when(mInjector.getDevicePolicyManager(any())).thenReturn(mDevicePolicyManager);
+        when(mInjector.getRequestGenerator()).thenReturn(new AtomicLong(TEST_REQUEST_ID - 1));
 
         when(mResources.getString(R.string.biometric_error_hw_unavailable))
                 .thenReturn(ERROR_HW_UNAVAILABLE);
@@ -215,8 +218,7 @@
                 mBiometricService.mCurrentAuthSession.getState());
 
         verify(mBiometricService.mCurrentAuthSession.mPreAuthInfo.eligibleSensors.get(0).impl)
-                .cancelAuthenticationFromService(any(),
-                        any());
+                .cancelAuthenticationFromService(any(), any(), anyLong());
 
         // Simulate ERROR_CANCELED received from HAL
         mBiometricService.mBiometricSensorReceiver.onError(
@@ -272,8 +274,9 @@
                 eq(true) /* credentialAllowed */,
                 anyBoolean() /* requireConfirmation */,
                 anyInt() /* userId */,
+                anyLong() /* operationId */,
                 eq(TEST_PACKAGE_NAME),
-                anyLong() /* sessionId */,
+                eq(TEST_REQUEST_ID),
                 eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
     }
 
@@ -357,8 +360,9 @@
                 eq(false) /* credentialAllowed */,
                 eq(false) /* requireConfirmation */,
                 anyInt() /* userId */,
+                anyLong() /* operationId */,
                 eq(TEST_PACKAGE_NAME),
-                anyLong() /* sessionId */,
+                eq(TEST_REQUEST_ID),
                 eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
     }
 
@@ -467,6 +471,7 @@
                 anyInt() /* userId */,
                 any(IBiometricSensorReceiver.class),
                 anyString() /* opPackageName */,
+                eq(TEST_REQUEST_ID),
                 cookieCaptor.capture() /* cookie */,
                 anyBoolean() /* allowBackgroundAuthentication */);
 
@@ -488,8 +493,9 @@
                 eq(false) /* credentialAllowed */,
                 anyBoolean() /* requireConfirmation */,
                 anyInt() /* userId */,
+                anyLong() /* operationId */,
                 eq(TEST_PACKAGE_NAME),
-                anyLong() /* sessionId */,
+                eq(TEST_REQUEST_ID),
                 eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
 
         // Hardware authenticated
@@ -543,8 +549,9 @@
                 eq(true) /* credentialAllowed */,
                 anyBoolean() /* requireConfirmation */,
                 anyInt() /* userId */,
+                anyLong() /* operationId */,
                 eq(TEST_PACKAGE_NAME),
-                anyLong() /* sessionId */,
+                eq(TEST_REQUEST_ID),
                 eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
     }
 
@@ -705,8 +712,9 @@
                 anyBoolean() /* credentialAllowed */,
                 anyBoolean() /* requireConfirmation */,
                 anyInt() /* userId */,
+                anyLong() /* operationId */,
                 anyString(),
-                anyLong() /* sessionId */,
+                anyLong() /* requestId */,
                 eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
     }
 
@@ -805,8 +813,9 @@
                 eq(true) /* credentialAllowed */,
                 anyBoolean() /* requireConfirmation */,
                 anyInt() /* userId */,
+                anyLong() /* operationId */,
                 eq(TEST_PACKAGE_NAME),
-                anyLong() /* sessionId */,
+                eq(TEST_REQUEST_ID),
                 eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
     }
 
@@ -885,8 +894,9 @@
                 eq(true) /* credentialAllowed */,
                 anyBoolean() /* requireConfirmation */,
                 anyInt() /* userId */,
+                anyLong() /* operationId */,
                 eq(TEST_PACKAGE_NAME),
-                anyLong() /* sessionId */,
+                eq(TEST_REQUEST_ID),
                 eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
     }
 
@@ -1030,8 +1040,7 @@
                 eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
                 eq(0 /* vendorCode */));
         verify(mBiometricService.mSensors.get(0).impl).cancelAuthenticationFromService(
-                any(),
-                any());
+                any(), any(), anyLong());
         assertNull(mBiometricService.mCurrentAuthSession);
     }
 
@@ -1051,7 +1060,7 @@
         waitForIdle();
 
         verify(mBiometricService.mSensors.get(0).impl)
-                .cancelAuthenticationFromService(any(), any());
+                .cancelAuthenticationFromService(any(), any(), anyLong());
     }
 
     @Test
@@ -1071,7 +1080,7 @@
         waitForIdle();
 
         verify(mBiometricService.mSensors.get(0).impl)
-                .cancelAuthenticationFromService(any(), any());
+                .cancelAuthenticationFromService(any(), any(), anyLong());
     }
 
     @Test
@@ -1088,7 +1097,7 @@
         waitForIdle();
 
         verify(mBiometricService.mSensors.get(0).impl)
-                .cancelAuthenticationFromService(any(), any());
+                .cancelAuthenticationFromService(any(), any(), anyLong());
         verify(mReceiver1).onError(
                 eq(BiometricAuthenticator.TYPE_FACE),
                 eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
@@ -1126,7 +1135,7 @@
                 false /* requireConfirmation */, null /* authenticators */);
 
         mBiometricService.mImpl.cancelAuthentication(mBiometricService.mCurrentAuthSession.mToken,
-                TEST_PACKAGE_NAME);
+                TEST_PACKAGE_NAME, TEST_REQUEST_ID);
         waitForIdle();
 
         // Pretend that the HAL has responded to cancel with ERROR_CANCELED
@@ -1353,8 +1362,8 @@
         int authenticators = Authenticators.BIOMETRIC_STRONG;
         assertEquals(BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
                 invokeCanAuthenticate(mBiometricService, authenticators));
-        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
-                authenticators);
+        long requestId = invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */, authenticators);
         waitForIdle();
         verify(mReceiver1).onError(
                 eq(BiometricAuthenticator.TYPE_FINGERPRINT),
@@ -1366,7 +1375,7 @@
         authenticators = Authenticators.BIOMETRIC_WEAK;
         assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
                 invokeCanAuthenticate(mBiometricService, authenticators));
-        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+        requestId = invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
                 false /* requireConfirmation */,
                 authenticators);
         waitForIdle();
@@ -1377,8 +1386,9 @@
                 eq(false) /* credentialAllowed */,
                 anyBoolean() /* requireConfirmation */,
                 anyInt() /* userId */,
+                anyLong() /* operationId */,
                 eq(TEST_PACKAGE_NAME),
-                anyLong() /* sessionId */,
+                eq(requestId),
                 eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
 
         // Requesting strong and credential, when credential is setup
@@ -1387,7 +1397,7 @@
         when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
         assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
                 invokeCanAuthenticate(mBiometricService, authenticators));
-        invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+        requestId = invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
                 false /* requireConfirmation */,
                 authenticators);
         waitForIdle();
@@ -1399,8 +1409,9 @@
                 eq(true) /* credentialAllowed */,
                 anyBoolean() /* requireConfirmation */,
                 anyInt() /* userId */,
+                anyLong() /* operationId */,
                 eq(TEST_PACKAGE_NAME),
-                anyLong() /* sessionId */,
+                eq(requestId),
                 eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
 
         // Un-downgrading the authenticator allows successful strong auth
@@ -1414,7 +1425,7 @@
         authenticators = Authenticators.BIOMETRIC_STRONG;
         assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
                 invokeCanAuthenticate(mBiometricService, authenticators));
-        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+        requestId = invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
                 false /* requireConfirmation */, authenticators);
         waitForIdle();
         verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
@@ -1424,8 +1435,9 @@
                 eq(false) /* credentialAllowed */,
                 anyBoolean() /* requireConfirmation */,
                 anyInt() /* userId */,
+                anyLong() /* operationId */,
                 eq(TEST_PACKAGE_NAME),
-                anyLong() /* sessionId */,
+                eq(requestId),
                 eq(BIOMETRIC_MULTI_SENSOR_DEFAULT));
     }
 
@@ -1617,11 +1629,12 @@
         mBiometricService.mStatusBarService = mock(IStatusBarService.class);
     }
 
-    private void invokeAuthenticateAndStart(IBiometricService.Stub service,
+    private long invokeAuthenticateAndStart(IBiometricService.Stub service,
             IBiometricServiceReceiver receiver, boolean requireConfirmation,
             Integer authenticators) throws Exception {
         // Request auth, creates a pending session
-        invokeAuthenticate(service, receiver, requireConfirmation, authenticators);
+        final long requestId = invokeAuthenticate(
+                service, receiver, requireConfirmation, authenticators);
         waitForIdle();
 
         startPendingAuthSession(mBiometricService);
@@ -1629,6 +1642,8 @@
 
         assertNotNull(mBiometricService.mCurrentAuthSession);
         assertEquals(STATE_AUTH_STARTED, mBiometricService.mCurrentAuthSession.getState());
+
+        return requestId;
     }
 
     private static void startPendingAuthSession(BiometricService service) throws Exception {
@@ -1644,10 +1659,10 @@
         service.mImpl.onReadyForAuthentication(cookie);
     }
 
-    private static void invokeAuthenticate(IBiometricService.Stub service,
+    private static long invokeAuthenticate(IBiometricService.Stub service,
             IBiometricServiceReceiver receiver, boolean requireConfirmation,
             Integer authenticators) throws Exception {
-        service.authenticate(
+        return service.authenticate(
                 new Binder() /* token */,
                 0 /* operationId */,
                 0 /* userId */,
@@ -1657,9 +1672,9 @@
                         false /* checkDevicePolicy */));
     }
 
-    private static void invokeAuthenticateForWorkApp(IBiometricService.Stub service,
+    private static long invokeAuthenticateForWorkApp(IBiometricService.Stub service,
             IBiometricServiceReceiver receiver, Integer authenticators) throws Exception {
-        service.authenticate(
+        return service.authenticate(
                 new Binder() /* token */,
                 0 /* operationId */,
                 0 /* userId */,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index a41f79e..e3e3900 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -40,6 +40,7 @@
 import android.testing.TestableContext;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
@@ -193,7 +194,7 @@
         // Request it to be canceled. The operation can be canceled immediately, and the scheduler
         // should go back to idle, since in this case the framework has not even requested the HAL
         // to authenticate yet.
-        mScheduler.cancelAuthenticationOrDetection(mToken);
+        mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
         assertNull(mScheduler.mCurrentOperation);
     }
 
@@ -303,7 +304,7 @@
                 mScheduler.mPendingOperations.getFirst().mState);
 
         // Request cancel before the authentication client has started
-        mScheduler.cancelAuthenticationOrDetection(mToken);
+        mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
         waitForIdle();
         assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
                 mScheduler.mPendingOperations.getFirst().mState);
@@ -318,6 +319,107 @@
     }
 
     @Test
+    public void testCancels_whenAuthRequestIdNotSet() {
+        testCancelsWhenRequestId(null /* requestId */, 2, true /* started */);
+    }
+
+    @Test
+    public void testCancels_whenAuthRequestIdNotSet_notStarted() {
+        testCancelsWhenRequestId(null /* requestId */, 2, false /* started */);
+    }
+
+    @Test
+    public void testCancels_whenAuthRequestIdMatches() {
+        testCancelsWhenRequestId(200L, 200, true /* started */);
+    }
+
+    @Test
+    public void testCancels_whenAuthRequestIdMatches_noStarted() {
+        testCancelsWhenRequestId(200L, 200, false /* started */);
+    }
+
+    @Test
+    public void testDoesNotCancel_whenAuthRequestIdMismatched() {
+        testCancelsWhenRequestId(10L, 20, true /* started */);
+    }
+
+    @Test
+    public void testDoesNotCancel_whenAuthRequestIdMismatched_notStarted() {
+        testCancelsWhenRequestId(10L, 20, false /* started */);
+    }
+
+    private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+            boolean started) {
+        final boolean matches = requestId == null || requestId == cancelRequestId;
+        final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+        final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+        final TestAuthenticationClient client = new TestAuthenticationClient(
+                mContext, lazyDaemon, mToken, callback);
+        if (requestId != null) {
+            client.setRequestId(requestId);
+        }
+
+        mScheduler.scheduleClientMonitor(client);
+        if (started) {
+            mScheduler.startPreparedClient(client.getCookie());
+        }
+        waitForIdle();
+        mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+        waitForIdle();
+
+        assertEquals(matches && started ? 1 : 0, client.mNumCancels);
+
+        if (matches) {
+            if (started) {
+                assertEquals(Operation.STATE_STARTED_CANCELING,
+                        mScheduler.mCurrentOperation.mState);
+            }
+        } else {
+            if (started) {
+                assertEquals(Operation.STATE_STARTED,
+                        mScheduler.mCurrentOperation.mState);
+            } else {
+                assertEquals(Operation.STATE_WAITING_FOR_COOKIE,
+                        mScheduler.mCurrentOperation.mState);
+            }
+        }
+    }
+
+    @Test
+    public void testCancelsPending_whenAuthRequestIdsSet() {
+        final long requestId1 = 10;
+        final long requestId2 = 20;
+        final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+        final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+        final TestAuthenticationClient client1 = new TestAuthenticationClient(
+                mContext, lazyDaemon, mToken, callback);
+        client1.setRequestId(requestId1);
+        final TestAuthenticationClient client2 = new TestAuthenticationClient(
+                mContext, lazyDaemon, mToken, callback);
+        client2.setRequestId(requestId2);
+
+        mScheduler.scheduleClientMonitor(client1);
+        mScheduler.scheduleClientMonitor(client2);
+        mScheduler.startPreparedClient(client1.getCookie());
+        waitForIdle();
+        mScheduler.cancelAuthenticationOrDetection(mToken, 9999);
+        waitForIdle();
+
+        assertEquals(Operation.STATE_STARTED,
+                mScheduler.mCurrentOperation.mState);
+        assertEquals(Operation.STATE_WAITING_IN_QUEUE,
+                mScheduler.mPendingOperations.getFirst().mState);
+
+        mScheduler.cancelAuthenticationOrDetection(mToken, requestId2);
+        waitForIdle();
+
+        assertEquals(Operation.STATE_STARTED,
+                mScheduler.mCurrentOperation.mState);
+        assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
+                mScheduler.mPendingOperations.getFirst().mState);
+    }
+
+    @Test
     public void testInterruptPrecedingClients_whenExpected() {
         final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class,
                 withSettings().extraInterfaces(Interruptable.class));
@@ -377,12 +479,10 @@
 
         @Override
         protected void stopHalOperation() {
-
         }
 
         @Override
         protected void startHalOperation() {
-
         }
 
         @Override
@@ -397,6 +497,7 @@
     }
 
     private static class TestAuthenticationClient extends AuthenticationClient<Object> {
+        int mNumCancels = 0;
 
         public TestAuthenticationClient(@NonNull Context context,
                 @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
@@ -428,6 +529,11 @@
         public boolean wasUserDetected() {
             return false;
         }
+
+        public void cancel() {
+            mNumCancels++;
+            super.cancel();
+        }
     }
 
     private static class TestClientMonitor2 extends TestClientMonitor {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index f241fe1..dbed445 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -151,7 +151,7 @@
         String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" };
         String[] appdir = { "app", "priv-app" };
         for (int i = 0; i < partitions.length; i++) {
-            final PackageManagerService.ScanPartition scanPartition =
+            final ScanPartition scanPartition =
                     PackageManagerService.SYSTEM_PARTITIONS.get(i);
             for (int j = 0; j < appdir.length; j++) {
                 File path = new File(String.format("%s/%s/A.apk", partitions[i], appdir[j]));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 6c6cfd4..77b9fd3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -87,6 +87,8 @@
     PlatformCompat mMockCompatibility;
     @Mock
     PackageManagerService.Injector mMockInjector;
+    @Mock
+    PackageManagerService mMockPackageManager;
 
     @Before
     public void setupInjector() {
@@ -432,7 +434,7 @@
         final ParsingPackage basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
                 .addUsesPermission(new ParsedUsesPermission(Manifest.permission.FACTORY_TEST, 0));
 
-        final ScanResult scanResult = PackageManagerService.scanPackageOnlyLI(
+        final ScanResult scanResult = mMockPackageManager.scanPackageOnlyLI(
                 createBasicScanRequestBuilder(basicPackage).build(),
                 mMockInjector,
                 true /*isUnderFactoryTest*/,
@@ -480,7 +482,7 @@
 
     private ScanResult executeScan(
             ScanRequest scanRequest) throws PackageManagerException {
-        ScanResult result = PackageManagerService.scanPackageOnlyLI(
+        ScanResult result = mMockPackageManager.scanPackageOnlyLI(
                 scanRequest,
                 mMockInjector,
                 false /*isUnderFactoryTest*/,
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
index f54f144..39815b7 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -27,6 +27,7 @@
 import android.media.MediaFormat;
 import android.media.audio.common.AudioChannelLayout;
 import android.media.audio.common.AudioConfig;
+import android.media.audio.common.AudioConfigBase;
 import android.media.audio.common.AudioFormatDescription;
 import android.media.audio.common.AudioFormatType;
 import android.media.soundtrigger.AudioCapabilities;
@@ -340,10 +341,11 @@
         event.capturePreambleMs = 345;
         event.triggerInData = true;
         event.audioConfig = new AudioConfig();
-        event.audioConfig.sampleRateHz = 456;
-        event.audioConfig.channelMask = AudioChannelLayout.layoutMask(
+        event.audioConfig.base = new AudioConfigBase();
+        event.audioConfig.base.sampleRate = 456;
+        event.audioConfig.base.channelMask = AudioChannelLayout.layoutMask(
                 AudioChannelLayout.LAYOUT_MONO);
-        event.audioConfig.format = createAudioFormatMp3();
+        event.audioConfig.base.format = createAudioFormatMp3();
         //event.audioConfig.offloadInfo is irrelevant.
         event.data = new byte[]{31, 32, 33};
         return event;
@@ -367,10 +369,10 @@
         assertEquals(234, event.captureDelayMs);
         assertEquals(345, event.capturePreambleMs);
         assertTrue(event.triggerInData);
-        assertEquals(456, event.audioConfig.sampleRateHz);
+        assertEquals(456, event.audioConfig.base.sampleRate);
         assertEquals(AudioChannelLayout.layoutMask(AudioChannelLayout.LAYOUT_MONO),
-                event.audioConfig.channelMask);
-        assertEquals(createAudioFormatMp3(), event.audioConfig.format);
+                event.audioConfig.base.channelMask);
+        assertEquals(createAudioFormatMp3(), event.audioConfig.base.format);
         assertArrayEquals(new byte[]{31, 32, 33}, event.data);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 8f9eb22..2c5159a 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -35,7 +35,6 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
@@ -1016,7 +1015,7 @@
         assertEquals(IExternalVibratorService.SCALE_NONE, firstScale);
         assertEquals(IExternalVibratorService.SCALE_NONE, secondScale);
         verify(firstController).mute();
-        verifyNoMoreInteractions(secondController);
+        verify(secondController, never()).mute();
         // Set external control called only once.
         assertEquals(Arrays.asList(true), mVibratorProviders.get(1).getExternalControlStates());
     }
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
index a67f645..1612321 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
@@ -18,13 +18,14 @@
 
 import android.annotation.NonNull;
 import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
 import android.util.ArrayMap;
 import android.util.Pair;
 
 import com.android.internal.util.Preconditions;
-import com.android.server.utils.DeviceConfigInterface;
 
 import java.lang.reflect.Constructor;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
@@ -33,10 +34,17 @@
 
 public class FakeDeviceConfigInterface implements DeviceConfigInterface {
 
+    private static final String COMPOSITE_DELIMITER = "/";
     private Map<String, String> mProperties = new HashMap<>();
     private ArrayMap<DeviceConfig.OnPropertiesChangedListener, Pair<String, Executor>> mListeners =
             new ArrayMap<>();
 
+    private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
+        Preconditions.checkNotNull(namespace);
+        Preconditions.checkNotNull(name);
+        return namespace + COMPOSITE_DELIMITER + name;
+    }
+
     public void clearProperties() {
         mProperties.clear();
     }
@@ -91,6 +99,59 @@
     }
 
     @Override
+    public DeviceConfig.Properties getProperties(String namespace, String... names) {
+        if (!mProperties.keySet().contains(namespace)) {
+            return new DeviceConfig.Properties(namespace, null);
+        }
+        DeviceConfig.Properties.Builder propertiesBuilder = new DeviceConfig.Properties.Builder(
+                namespace);
+
+        for (String compositeName : mProperties.keySet()) {
+            if (compositeName.split(COMPOSITE_DELIMITER).length != 2) {
+                continue;
+            }
+
+            String existingPropertyNamespace = compositeName.split(COMPOSITE_DELIMITER)[0];
+            String existingPropertyName = compositeName.split(COMPOSITE_DELIMITER)[1];
+
+            if ((names.length == 0 && existingPropertyNamespace.equals(namespace)) || Arrays.asList(
+                    names).contains(compositeName)) {
+                propertiesBuilder.setString(existingPropertyName, mProperties.get(compositeName));
+            }
+        }
+
+        return propertiesBuilder.build();
+    }
+
+    @Override
+    public boolean setProperty(String namespace, String name, String value, boolean makeDefault) {
+        putPropertyAndNotify(namespace, name, value);
+        return true;
+    }
+
+    @Override
+    public boolean setProperties(DeviceConfig.Properties properties)
+            throws DeviceConfig.BadConfigException {
+        for (String property : properties.getKeyset()) {
+            String compositeName = createCompositeName(properties.getNamespace(), property);
+            putPropertyAndNotify(properties.getNamespace(), compositeName,
+                    properties.getString(property, ""));
+        }
+        return true;
+    }
+
+    @Override
+    public boolean deleteProperty(String namespace, String name) {
+        mProperties.remove(createCompositeName(namespace, name));
+        return true;
+    }
+
+    @Override
+    public void resetToDefaults(int resetMode, String namespace) {
+        clearProperties();
+    }
+
+    @Override
     public String getString(String namespace, String name, String defaultValue) {
         String value = getProperty(namespace, name);
         return value != null ? value : defaultValue;
@@ -166,10 +227,4 @@
             DeviceConfig.OnPropertiesChangedListener listener) {
         mListeners.remove(listener);
     }
-
-    private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
-        Preconditions.checkNotNull(namespace);
-        Preconditions.checkNotNull(name);
-        return namespace + "/" + name;
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index a165e1c..53bae41 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -26,15 +26,19 @@
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.verify;
 
 import android.os.Binder;
 import android.os.IBinder;
@@ -47,6 +51,8 @@
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
 import android.view.WindowManager;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentOrganizer;
 
 import androidx.test.filters.SmallTest;
 
@@ -737,4 +743,38 @@
                 mAppTransitionController.getRemoteAnimationOverride(
                         activity, TRANSIT_OLD_ACTIVITY_OPEN, new ArraySet<Integer>()));
     }
+
+    @Test
+    public void testGetRemoteAnimationOverrideTaskFragmentOrganizer() {
+        // TaskFragmentOrganizer registers remote animation.
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final ITaskFragmentOrganizer iOrganizer =
+                ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
+        final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+        final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+                new TestRemoteAnimationRunner(), 10, 1);
+        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter);
+        mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
+        mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition);
+
+        // Create a TaskFragment with embedded activity.
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(createTask(mDisplayContent))
+                .createActivityCount(1)
+                .setOrganizer(organizer)
+                .build();
+        final ActivityRecord activity = taskFragment.getTopMostActivity();
+        activity.allDrawn = true;
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare a transition for TaskFragment.
+        mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0);
+        mDisplayContent.mOpeningApps.add(activity);
+        mDisplayContent.mChangingContainers.add(taskFragment);
+        mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+
+        // Check if the transition has been overridden.
+        verify(mDisplayContent.mAppTransition)
+                .overridePendingAppTransitionRemote(adapter, false /* sync */);
+    }
 }
\ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 5c79f5c..98c1eabe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -21,6 +21,7 @@
 import static com.android.server.wm.testing.Assert.assertThrows;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -36,6 +37,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.view.RemoteAnimationDefinition;
 import android.view.SurfaceControl;
 import android.window.ITaskFragmentOrganizer;
 import android.window.TaskFragmentCreationParams;
@@ -70,6 +72,7 @@
     private IBinder mFragmentToken;
     private WindowContainerTransaction mTransaction;
     private WindowContainerToken mFragmentWindowToken;
+    private RemoteAnimationDefinition mDefinition;
 
     @Before
     public void setup() {
@@ -83,6 +86,7 @@
                 new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
         mTransaction = new WindowContainerTransaction();
         mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
+        mDefinition = new RemoteAnimationDefinition();
 
         spyOn(mController);
         spyOn(mOrganizer);
@@ -208,6 +212,18 @@
     }
 
     @Test
+    public void testRegisterRemoteAnimations() {
+        mController.registerOrganizer(mIOrganizer);
+        mController.registerRemoteAnimations(mIOrganizer, mDefinition);
+
+        assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer));
+
+        mController.unregisterRemoteAnimations(mIOrganizer);
+
+        assertNull(mController.getRemoteAnimationDefinition(mIOrganizer));
+    }
+
+    @Test
     public void testWindowContainerTransaction_setTaskFragmentOrganizer() {
         mOrganizer.applyTransaction(mTransaction);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index aa647ba..454ecd7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -67,6 +67,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
@@ -100,6 +101,7 @@
 import android.view.WindowManager.DisplayImePolicy;
 import android.window.ITransitionPlayer;
 import android.window.StartingWindowInfo;
+import android.window.TaskFragmentOrganizer;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
 
@@ -1135,6 +1137,8 @@
         private boolean mCreateParentTask;
         private boolean mCreateEmbeddedTask;
         private int mCreateActivityCount = 0;
+        @Nullable
+        private TaskFragmentOrganizer mOrganizer;
 
         TaskFragmentBuilder(ActivityTaskManagerService service) {
             mAtm = service;
@@ -1161,11 +1165,16 @@
             return this;
         }
 
+        TaskFragmentBuilder setOrganizer(@Nullable TaskFragmentOrganizer organizer) {
+            mOrganizer = organizer;
+            return this;
+        }
+
         TaskFragment build() {
             SystemServicesTestRule.checkHoldsLock(mAtm.mGlobalLock);
 
             final TaskFragment taskFragment = new TaskFragment(mAtm, null /* fragmentToken */,
-                    false /* createdByOrganizer */);
+                    mOrganizer != null);
             if (mParentTask == null && mCreateParentTask) {
                 mParentTask = new TaskBuilder(mAtm.mTaskSupervisor).build();
             }
@@ -1183,6 +1192,10 @@
                 taskFragment.addChild(activity);
                 mCreateActivityCount--;
             }
+            if (mOrganizer != null) {
+                taskFragment.setTaskFragmentOrganizer(
+                        mOrganizer.getOrganizerToken(), 10000 /* pid */);
+            }
             return taskFragment;
         }
     }
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 02bd001..bc0a146 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -23,6 +23,8 @@
 import android.os.Bundle;
 import android.util.ArrayMap;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -115,6 +117,7 @@
     public static final int SDK_VERSION_R = 30;
 
     // A Map allows us to track each Call by its Telecom-specified call ID
+    @GuardedBy("mLock")
     private final Map<String, Call> mCallByTelecomCallId = new ArrayMap<>();
 
     // A List allows us to keep the Calls in a stable iteration order so that casually developed
@@ -154,7 +157,7 @@
             return;
         }
 
-        Call call = mCallByTelecomCallId.get(parcelableCall.getId());
+        Call call = getCallById(parcelableCall.getId());
         if (call == null) {
             call = new Call(this, parcelableCall.getId(), mInCallAdapter,
                     parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
@@ -191,14 +194,14 @@
         if (mTargetSdkVersion < SDK_VERSION_R
                 && parcelableCall.getState() == Call.STATE_AUDIO_PROCESSING) {
             Log.i(this, "removing audio processing call during update for sdk compatibility");
-            Call call = mCallByTelecomCallId.get(parcelableCall.getId());
+            Call call = getCallById(parcelableCall.getId());
             if (call != null) {
                 internalRemoveCall(call);
             }
             return;
         }
 
-        Call call = mCallByTelecomCallId.get(parcelableCall.getId());
+        Call call = getCallById(parcelableCall.getId());
         if (call != null) {
             checkCallTree(parcelableCall);
             call.internalUpdate(parcelableCall, mCallByTelecomCallId);
@@ -215,8 +218,14 @@
         }
     }
 
+    Call getCallById(String callId) {
+        synchronized (mLock) {
+            return mCallByTelecomCallId.get(callId);
+        }
+    }
+
     final void internalSetPostDialWait(String telecomId, String remaining) {
-        Call call = mCallByTelecomCallId.get(telecomId);
+        Call call = getCallById(telecomId);
         if (call != null) {
             call.internalSetPostDialWait(remaining);
         }
@@ -230,7 +239,7 @@
     }
 
     final Call internalGetCallByTelecomId(String telecomId) {
-        return mCallByTelecomCallId.get(telecomId);
+        return getCallById(telecomId);
     }
 
     final void internalBringToForeground(boolean showDialpad) {
@@ -249,35 +258,35 @@
     }
 
     final void internalOnConnectionEvent(String telecomId, String event, Bundle extras) {
-        Call call = mCallByTelecomCallId.get(telecomId);
+        Call call = getCallById(telecomId);
         if (call != null) {
             call.internalOnConnectionEvent(event, extras);
         }
     }
 
     final void internalOnRttUpgradeRequest(String callId, int requestId) {
-        Call call = mCallByTelecomCallId.get(callId);
+        Call call = getCallById(callId);
         if (call != null) {
             call.internalOnRttUpgradeRequest(requestId);
         }
     }
 
     final void internalOnRttInitiationFailure(String callId, int reason) {
-        Call call = mCallByTelecomCallId.get(callId);
+        Call call = getCallById(callId);
         if (call != null) {
             call.internalOnRttInitiationFailure(reason);
         }
     }
 
     final void internalOnHandoverFailed(String callId, int error) {
-        Call call = mCallByTelecomCallId.get(callId);
+        Call call = getCallById(callId);
         if (call != null) {
             call.internalOnHandoverFailed(error);
         }
     }
 
     final void internalOnHandoverComplete(String callId) {
-        Call call = mCallByTelecomCallId.get(callId);
+        Call call = getCallById(callId);
         if (call != null) {
             call.internalOnHandoverComplete();
         }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 412c02c..39cb04a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3483,7 +3483,8 @@
      */
     private int getLogicalSlotIndex(int physicalSlotIndex) {
         UiccSlotInfo[] slotInfos = getUiccSlotsInfo();
-        if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length) {
+        if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length
+                && slotInfos[physicalSlotIndex] != null) {
             return slotInfos[physicalSlotIndex].getLogicalSlotIdx();
         }
 
diff --git a/tools/aapt/Android.bp b/tools/aapt/Android.bp
index a19d183..01eb4f6 100644
--- a/tools/aapt/Android.bp
+++ b/tools/aapt/Android.bp
@@ -50,7 +50,6 @@
         "libbase",
         "libz",
     ],
-    group_static_libs: true,
 
     cflags: [
         "-Wall",
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 12dc156..740b44e 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -72,7 +72,6 @@
         "libidmap2_policies",
     ],
     stl: "libc++_static",
-    group_static_libs: true,
 }
 
 // ==========================================================
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 3cdb27c..ac23c3d 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -35,6 +35,7 @@
         echo
         echo "If your change contains no confidential details (such as security fixes), please"
         echo "upload and merge this change at https://android-review.googlesource.com/."
+        echo "Else add a tag 'Ignore-AOSP-First:' with the reason to bypass AOSP."
         echo
         exit 1
     fi
diff --git a/tools/split-select/Android.bp b/tools/split-select/Android.bp
index c12fc6a..5402657 100644
--- a/tools/split-select/Android.bp
+++ b/tools/split-select/Android.bp
@@ -48,7 +48,6 @@
         "libbase",
         "libz",
     ],
-    group_static_libs: true,
 
     target: {
         windows: {