Merge "Send AIDL face enrollment frame info to clients" into sc-dev
diff --git a/StubLibraries.bp b/StubLibraries.bp
index e6f50b7..720bfc0 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -358,7 +358,7 @@
     visibility: ["//visibility:private"],
 }
 
-java_library_static {
+java_library {
     name: "android-non-updatable.stubs",
     defaults: ["android-non-updatable_defaults_stubs_current"],
     srcs: [":api-stubs-docs-non-updatable"],
@@ -368,7 +368,7 @@
     },
 }
 
-java_library_static {
+java_library {
     name: "android-non-updatable.stubs.system",
     defaults: ["android-non-updatable_defaults_stubs_current"],
     srcs: [":system-api-stubs-docs-non-updatable"],
@@ -378,7 +378,7 @@
     },
 }
 
-java_library_static {
+java_library {
     name: "android-non-updatable.stubs.module_lib",
     defaults: ["android-non-updatable_defaults_stubs_current"],
     srcs: [":module-lib-api-stubs-docs-non-updatable"],
@@ -392,7 +392,7 @@
     },
 }
 
-java_library_static {
+java_library {
     name: "android-non-updatable.stubs.test",
     defaults: ["android-non-updatable_defaults_stubs_current"],
     srcs: [":test-api-stubs-docs-non-updatable"],
@@ -415,7 +415,7 @@
     defaults_visibility: ["//frameworks/base/services"],
 }
 
-java_library_static {
+java_library {
     name: "android_stubs_current",
     static_libs: modules_public_stubs + [
         "android-non-updatable.stubs",
@@ -424,7 +424,7 @@
     defaults: ["android.jar_defaults"],
 }
 
-java_library_static {
+java_library {
     name: "android_system_stubs_current",
     static_libs: modules_system_stubs + [
         "android-non-updatable.stubs.system",
@@ -450,7 +450,7 @@
     ],
 }
 
-java_library_static {
+java_library {
     name: "android_test_stubs_current",
     // Modules do not have test APIs, but we want to include their SystemApis, like we include
     // the SystemApi of framework-non-updatable-sources.
@@ -467,7 +467,7 @@
     },
 }
 
-java_library_static {
+java_library {
     name: "android_module_lib_stubs_current",
     defaults: [
         "android.jar_defaults",
@@ -482,6 +482,22 @@
     },
 }
 
+java_library {
+    name: "android_system_server_stubs_current",
+    defaults: ["android_stubs_dists_default"],
+    srcs: [":services-non-updatable-stubs"],
+    installable: false,
+    static_libs: [
+        "android_module_lib_stubs_current",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+    java_version: "1.8",
+    dist: {
+        dir: "apistubs/android/system-server",
+    },
+}
+
 /////////////////////////////////////////////////////////////////////
 // hwbinder.stubs provides APIs required for building HIDL Java
 // libraries.
@@ -515,7 +531,7 @@
     visibility: ["//visibility:private"],
 }
 
-java_library_static {
+java_library {
     name: "hwbinder.stubs",
     sdk_version: "core_current",
     libs: ["framework-annotations-lib"],
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
index 8a162d4..b7bd387 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -96,8 +96,13 @@
     }
 
     /**
-     * Returns a {@link Set} of schema type that were deleted by the {@link
-     * AppSearchSession#setSchema} call.
+     * Returns a {@link Set} of deleted schema types.
+     *
+     * <p>A "deleted" type is a schema type that was previously a part of the database schema but
+     * was not present in the {@link SetSchemaRequest} object provided in the
+     * {@link AppSearchSession#setSchema) call.
+     *
+     * <p>Documents for a deleted type are removed from the database.
      */
     @NonNull
     public Set<String> getDeletedTypes() {
@@ -113,6 +118,15 @@
     /**
      * Returns a {@link Set} of schema type that were migrated by the {@link
      * AppSearchSession#setSchema} call.
+     *
+     * <p>A "migrated" type is a schema type that has triggered a {@link Migrator} instance to
+     * migrate documents of the schema type to another schema type, or to another version of the
+     * schema type.
+     *
+     * <p>If a document fails to be migrated, a {@link MigrationFailure} will be generated for that
+     * document.
+     *
+     * @see Migrator
      */
     @NonNull
     public Set<String> getMigratedTypes() {
@@ -132,6 +146,7 @@
      * <p>If a {@link Migrator} is provided for this type and the migration is success triggered.
      * The type will also appear in {@link #getMigratedTypes()}.
      *
+     * @see SetSchemaRequest
      * @see AppSearchSession#setSchema
      * @see SetSchemaRequest.Builder#setForceOverride
      */
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
index ca4ea2b..62593ae8 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
@@ -42,6 +42,8 @@
      * Initializes an {@link AppSearchException} with a result code and message.
      *
      * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
+     * @param message The detail message (which is saved for later retrieval by the {@link
+     *     #getMessage()} method).
      */
     public AppSearchException(
             @AppSearchResult.ResultCode int resultCode, @Nullable String message) {
@@ -52,6 +54,11 @@
      * Initializes an {@link AppSearchException} with a result code, message and cause.
      *
      * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
+     * @param message The detail message (which is saved for later retrieval by the {@link
+     *     #getMessage()} method).
+     * @param cause The cause (which is saved for later retrieval by the {@link #getCause()}
+     *     method). (A null value is permitted, and indicates that the cause is nonexistent or
+     *     unknown.)
      */
     public AppSearchException(
             @AppSearchResult.ResultCode int resultCode,
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 3f6e8a5..a4188a2 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -501,7 +501,8 @@
                                     packageName,
                                     databaseName,
                                     queryExpression,
-                                    new SearchSpec(searchSpecBundle));
+                                    new SearchSpec(searchSpecBundle),
+                                    /*logger=*/ null);
                     invokeCallbackOnResult(
                             callback,
                             AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
@@ -535,7 +536,8 @@
                                     queryExpression,
                                     new SearchSpec(searchSpecBundle),
                                     packageName,
-                                    callingUid);
+                                    callingUid,
+                                    /*logger=*/ null);
                     invokeCallbackOnResult(
                             callback,
                             AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
@@ -609,7 +611,8 @@
                                 packageName,
                                 databaseName,
                                 queryExpression,
-                                new SearchSpec(searchSpecBundle));
+                                new SearchSpec(searchSpecBundle),
+                                /*logger=*/ null);
                         while (!searchResultPage.getResults().isEmpty()) {
                             for (int i = 0; i < searchResultPage.getResults().size(); i++) {
                                 AppSearchMigrationHelper.writeBundleToOutputStream(
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index 45023f9..94ee830 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -149,7 +149,8 @@
     private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
             throws AppSearchException {
         File appSearchDir = getAppSearchDir(userId);
-        return AppSearchImpl.create(appSearchDir, context, userId, mGlobalQuerierPackage);
+        return AppSearchImpl.create(
+                appSearchDir, context, userId, mGlobalQuerierPackage, /*logger=*/ 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 98fcb13..e5e20e7 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
@@ -54,7 +54,9 @@
 import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter;
 import com.android.server.appsearch.external.localstorage.converter.SetSchemaResponseToProtoConverter;
 import com.android.server.appsearch.external.localstorage.converter.TypePropertyPathToProtoConverter;
+import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
 import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
 
 import com.google.android.icing.IcingSearchEngine;
 import com.google.android.icing.proto.DeleteByQueryResultProto;
@@ -178,28 +180,63 @@
     /**
      * Creates and initializes an instance of {@link AppSearchImpl} which writes data to the given
      * folder.
+     *
+     * <p>Clients can pass a {@link AppSearchLogger} here through their AppSearchSession, but it
+     * can't be saved inside {@link AppSearchImpl}, because the impl will be shared by all the
+     * sessions for the same package in JetPack.
+     *
+     * <p>Instead, logger instance needs to be passed to each individual method, like create, query
+     * and putDocument.
+     *
+     * @param logger collects stats for initialization if provided.
      */
     @NonNull
     public static AppSearchImpl create(
             @NonNull File icingDir,
             @NonNull Context context,
             int userId,
-            @NonNull String globalQuerierPackage)
+            @NonNull String globalQuerierPackage,
+            @Nullable AppSearchLogger logger)
             throws AppSearchException {
         Objects.requireNonNull(icingDir);
         Objects.requireNonNull(context);
         Objects.requireNonNull(globalQuerierPackage);
+
+        long totalLatencyStartMillis = SystemClock.elapsedRealtime();
+        InitializeStats.Builder initStatsBuilder = null;
+        if (logger != null) {
+            initStatsBuilder = new InitializeStats.Builder();
+        }
+
         AppSearchImpl appSearchImpl =
-                new AppSearchImpl(icingDir, context, userId, globalQuerierPackage);
+                new AppSearchImpl(
+                        icingDir, context, userId, globalQuerierPackage, initStatsBuilder);
+
+        long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime();
         appSearchImpl.initializeVisibilityStore();
+        long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime();
+
+        if (logger != null && initStatsBuilder != null) {
+            initStatsBuilder
+                    .setTotalLatencyMillis(
+                            (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis))
+                    .setPrepareVisibilityStoreLatencyMillis(
+                            (int)
+                                    (prepareVisibilityStoreLatencyEndMillis
+                                            - prepareVisibilityStoreLatencyStartMillis));
+            logger.logStats(initStatsBuilder.build());
+        }
+
         return appSearchImpl;
     }
 
+    /** @param initStatsBuilder collects stats for initialization if provided. */
     private AppSearchImpl(
             @NonNull File icingDir,
             @NonNull Context context,
             int userId,
-            @NonNull String globalQuerierPackage)
+            @NonNull String globalQuerierPackage,
+            @Nullable InitializeStats.Builder initStatsBuilder)
             throws AppSearchException {
         mReadWriteLock.writeLock().lock();
 
@@ -211,13 +248,24 @@
                             .setBaseDir(icingDir.getAbsolutePath())
                             .build();
             mIcingSearchEngineLocked = new IcingSearchEngine(options);
-
             mVisibilityStoreLocked =
                     new VisibilityStore(this, context, userId, globalQuerierPackage);
-
             InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
+
+            if (initStatsBuilder != null) {
+                initStatsBuilder
+                        .setStatusCode(
+                                statusProtoToAppSearchException(initializeResultProto.getStatus())
+                                        .getResultCode())
+                        // TODO(b/173532925) how to get DeSyncs value
+                        .setHasDeSync(false);
+                AppSearchLoggerHelper.copyNativeStats(
+                        initializeResultProto.getInitializeStats(), initStatsBuilder);
+            }
+
+            long prepareSchemaAndNamespacesLatencyStartMillis = SystemClock.elapsedRealtime();
             SchemaProto schemaProto;
-            GetAllNamespacesResultProto getAllNamespacesResultProto;
+            GetAllNamespacesResultProto getAllNamespacesResultProto = null;
             try {
                 checkSuccess(initializeResultProto.getStatus());
                 schemaProto = getSchemaProtoLocked();
@@ -225,6 +273,17 @@
                 checkSuccess(getAllNamespacesResultProto.getStatus());
             } catch (AppSearchException e) {
                 Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e);
+                if (initStatsBuilder != null && getAllNamespacesResultProto != null) {
+                    initStatsBuilder
+                            .setStatusCode(
+                                    statusProtoToAppSearchException(
+                                                    getAllNamespacesResultProto.getStatus())
+                                            .getResultCode())
+                            .setPrepareSchemaAndNamespacesLatencyMillis(
+                                    (int)
+                                            (SystemClock.elapsedRealtime()
+                                                    - prepareSchemaAndNamespacesLatencyStartMillis));
+                }
                 // Some error. Reset and see if it fixes it.
                 resetLocked();
                 return;
@@ -240,6 +299,14 @@
             for (String prefixedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
                 addToMap(mNamespaceMapLocked, getPrefix(prefixedNamespace), prefixedNamespace);
             }
+
+            // logging prepare_schema_and_namespaces latency
+            if (initStatsBuilder != null) {
+                initStatsBuilder.setPrepareSchemaAndNamespacesLatencyMillis(
+                        (int)
+                                (SystemClock.elapsedRealtime()
+                                        - prepareSchemaAndNamespacesLatencyStartMillis));
+            }
         } finally {
             mReadWriteLock.writeLock().unlock();
         }
@@ -539,7 +606,7 @@
             addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace());
 
             // Logging stats
-            if (logger != null) {
+            if (logger != null && pStatsBuilder != null) {
                 pStatsBuilder
                         .getGeneralStatsBuilder()
                         .setStatusCode(
@@ -562,7 +629,7 @@
         } finally {
             mReadWriteLock.writeLock().unlock();
 
-            if (logger != null) {
+            if (logger != null && pStatsBuilder != null) {
                 long totalEndTimeMillis = SystemClock.elapsedRealtime();
                 pStatsBuilder
                         .getGeneralStatsBuilder()
@@ -644,6 +711,7 @@
      * @param databaseName The databaseName this query for.
      * @param queryExpression Query String to search.
      * @param searchSpec Spec for setting filters, raw query etc.
+     * @param logger logger to collect query stats
      * @return The results of performing this search. It may contain an empty list of results if no
      *     documents matched the query.
      * @throws AppSearchException on IcingSearchEngine error.
@@ -653,8 +721,17 @@
             @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull String queryExpression,
-            @NonNull SearchSpec searchSpec)
+            @NonNull SearchSpec searchSpec,
+            @Nullable AppSearchLogger logger)
             throws AppSearchException {
+        long totalLatencyStartMillis = SystemClock.elapsedRealtime();
+        SearchStats.Builder sStatsBuilder = null;
+        if (logger != null) {
+            sStatsBuilder =
+                    new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, packageName)
+                            .setDatabase(databaseName);
+        }
+
         mReadWriteLock.readLock().lock();
         try {
             throwIfClosedLocked();
@@ -673,9 +750,15 @@
                     Collections.singleton(createPrefix(packageName, databaseName)),
                     allowedPrefixedSchemas,
                     queryExpression,
-                    searchSpec);
+                    searchSpec,
+                    sStatsBuilder);
         } finally {
             mReadWriteLock.readLock().unlock();
+            if (logger != null && sStatsBuilder != null) {
+                sStatsBuilder.setTotalLatencyMillis(
+                        (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
+                logger.logStats(sStatsBuilder.build());
+            }
         }
     }
 
@@ -689,6 +772,7 @@
      * @param searchSpec Spec for setting filters, raw query etc.
      * @param callerPackageName Package name of the caller, should belong to the {@code callerUid}.
      * @param callerUid UID of the client making the globalQuery call.
+     * @param logger logger to collect globalQuery stats
      * @return The results of performing this search. It may contain an empty list of results if no
      *     documents matched the query.
      * @throws AppSearchException on IcingSearchEngine error.
@@ -698,8 +782,16 @@
             @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec,
             @NonNull String callerPackageName,
-            int callerUid)
+            int callerUid,
+            @Nullable AppSearchLogger logger)
             throws AppSearchException {
+        long totalLatencyStartMillis = SystemClock.elapsedRealtime();
+        SearchStats.Builder sStatsBuilder = null;
+        if (logger != null) {
+            sStatsBuilder =
+                    new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_GLOBAL, callerPackageName);
+        }
+
         mReadWriteLock.readLock().lock();
         try {
             throwIfClosedLocked();
@@ -754,9 +846,19 @@
             }
 
             return doQueryLocked(
-                    prefixFilters, allowedPrefixedSchemas, queryExpression, searchSpec);
+                    prefixFilters,
+                    allowedPrefixedSchemas,
+                    queryExpression,
+                    searchSpec,
+                    sStatsBuilder);
         } finally {
             mReadWriteLock.readLock().unlock();
+
+            if (logger != null && sStatsBuilder != null) {
+                sStatsBuilder.setTotalLatencyMillis(
+                        (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
+                logger.logStats(sStatsBuilder.build());
+            }
         }
     }
 
@@ -794,8 +896,11 @@
             @NonNull Set<String> prefixes,
             @NonNull Set<String> allowedPrefixedSchemas,
             @NonNull String queryExpression,
-            @NonNull SearchSpec searchSpec)
+            @NonNull SearchSpec searchSpec,
+            @Nullable SearchStats.Builder sStatsBuilder)
             throws AppSearchException {
+        long rewriteSearchSpecLatencyStartMillis = SystemClock.elapsedRealtime();
+
         SearchSpecProto.Builder searchSpecBuilder =
                 SearchSpecToProtoConverter.toSearchSpecProto(searchSpec).toBuilder()
                         .setQuery(queryExpression);
@@ -804,9 +909,17 @@
         // sending request to Icing.
         if (!rewriteSearchSpecForPrefixesLocked(
                 searchSpecBuilder, prefixes, allowedPrefixedSchemas)) {
+            if (sStatsBuilder != null) {
+                sStatsBuilder.setRewriteSearchSpecLatencyMillis(
+                        (int)
+                                (SystemClock.elapsedRealtime()
+                                        - rewriteSearchSpecLatencyStartMillis));
+            }
             return new SearchResultPage(Bundle.EMPTY);
         }
 
+        // rewriteSearchSpec, rewriteResultSpec and convertScoringSpec are all counted in
+        // rewriteSearchSpecLatencyMillis
         ResultSpecProto.Builder resultSpecBuilder =
                 SearchSpecToProtoConverter.toResultSpecProto(searchSpec).toBuilder();
 
@@ -822,15 +935,38 @@
             addPerNamespaceResultGroupingsLocked(
                     resultSpecBuilder, prefixes, searchSpec.getResultGroupingLimit());
         }
-        rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes, allowedPrefixedSchemas);
 
+        rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes, allowedPrefixedSchemas);
         ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
+
+        long rewriteSearchSpecLatencyEndMillis = SystemClock.elapsedRealtime();
+
         SearchResultProto searchResultProto =
                 mIcingSearchEngineLocked.search(
                         searchSpecBuilder.build(), scoringSpec, resultSpecBuilder.build());
+
+        if (sStatsBuilder != null) {
+            sStatsBuilder
+                    .setStatusCode(
+                            statusProtoToAppSearchException(searchResultProto.getStatus())
+                                    .getResultCode())
+                    .setRewriteSearchSpecLatencyMillis(
+                            (int)
+                                    (rewriteSearchSpecLatencyEndMillis
+                                            - rewriteSearchSpecLatencyStartMillis));
+            AppSearchLoggerHelper.copyNativeStats(searchResultProto.getQueryStats(), sStatsBuilder);
+        }
+
         checkSuccess(searchResultProto.getStatus());
 
-        return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
+        long rewriteSearchResultLatencyStartMillis = SystemClock.elapsedRealtime();
+        SearchResultPage resultPage = rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
+        if (sStatsBuilder != null) {
+            sStatsBuilder.setRewriteSearchResultLatencyMillis(
+                    (int) (SystemClock.elapsedRealtime() - rewriteSearchResultLatencyStartMillis));
+        }
+
+        return resultPage;
     }
 
     /**
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 0f23d92..97c8869 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
@@ -20,7 +20,9 @@
 import android.app.appsearch.exceptions.AppSearchException;
 
 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.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
 
 /**
  * An interface for implementing client-defined logging AppSearch operations stats.
@@ -39,5 +41,11 @@
     /** Logs {@link PutDocumentStats} */
     void logStats(@NonNull PutDocumentStats stats) throws AppSearchException;
 
+    /** Logs {@link InitializeStats} */
+    void logStats(@NonNull InitializeStats stats) throws AppSearchException;
+
+    /** Logs {@link SearchStats} */
+    void logStats(@NonNull SearchStats stats) throws AppSearchException;
+
     // 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 cdd7952..4a5ecf1 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
@@ -18,9 +18,13 @@
 
 import android.annotation.NonNull;
 
+import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
 import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
 
+import com.google.android.icing.proto.InitializeStatsProto;
 import com.google.android.icing.proto.PutDocumentStatsProto;
+import com.google.android.icing.proto.QueryStatsProto;
 
 import java.util.Objects;
 
@@ -35,7 +39,7 @@
     private AppSearchLoggerHelper() {}
 
     /**
-     * Copies native stats to builder.
+     * Copies native PutDocument stats to builder.
      *
      * @param fromNativeStats stats copied from
      * @param toStatsBuilder stats copied to
@@ -56,4 +60,64 @@
                 .setNativeExceededMaxNumTokens(
                         fromNativeStats.getTokenizationStats().getExceededMaxTokenNum());
     }
+
+    /**
+     * Copies native Initialize stats to builder.
+     *
+     * @param fromNativeStats stats copied from
+     * @param toStatsBuilder stats copied to
+     */
+    static void copyNativeStats(
+            @NonNull InitializeStatsProto fromNativeStats,
+            @NonNull InitializeStats.Builder toStatsBuilder) {
+        Objects.requireNonNull(fromNativeStats);
+        Objects.requireNonNull(toStatsBuilder);
+        toStatsBuilder
+                .setNativeLatencyMillis(fromNativeStats.getLatencyMs())
+                .setDocumentStoreRecoveryCause(
+                        fromNativeStats.getDocumentStoreRecoveryCause().getNumber())
+                .setIndexRestorationCause(fromNativeStats.getIndexRestorationCause().getNumber())
+                .setSchemaStoreRecoveryCause(
+                        fromNativeStats.getSchemaStoreRecoveryCause().getNumber())
+                .setDocumentStoreRecoveryLatencyMillis(
+                        fromNativeStats.getDocumentStoreRecoveryLatencyMs())
+                .setIndexRestorationLatencyMillis(fromNativeStats.getIndexRestorationLatencyMs())
+                .setSchemaStoreRecoveryLatencyMillis(
+                        fromNativeStats.getSchemaStoreRecoveryLatencyMs())
+                .setDocumentStoreDataStatus(
+                        fromNativeStats.getDocumentStoreDataStatus().getNumber())
+                .setDocumentCount(fromNativeStats.getNumDocuments())
+                .setSchemaTypeCount(fromNativeStats.getNumSchemaTypes());
+    }
+
+    /*
+     * Copy native Query stats to buiilder.
+     *
+     * @param fromNativeStats Stats copied from.
+     * @param toStatsBuilder Stats copied to.
+     */
+    static void copyNativeStats(
+            @NonNull QueryStatsProto fromNativeStats, @NonNull SearchStats.Builder toStatsBuilder) {
+        Objects.requireNonNull(fromNativeStats);
+        Objects.requireNonNull(toStatsBuilder);
+        toStatsBuilder
+                .setNativeLatencyMillis(fromNativeStats.getLatencyMs())
+                .setTermCount(fromNativeStats.getNumTerms())
+                // TODO(b/173532925) query length missing in native
+                // .setNativeQueryLength(0)
+                .setFilteredNamespaceCount(fromNativeStats.getNumNamespacesFiltered())
+                .setFilteredSchemaTypeCount(fromNativeStats.getNumSchemaTypesFiltered())
+                .setRequestedPageSize(fromNativeStats.getRequestedPageSize())
+                .setCurrentPageReturnedResultCount(
+                        fromNativeStats.getNumResultsReturnedCurrentPage())
+                .setIsFirstPage(fromNativeStats.getIsFirstPage())
+                .setParseQueryLatencyMillis(fromNativeStats.getParseQueryLatencyMs())
+                .setRankingStrategy(fromNativeStats.getRankingStrategy().getNumber())
+                .setScoredDocumentCount(fromNativeStats.getNumDocumentsScored())
+                .setScoringLatencyMillis(fromNativeStats.getScoringLatencyMs())
+                .setRankingLatencyMillis(fromNativeStats.getRankingLatencyMs())
+                .setResultWithSnippetsCount(fromNativeStats.getNumResultsWithSnippets())
+                .setDocumentRetrievingLatencyMillis(
+                        fromNativeStats.getDocumentRetrievalLatencyMs());
+    }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java
new file mode 100644
index 0000000..5364a0c
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.external.localstorage.stats;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class holds detailed stats for initialization
+ *
+ * @hide
+ */
+public final class InitializeStats {
+    /**
+     * The cause of IcingSearchEngine recovering from a previous bad state during initialization.
+     */
+    @IntDef(
+            value = {
+                // It needs to be sync with RecoveryCause in
+                // external/icing/proto/icing/proto/logging.proto#InitializeStatsProto
+                RECOVERY_CAUSE_NONE,
+                RECOVERY_CAUSE_DATA_LOSS,
+                RECOVERY_CAUSE_INCONSISTENT_WITH_GROUND_TRUTH,
+                RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH,
+                RECOVERY_CAUSE_IO_ERROR,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RecoveryCause {}
+
+    // No recovery happened.
+    public static final int RECOVERY_CAUSE_NONE = 0;
+    // Data loss in ground truth.
+    public static final int RECOVERY_CAUSE_DATA_LOSS = 1;
+    // Data in index is inconsistent with ground truth.
+    public static final int RECOVERY_CAUSE_INCONSISTENT_WITH_GROUND_TRUTH = 2;
+    // Total checksum of all the components does not match.
+    public static final int RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH = 3;
+    // Random I/O errors.
+    public static final int RECOVERY_CAUSE_IO_ERROR = 4;
+
+    /** Status regarding how much data is lost during the initialization. */
+    @IntDef(
+            value = {
+                // It needs to be sync with DocumentStoreDataStatus in
+                // external/icing/proto/icing/proto/logging.proto#InitializeStatsProto
+
+                DOCUMENT_STORE_DATA_STATUS_NO_DATA_LOSS,
+                DOCUMENT_STORE_DATA_STATUS_PARTIAL_LOSS,
+                DOCUMENT_STORE_DATA_STATUS_COMPLETE_LOSS,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DocumentStoreDataStatus {}
+
+    // Document store is successfully initialized or fully recovered.
+    public static final int DOCUMENT_STORE_DATA_STATUS_NO_DATA_LOSS = 0;
+    // Ground truth data is partially lost.
+    public static final int DOCUMENT_STORE_DATA_STATUS_PARTIAL_LOSS = 1;
+    // Ground truth data is completely lost.
+    public static final int DOCUMENT_STORE_DATA_STATUS_COMPLETE_LOSS = 2;
+
+    @AppSearchResult.ResultCode private final int mStatusCode;
+    private final int mTotalLatencyMillis;
+    /** Whether the initialize() detects deSync. */
+    private final boolean mHasDeSync;
+    /** Time used to read and process the schema and namespaces. */
+    private final int mPrepareSchemaAndNamespacesLatencyMillis;
+    /** Time used to read and process the visibility store. */
+    private final int mPrepareVisibilityStoreLatencyMillis;
+    /** Overall time used for the native function call. */
+    private final int mNativeLatencyMillis;
+
+    @RecoveryCause private final int mNativeDocumentStoreRecoveryCause;
+    @RecoveryCause private final int mNativeIndexRestorationCause;
+    @RecoveryCause private final int mNativeSchemaStoreRecoveryCause;
+    /** Time used to recover the document store. */
+    private final int mNativeDocumentStoreRecoveryLatencyMillis;
+    /** Time used to restore the index. */
+    private final int mNativeIndexRestorationLatencyMillis;
+    /** Time used to recover the schema store. */
+    private final int mNativeSchemaStoreRecoveryLatencyMillis;
+    /** Status regarding how much data is lost during the initialization. */
+    private final int mNativeDocumentStoreDataStatus;
+    /**
+     * Returns number of documents currently in document store. Those may include alive, deleted,
+     * and expired documents.
+     */
+    private final int mNativeNumDocuments;
+    /** Returns number of schema types currently in the schema store. */
+    private final int mNativeNumSchemaTypes;
+
+    /** Returns the status of the initialization. */
+    @AppSearchResult.ResultCode
+    public int getStatusCode() {
+        return mStatusCode;
+    }
+
+    /** Returns the total latency in milliseconds for the initialization. */
+    public int getTotalLatencyMillis() {
+        return mTotalLatencyMillis;
+    }
+
+    /**
+     * Returns whether the initialize() detects deSync.
+     *
+     * <p>If there is a deSync, it means AppSearch and IcingSearchEngine have an inconsistent view
+     * of what data should exist.
+     */
+    public boolean hasDeSync() {
+        return mHasDeSync;
+    }
+
+    /** Returns time used to read and process the schema and namespaces. */
+    public int getPrepareSchemaAndNamespacesLatencyMillis() {
+        return mPrepareSchemaAndNamespacesLatencyMillis;
+    }
+
+    /** Returns time used to read and process the visibility file. */
+    public int getPrepareVisibilityStoreLatencyMillis() {
+        return mPrepareVisibilityStoreLatencyMillis;
+    }
+
+    /** Returns overall time used for the native function call. */
+    public int getNativeLatencyMillis() {
+        return mNativeLatencyMillis;
+    }
+
+    /**
+     * Returns recovery cause for document store.
+     *
+     * <p>Possible recovery causes for document store:
+     * <li>{@link InitializeStats#RECOVERY_CAUSE_DATA_LOSS}
+     * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
+     * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
+     */
+    @RecoveryCause
+    public int getDocumentStoreRecoveryCause() {
+        return mNativeDocumentStoreRecoveryCause;
+    }
+
+    /**
+     * Returns restoration cause for index store.
+     *
+     * <p>Possible causes:
+     * <li>{@link InitializeStats#RECOVERY_CAUSE_INCONSISTENT_WITH_GROUND_TRUTH}
+     * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
+     * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
+     */
+    @RecoveryCause
+    public int getIndexRestorationCause() {
+        return mNativeIndexRestorationCause;
+    }
+
+    /**
+     * Returns recovery cause for schema store.
+     *
+     * <p>Possible causes:
+     * <li>IO_ERROR
+     */
+    @RecoveryCause
+    public int getSchemaStoreRecoveryCause() {
+        return mNativeSchemaStoreRecoveryCause;
+    }
+
+    /** Returns time used to recover the document store. */
+    public int getDocumentStoreRecoveryLatencyMillis() {
+        return mNativeDocumentStoreRecoveryLatencyMillis;
+    }
+
+    /** Returns time used to restore the index. */
+    public int getIndexRestorationLatencyMillis() {
+        return mNativeIndexRestorationLatencyMillis;
+    }
+
+    /** Returns time used to recover the schema store. */
+    public int getSchemaStoreRecoveryLatencyMillis() {
+        return mNativeSchemaStoreRecoveryLatencyMillis;
+    }
+
+    /** Returns status about how much data is lost during the initialization. */
+    @DocumentStoreDataStatus
+    public int getDocumentStoreDataStatus() {
+        return mNativeDocumentStoreDataStatus;
+    }
+
+    /**
+     * Returns number of documents currently in document store. Those may include alive, deleted,
+     * and expired documents.
+     */
+    public int getDocumentCount() {
+        return mNativeNumDocuments;
+    }
+
+    /** Returns number of schema types currently in the schema store. */
+    public int getSchemaTypeCount() {
+        return mNativeNumSchemaTypes;
+    }
+
+    InitializeStats(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+        mStatusCode = builder.mStatusCode;
+        mTotalLatencyMillis = builder.mTotalLatencyMillis;
+        mHasDeSync = builder.mHasDeSync;
+        mPrepareSchemaAndNamespacesLatencyMillis = builder.mPrepareSchemaAndNamespacesLatencyMillis;
+        mPrepareVisibilityStoreLatencyMillis = builder.mPrepareVisibilityStoreLatencyMillis;
+        mNativeLatencyMillis = builder.mNativeLatencyMillis;
+        mNativeDocumentStoreRecoveryCause = builder.mNativeDocumentStoreRecoveryCause;
+        mNativeIndexRestorationCause = builder.mNativeIndexRestorationCause;
+        mNativeSchemaStoreRecoveryCause = builder.mNativeSchemaStoreRecoveryCause;
+        mNativeDocumentStoreRecoveryLatencyMillis =
+                builder.mNativeDocumentStoreRecoveryLatencyMillis;
+        mNativeIndexRestorationLatencyMillis = builder.mNativeIndexRestorationLatencyMillis;
+        mNativeSchemaStoreRecoveryLatencyMillis = builder.mNativeSchemaStoreRecoveryLatencyMillis;
+        mNativeDocumentStoreDataStatus = builder.mNativeDocumentStoreDataStatus;
+        mNativeNumDocuments = builder.mNativeNumDocuments;
+        mNativeNumSchemaTypes = builder.mNativeNumSchemaTypes;
+    }
+
+    /** Builder for {@link InitializeStats}. */
+    public static class Builder {
+        @AppSearchResult.ResultCode int mStatusCode;
+        int mTotalLatencyMillis;
+        boolean mHasDeSync;
+        int mPrepareSchemaAndNamespacesLatencyMillis;
+        int mPrepareVisibilityStoreLatencyMillis;
+        int mNativeLatencyMillis;
+        @RecoveryCause int mNativeDocumentStoreRecoveryCause;
+        @RecoveryCause int mNativeIndexRestorationCause;
+        @RecoveryCause int mNativeSchemaStoreRecoveryCause;
+        int mNativeDocumentStoreRecoveryLatencyMillis;
+        int mNativeIndexRestorationLatencyMillis;
+        int mNativeSchemaStoreRecoveryLatencyMillis;
+        @DocumentStoreDataStatus int mNativeDocumentStoreDataStatus;
+        int mNativeNumDocuments;
+        int mNativeNumSchemaTypes;
+
+        /** Sets the status of the initialization. */
+        @NonNull
+        public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
+            mStatusCode = statusCode;
+            return this;
+        }
+
+        /** Sets the total latency of the initialization in milliseconds. */
+        @NonNull
+        public Builder setTotalLatencyMillis(int totalLatencyMillis) {
+            mTotalLatencyMillis = totalLatencyMillis;
+            return this;
+        }
+
+        /**
+         * Sets whether the initialize() detects deSync.
+         *
+         * <p>If there is a deSync, it means AppSearch and IcingSearchEngine have an inconsistent
+         * view of what data should exist.
+         */
+        @NonNull
+        public Builder setHasDeSync(boolean hasDeSync) {
+            mHasDeSync = hasDeSync;
+            return this;
+        }
+
+        /** Sets time used to read and process the schema and namespaces. */
+        @NonNull
+        public Builder setPrepareSchemaAndNamespacesLatencyMillis(
+                int prepareSchemaAndNamespacesLatencyMillis) {
+            mPrepareSchemaAndNamespacesLatencyMillis = prepareSchemaAndNamespacesLatencyMillis;
+            return this;
+        }
+
+        /** Sets time used to read and process the visibility file. */
+        @NonNull
+        public Builder setPrepareVisibilityStoreLatencyMillis(
+                int prepareVisibilityStoreLatencyMillis) {
+            mPrepareVisibilityStoreLatencyMillis = prepareVisibilityStoreLatencyMillis;
+            return this;
+        }
+
+        /** Sets overall time used for the native function call. */
+        @NonNull
+        public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
+            mNativeLatencyMillis = nativeLatencyMillis;
+            return this;
+        }
+
+        /**
+         * Sets recovery cause for document store.
+         *
+         * <p>Possible recovery causes for document store:
+         * <li>{@link InitializeStats#RECOVERY_CAUSE_DATA_LOSS}
+         * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
+         * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
+         */
+        @NonNull
+        public Builder setDocumentStoreRecoveryCause(
+                @RecoveryCause int documentStoreRecoveryCause) {
+            mNativeDocumentStoreRecoveryCause = documentStoreRecoveryCause;
+            return this;
+        }
+
+        /**
+         * Sets restoration cause for index store.
+         *
+         * <p>Possible causes:
+         * <li>{@link InitializeStats#DOCUMENT_STORE_DATA_STATUS_COMPLETE_LOSS}
+         * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
+         * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
+         */
+        @NonNull
+        public Builder setIndexRestorationCause(@RecoveryCause int indexRestorationCause) {
+            mNativeIndexRestorationCause = indexRestorationCause;
+            return this;
+        }
+
+        /**
+         * Returns recovery cause for schema store.
+         *
+         * <p>Possible causes:
+         * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
+         */
+        @NonNull
+        public Builder setSchemaStoreRecoveryCause(@RecoveryCause int schemaStoreRecoveryCause) {
+            mNativeSchemaStoreRecoveryCause = schemaStoreRecoveryCause;
+            return this;
+        }
+
+        /** Sets time used to recover the document store. */
+        @NonNull
+        public Builder setDocumentStoreRecoveryLatencyMillis(
+                int documentStoreRecoveryLatencyMillis) {
+            mNativeDocumentStoreRecoveryLatencyMillis = documentStoreRecoveryLatencyMillis;
+            return this;
+        }
+
+        /** Sets time used to restore the index. */
+        @NonNull
+        public Builder setIndexRestorationLatencyMillis(int indexRestorationLatencyMillis) {
+            mNativeIndexRestorationLatencyMillis = indexRestorationLatencyMillis;
+            return this;
+        }
+
+        /** Sets time used to recover the schema store. */
+        @NonNull
+        public Builder setSchemaStoreRecoveryLatencyMillis(int schemaStoreRecoveryLatencyMillis) {
+            mNativeSchemaStoreRecoveryLatencyMillis = schemaStoreRecoveryLatencyMillis;
+            return this;
+        }
+
+        /**
+         * Sets Native Document Store Data status. status is defined in
+         * external/icing/proto/icing/proto/logging.proto
+         */
+        @NonNull
+        public Builder setDocumentStoreDataStatus(
+                @DocumentStoreDataStatus int documentStoreDataStatus) {
+            mNativeDocumentStoreDataStatus = documentStoreDataStatus;
+            return this;
+        }
+
+        /**
+         * Sets number of documents currently in document store. Those may include alive, deleted,
+         * and expired documents.
+         */
+        @NonNull
+        public Builder setDocumentCount(int numDocuments) {
+            mNativeNumDocuments = numDocuments;
+            return this;
+        }
+
+        /** Sets number of schema types currently in the schema store. */
+        @NonNull
+        public Builder setSchemaTypeCount(int numSchemaTypes) {
+            mNativeNumSchemaTypes = numSchemaTypes;
+            return this;
+        }
+
+        /**
+         * Constructs a new {@link InitializeStats} from the contents of this {@link
+         * InitializeStats.Builder}
+         */
+        @NonNull
+        public InitializeStats build() {
+            return new InitializeStats(/* builder= */ this);
+        }
+    }
+}
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
new file mode 100644
index 0000000..7b22b2f
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java
@@ -0,0 +1,460 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appsearch.external.localstorage.stats;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.SearchSpec;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class holds detailed stats for {@link android.app.appsearch.AppSearchSession#search(String,
+ * SearchSpec)}
+ *
+ * @hide
+ */
+public final class SearchStats {
+    @IntDef(
+            value = {
+                // Searches apps' own documents.
+                VISIBILITY_SCOPE_LOCAL,
+                // Searches the global documents. Including platform surfaceable and 3p-access.
+                VISIBILITY_SCOPE_GLOBAL,
+                // 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)
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VisibilityScope {}
+
+    // Searches apps' own documents.
+    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;
+
+    @NonNull private final String mPackageName;
+    @Nullable private final String mDatabase;
+    /**
+     * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal
+     * state.
+     */
+    @AppSearchResult.ResultCode private final int mStatusCode;
+
+    private final int mTotalLatencyMillis;
+    /** Time used to rewrite the search spec. */
+    private final int mRewriteSearchSpecLatencyMillis;
+    /** Time used to rewrite the search results. */
+    private final int mRewriteSearchResultLatencyMillis;
+    /** Defines the scope the query is searching over */
+    @VisibilityScope private final int mVisibilityScope;
+    /** Overall time used for the native function call. */
+    private final int mNativeLatencyMillis;
+    /** Number of terms in the query string. */
+    private final int mNativeNumTerms;
+    /** Length of the query string. */
+    private final int mNativeQueryLength;
+    /** Number of namespaces filtered. */
+    private final int mNativeNumNamespacesFiltered;
+    /** Number of schema types filtered. */
+    private final int mNativeNumSchemaTypesFiltered;
+    /** The requested number of results in one page. */
+    private final int mNativeRequestedPageSize;
+    /** The actual number of results returned in the current page. */
+    private final int mNativeNumResultsReturnedCurrentPage;
+    /**
+     * Whether the function call is querying the first page. If it's not, Icing will fetch the
+     * results from cache so that some steps may be skipped.
+     */
+    private final boolean mNativeIsFirstPage;
+    /**
+     * Time used to parse the query, including 2 parts: tokenizing and transforming tokens into an
+     * iterator tree.
+     */
+    private final int mNativeParseQueryLatencyMillis;
+    /** Strategy of scoring and ranking. */
+    @SearchSpec.RankingStrategy private final int mNativeRankingStrategy;
+    /** Number of documents scored. */
+    private final int mNativeNumDocumentsScored;
+    /** Time used to score the raw results. */
+    private final int mNativeScoringLatencyMillis;
+    /** Time used to rank the scored results. */
+    private final int mNativeRankingLatencyMillis;
+    /**
+     * Time used to fetch the document protos. Note that it includes the time to snippet if {@link
+     * SearchStats#mNativeNumResultsWithSnippets} is greater than 0.
+     */
+    private final int mNativeDocumentRetrievingLatencyMillis;
+    /** How many snippets are calculated. */
+    private final int mNativeNumResultsWithSnippets;
+
+    SearchStats(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+        mPackageName = builder.mPackageName;
+        mDatabase = builder.mDatabase;
+        mStatusCode = builder.mStatusCode;
+        mTotalLatencyMillis = builder.mTotalLatencyMillis;
+        mRewriteSearchSpecLatencyMillis = builder.mRewriteSearchSpecLatencyMillis;
+        mRewriteSearchResultLatencyMillis = builder.mRewriteSearchResultLatencyMillis;
+        mVisibilityScope = builder.mVisibilityScope;
+        mNativeLatencyMillis = builder.mNativeLatencyMillis;
+        mNativeNumTerms = builder.mNativeNumTerms;
+        mNativeQueryLength = builder.mNativeQueryLength;
+        mNativeNumNamespacesFiltered = builder.mNativeNumNamespacesFiltered;
+        mNativeNumSchemaTypesFiltered = builder.mNativeNumSchemaTypesFiltered;
+        mNativeRequestedPageSize = builder.mNativeRequestedPageSize;
+        mNativeNumResultsReturnedCurrentPage = builder.mNativeNumResultsReturnedCurrentPage;
+        mNativeIsFirstPage = builder.mNativeIsFirstPage;
+        mNativeParseQueryLatencyMillis = builder.mNativeParseQueryLatencyMillis;
+        mNativeRankingStrategy = builder.mNativeRankingStrategy;
+        mNativeNumDocumentsScored = builder.mNativeNumDocumentsScored;
+        mNativeScoringLatencyMillis = builder.mNativeScoringLatencyMillis;
+        mNativeRankingLatencyMillis = builder.mNativeRankingLatencyMillis;
+        mNativeNumResultsWithSnippets = builder.mNativeNumResultsWithSnippets;
+        mNativeDocumentRetrievingLatencyMillis = builder.mNativeDocumentRetrievingLatencyMillis;
+    }
+
+    /** Returns the package name of the session. */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Returns the database name of the session.
+     *
+     * @return database name used by the session. {@code null} if and only if it is a global
+     *     search(visibilityScope is {@link SearchStats#VISIBILITY_SCOPE_GLOBAL}).
+     */
+    @Nullable
+    public String getDatabase() {
+        return mDatabase;
+    }
+
+    /** Returns status of the search. */
+    @AppSearchResult.ResultCode
+    public int getStatusCode() {
+        return mStatusCode;
+    }
+
+    /** Returns the total latency of the search. */
+    public int getTotalLatencyMillis() {
+        return mTotalLatencyMillis;
+    }
+
+    /** Returns how much time spent on rewriting the {@link SearchSpec}. */
+    public int getRewriteSearchSpecLatencyMillis() {
+        return mRewriteSearchSpecLatencyMillis;
+    }
+
+    /** Returns how much time spent on rewriting the {@link android.app.appsearch.SearchResult}. */
+    public int getRewriteSearchResultLatencyMillis() {
+        return mRewriteSearchResultLatencyMillis;
+    }
+
+    /** Returns the visibility scope of the search. */
+    @VisibilityScope
+    public int getVisibilityScope() {
+        return mVisibilityScope;
+    }
+
+    /** Returns how much time spent on the native calls. */
+    public int getNativeLatencyMillis() {
+        return mNativeLatencyMillis;
+    }
+
+    /** Returns number of terms in the search string. */
+    public int getTermCount() {
+        return mNativeNumTerms;
+    }
+
+    /** Returns the length of the search string. */
+    public int getQueryLength() {
+        return mNativeQueryLength;
+    }
+
+    /** Returns number of namespaces filtered. */
+    public int getFilteredNamespaceCount() {
+        return mNativeNumNamespacesFiltered;
+    }
+
+    /** Returns number of schema types filtered. */
+    public int getFilteredSchemaTypeCount() {
+        return mNativeNumSchemaTypesFiltered;
+    }
+
+    /** Returns the requested number of results in one page. */
+    public int getRequestedPageSize() {
+        return mNativeRequestedPageSize;
+    }
+
+    /** Returns the actual number of results returned in the current page. */
+    public int getCurrentPageReturnedResultCount() {
+        return mNativeNumResultsReturnedCurrentPage;
+    }
+
+    // TODO(b/185184738) Make it an integer to show how many pages having been returned.
+    /** Returns whether the function call is querying the first page. */
+    public boolean isFirstPage() {
+        return mNativeIsFirstPage;
+    }
+
+    /**
+     * Returns time used to parse the query, including 2 parts: tokenizing and transforming tokens
+     * into an iterator tree.
+     */
+    public int getParseQueryLatencyMillis() {
+        return mNativeParseQueryLatencyMillis;
+    }
+
+    /** Returns strategy of scoring and ranking. */
+    @SearchSpec.RankingStrategy
+    public int getRankingStrategy() {
+        return mNativeRankingStrategy;
+    }
+
+    /** Returns number of documents scored. */
+    public int getScoredDocumentCount() {
+        return mNativeNumDocumentsScored;
+    }
+
+    /** Returns time used to score the raw results. */
+    public int getScoringLatencyMillis() {
+        return mNativeScoringLatencyMillis;
+    }
+
+    /** Returns time used to rank the scored results. */
+    public int getRankingLatencyMillis() {
+        return mNativeRankingLatencyMillis;
+    }
+
+    /**
+     * Returns time used to fetch the document protos. Note that it includes the time to snippet if
+     * {@link SearchStats#mNativeNumResultsWithSnippets} is not zero.
+     */
+    public int getDocumentRetrievingLatencyMillis() {
+        return mNativeDocumentRetrievingLatencyMillis;
+    }
+
+    /** Returns the number of the results in the page returned were snippeted. */
+    public int getResultWithSnippetsCount() {
+        return mNativeNumResultsWithSnippets;
+    }
+
+    /** Builder for {@link SearchStats} */
+    public static class Builder {
+        @NonNull final String mPackageName;
+        @Nullable String mDatabase;
+        @AppSearchResult.ResultCode int mStatusCode;
+        int mTotalLatencyMillis;
+        int mRewriteSearchSpecLatencyMillis;
+        int mRewriteSearchResultLatencyMillis;
+        int mVisibilityScope;
+        int mNativeLatencyMillis;
+        int mNativeNumTerms;
+        int mNativeQueryLength;
+        int mNativeNumNamespacesFiltered;
+        int mNativeNumSchemaTypesFiltered;
+        int mNativeRequestedPageSize;
+        int mNativeNumResultsReturnedCurrentPage;
+        boolean mNativeIsFirstPage;
+        int mNativeParseQueryLatencyMillis;
+        int mNativeRankingStrategy;
+        int mNativeNumDocumentsScored;
+        int mNativeScoringLatencyMillis;
+        int mNativeRankingLatencyMillis;
+        int mNativeNumResultsWithSnippets;
+        int mNativeDocumentRetrievingLatencyMillis;
+
+        /**
+         * Constructor
+         *
+         * @param visibilityScope scope for the corresponding search.
+         * @param packageName name of the calling package.
+         */
+        public Builder(@VisibilityScope int visibilityScope, @NonNull String packageName) {
+            mVisibilityScope = visibilityScope;
+            mPackageName = Objects.requireNonNull(packageName);
+        }
+
+        /** Sets the database used by the session. */
+        @NonNull
+        public Builder setDatabase(@NonNull String database) {
+            mDatabase = Objects.requireNonNull(database);
+            return this;
+        }
+
+        /** Sets the status of the search. */
+        @NonNull
+        public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
+            mStatusCode = statusCode;
+            return this;
+        }
+
+        /** Sets total latency for the search. */
+        @NonNull
+        public Builder setTotalLatencyMillis(int totalLatencyMillis) {
+            mTotalLatencyMillis = totalLatencyMillis;
+            return this;
+        }
+
+        /** Sets time used to rewrite the search spec. */
+        @NonNull
+        public Builder setRewriteSearchSpecLatencyMillis(int rewriteSearchSpecLatencyMillis) {
+            mRewriteSearchSpecLatencyMillis = rewriteSearchSpecLatencyMillis;
+            return this;
+        }
+
+        /** Sets time used to rewrite the search results. */
+        @NonNull
+        public Builder setRewriteSearchResultLatencyMillis(int rewriteSearchResultLatencyMillis) {
+            mRewriteSearchResultLatencyMillis = rewriteSearchResultLatencyMillis;
+            return this;
+        }
+
+        /** Sets overall time used for the native function calls. */
+        @NonNull
+        public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
+            mNativeLatencyMillis = nativeLatencyMillis;
+            return this;
+        }
+
+        /** Sets number of terms in the search string. */
+        @NonNull
+        public Builder setTermCount(int termCount) {
+            mNativeNumTerms = termCount;
+            return this;
+        }
+
+        /** Sets length of the search string. */
+        @NonNull
+        public Builder setQueryLength(int queryLength) {
+            mNativeQueryLength = queryLength;
+            return this;
+        }
+
+        /** Sets number of namespaces filtered. */
+        @NonNull
+        public Builder setFilteredNamespaceCount(int filteredNamespaceCount) {
+            mNativeNumNamespacesFiltered = filteredNamespaceCount;
+            return this;
+        }
+
+        /** Sets number of schema types filtered. */
+        @NonNull
+        public Builder setFilteredSchemaTypeCount(int filteredSchemaTypeCount) {
+            mNativeNumSchemaTypesFiltered = filteredSchemaTypeCount;
+            return this;
+        }
+
+        /** Sets the requested number of results in one page. */
+        @NonNull
+        public Builder setRequestedPageSize(int requestedPageSize) {
+            mNativeRequestedPageSize = requestedPageSize;
+            return this;
+        }
+
+        /** Sets the actual number of results returned in the current page. */
+        @NonNull
+        public Builder setCurrentPageReturnedResultCount(int currentPageReturnedResultCount) {
+            mNativeNumResultsReturnedCurrentPage = currentPageReturnedResultCount;
+            return this;
+        }
+
+        /**
+         * Sets whether the function call is querying the first page. If it's not, Icing will fetch
+         * the results from cache so that some steps may be skipped.
+         */
+        @NonNull
+        public Builder setIsFirstPage(boolean nativeIsFirstPage) {
+            mNativeIsFirstPage = nativeIsFirstPage;
+            return this;
+        }
+
+        /**
+         * Sets time used to parse the query, including 2 parts: tokenizing and transforming tokens
+         * into an iterator tree.
+         */
+        @NonNull
+        public Builder setParseQueryLatencyMillis(int parseQueryLatencyMillis) {
+            mNativeParseQueryLatencyMillis = parseQueryLatencyMillis;
+            return this;
+        }
+
+        /** Sets strategy of scoring and ranking. */
+        @NonNull
+        public Builder setRankingStrategy(@SearchSpec.RankingStrategy int rankingStrategy) {
+            mNativeRankingStrategy = rankingStrategy;
+            return this;
+        }
+
+        /** Sets number of documents scored. */
+        @NonNull
+        public Builder setScoredDocumentCount(int scoredDocumentCount) {
+            mNativeNumDocumentsScored = scoredDocumentCount;
+            return this;
+        }
+
+        /** Sets time used to score the raw results. */
+        @NonNull
+        public Builder setScoringLatencyMillis(int scoringLatencyMillis) {
+            mNativeScoringLatencyMillis = scoringLatencyMillis;
+            return this;
+        }
+
+        /** Sets time used to rank the scored results. */
+        @NonNull
+        public Builder setRankingLatencyMillis(int rankingLatencyMillis) {
+            mNativeRankingLatencyMillis = rankingLatencyMillis;
+            return this;
+        }
+
+        /** Sets time used to fetch the document protos. */
+        @NonNull
+        public Builder setDocumentRetrievingLatencyMillis(int documentRetrievingLatencyMillis) {
+            mNativeDocumentRetrievingLatencyMillis = documentRetrievingLatencyMillis;
+            return this;
+        }
+
+        /** Sets how many snippets are calculated. */
+        @NonNull
+        public Builder setResultWithSnippetsCount(int resultWithSnippetsCount) {
+            mNativeNumResultsWithSnippets = resultWithSnippetsCount;
+            return this;
+        }
+
+        /**
+         * Constructs a new {@link SearchStats} from the contents of this {@link
+         * SearchStats.Builder}.
+         */
+        @NonNull
+        public SearchStats build() {
+            if (mDatabase == null) {
+                Preconditions.checkState(
+                        mVisibilityScope != SearchStats.VISIBILITY_SCOPE_LOCAL,
+                        "database can not be null if visibilityScope is local.");
+            }
+
+            return new SearchStats(/* builder= */ this);
+        }
+    }
+}
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 731ab35..88f238e 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
@@ -31,7 +31,9 @@
 import com.android.internal.util.FrameworkStatsLog;
 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.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
 
 import java.io.UnsupportedEncodingException;
 import java.security.MessageDigest;
@@ -189,6 +191,16 @@
         }
     }
 
+    @Override
+    public void logStats(@NonNull InitializeStats stats) throws AppSearchException {
+        // TODO(b/173532925): Implement
+    }
+
+    @Override
+    public void logStats(@NonNull SearchStats stats) throws AppSearchException {
+        // TODO(b/173532925): Implement
+    }
+
     /**
      * Removes cached UID for package.
      *
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index ce0ebe6..10bba13 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I06df2c636d26419e653c5d8c9e7d15449da6816e
+I19dac52031c47099f621eced9f148931f1021f25
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 5729385..17682a5 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1513,6 +1513,11 @@
          * available quota (and the job will not be successfully scheduled).
          *
          * <p>
+         * Expedited job quota will replenish over time and as the user interacts with the app,
+         * so you should not have to worry about running out of quota because of processing from
+         * frequent user engagement.
+         *
+         * <p>
          * Expedited jobs may only set network, storage-not-low, and persistence constraints.
          * No other constraints are allowed.
          *
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 48b62a6..32655c7 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -45,25 +45,25 @@
 
     /** @hide */
     public static final int INTERNAL_STOP_REASON_CANCELED =
-            JobProtoEnums.STOP_REASON_CANCELLED; // 0.
+            JobProtoEnums.INTERNAL_STOP_REASON_CANCELLED; // 0.
     /** @hide */
     public static final int INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED =
-            JobProtoEnums.STOP_REASON_CONSTRAINTS_NOT_SATISFIED; // 1.
+            JobProtoEnums.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED; // 1.
     /** @hide */
     public static final int INTERNAL_STOP_REASON_PREEMPT =
-            JobProtoEnums.STOP_REASON_PREEMPT; // 2.
+            JobProtoEnums.INTERNAL_STOP_REASON_PREEMPT; // 2.
     /**
      * The job ran for at least its minimum execution limit.
      * @hide
      */
     public static final int INTERNAL_STOP_REASON_TIMEOUT =
-            JobProtoEnums.STOP_REASON_TIMEOUT; // 3.
+            JobProtoEnums.INTERNAL_STOP_REASON_TIMEOUT; // 3.
     /** @hide */
     public static final int INTERNAL_STOP_REASON_DEVICE_IDLE =
-            JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
+            JobProtoEnums.INTERNAL_STOP_REASON_DEVICE_IDLE; // 4.
     /** @hide */
     public static final int INTERNAL_STOP_REASON_DEVICE_THERMAL =
-            JobProtoEnums.STOP_REASON_DEVICE_THERMAL; // 5.
+            JobProtoEnums.INTERNAL_STOP_REASON_DEVICE_THERMAL; // 5.
     /**
      * The job is in the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
      * bucket.
@@ -71,26 +71,30 @@
      * @hide
      */
     public static final int INTERNAL_STOP_REASON_RESTRICTED_BUCKET =
-            JobProtoEnums.STOP_REASON_RESTRICTED_BUCKET; // 6.
+            JobProtoEnums.INTERNAL_STOP_REASON_RESTRICTED_BUCKET; // 6.
     /**
      * The app was uninstalled.
      * @hide
      */
-    public static final int INTERNAL_STOP_REASON_UNINSTALL = 7;
+    public static final int INTERNAL_STOP_REASON_UNINSTALL =
+            JobProtoEnums.INTERNAL_STOP_REASON_UNINSTALL; // 7.
     /**
      * The app's data was cleared.
      * @hide
      */
-    public static final int INTERNAL_STOP_REASON_DATA_CLEARED = 8;
+    public static final int INTERNAL_STOP_REASON_DATA_CLEARED =
+            JobProtoEnums.INTERNAL_STOP_REASON_DATA_CLEARED; // 8.
     /**
      * @hide
      */
-    public static final int INTERNAL_STOP_REASON_RTC_UPDATED = 9;
+    public static final int INTERNAL_STOP_REASON_RTC_UPDATED =
+            JobProtoEnums.INTERNAL_STOP_REASON_RTC_UPDATED; // 9.
     /**
      * The app called jobFinished() on its own.
      * @hide
      */
-    public static final int INTERNAL_STOP_REASON_SUCCESSFUL_FINISH = 10;
+    public static final int INTERNAL_STOP_REASON_SUCCESSFUL_FINISH =
+            JobProtoEnums.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH; // 10.
 
     /**
      * All the stop reason codes. This should be regarded as an immutable array at runtime.
@@ -109,6 +113,10 @@
             INTERNAL_STOP_REASON_DEVICE_IDLE,
             INTERNAL_STOP_REASON_DEVICE_THERMAL,
             INTERNAL_STOP_REASON_RESTRICTED_BUCKET,
+            INTERNAL_STOP_REASON_UNINSTALL,
+            INTERNAL_STOP_REASON_DATA_CLEARED,
+            INTERNAL_STOP_REASON_RTC_UPDATED,
+            INTERNAL_STOP_REASON_SUCCESSFUL_FINISH,
     };
 
     /**
@@ -363,14 +371,6 @@
     }
 
     /**
-     * @deprecated Use {@link #isExpeditedJob()} instead.
-     */
-    @Deprecated
-    public boolean isForegroundJob() {
-        return mIsExpedited;
-    }
-
-    /**
      * For jobs with {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)} set, this
      * provides an easy way to tell whether the job is being executed due to the deadline
      * expiring. Note: If the job is running because its deadline expired, it implies that its
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index aafeb48..54e47cf 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -43,6 +43,11 @@
 import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE;
 import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION;
 import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_ALARM_CANCELLED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_DATA_CLEARED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_EXACT_PERMISSION_REVOKED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_PI_CANCELLED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_UNDEFINED;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -116,6 +121,7 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.LocalLog;
+import com.android.internal.util.RingBuffer;
 import com.android.internal.util.StatLogger;
 import com.android.server.AlarmManagerInternal;
 import com.android.server.AppStateTracker;
@@ -160,6 +166,7 @@
 public class AlarmManagerService extends SystemService {
     private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
     private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP;
+    private static final int REMOVAL_HISTORY_SIZE_PER_UID = 10;
     static final int TIME_CHANGED_MASK = 1 << 16;
     static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK | ELAPSED_REALTIME_WAKEUP_MASK;
 
@@ -238,6 +245,7 @@
     AppWakeupHistory mAppWakeupHistory;
     AppWakeupHistory mAllowWhileIdleHistory;
     private final SparseLongArray mLastPriorityAlarmDispatch = new SparseLongArray();
+    private final SparseArray<RingBuffer<RemovedAlarm>> mRemovalHistory = new SparseArray<>();
     ClockReceiver mClockReceiver;
     final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
     IBinder.DeathRecipient mListenerDeathRecipient;
@@ -392,6 +400,57 @@
         }
     }
 
+    static class RemovedAlarm {
+        static final int REMOVE_REASON_UNDEFINED = 0;
+        static final int REMOVE_REASON_ALARM_CANCELLED = 1;
+        static final int REMOVE_REASON_EXACT_PERMISSION_REVOKED = 2;
+        static final int REMOVE_REASON_DATA_CLEARED = 3;
+        static final int REMOVE_REASON_PI_CANCELLED = 4;
+
+        final String mTag;
+        final long mWhenRemovedElapsed;
+        final long mWhenRemovedRtc;
+        final int mRemoveReason;
+
+        RemovedAlarm(Alarm a, int removeReason, long nowRtc, long nowElapsed) {
+            mTag = a.statsTag;
+            mRemoveReason = removeReason;
+            mWhenRemovedRtc = nowRtc;
+            mWhenRemovedElapsed = nowElapsed;
+        }
+
+        static final boolean isLoggable(int reason) {
+            // We don't want to log meaningless reasons. This also gives a way for callers to
+            // opt out of logging, e.g. when replacing an alarm.
+            return reason != REMOVE_REASON_UNDEFINED;
+        }
+
+        static final String removeReasonToString(int reason) {
+            switch (reason) {
+                case REMOVE_REASON_ALARM_CANCELLED:
+                    return "alarm_cancelled";
+                case REMOVE_REASON_EXACT_PERMISSION_REVOKED:
+                    return "exact_alarm_permission_revoked";
+                case REMOVE_REASON_DATA_CLEARED:
+                    return "data_cleared";
+                case REMOVE_REASON_PI_CANCELLED:
+                    return "pi_cancelled";
+                default:
+                    return "unknown:" + reason;
+            }
+        }
+
+        void dump(IndentingPrintWriter pw, long nowElapsed, SimpleDateFormat sdf) {
+            pw.print("[tag", mTag);
+            pw.print("reason", removeReasonToString(mRemoveReason));
+            pw.print("elapsed=");
+            TimeUtils.formatDuration(mWhenRemovedElapsed, nowElapsed, pw);
+            pw.print(" rtc=");
+            pw.print(sdf.format(new Date(mWhenRemovedRtc)));
+            pw.println("]");
+        }
+    }
+
     /**
      * All times are in milliseconds. These constants are kept synchronized with the system
      * global Settings. Any access to this class or its fields should be done while
@@ -1691,7 +1750,7 @@
 
     void removeImpl(PendingIntent operation, IAlarmListener listener) {
         synchronized (mLock) {
-            removeLocked(operation, listener);
+            removeLocked(operation, listener, REMOVE_REASON_UNDEFINED);
         }
     }
 
@@ -1812,7 +1871,7 @@
                     + " -- package not allowed to start");
             return;
         }
-        removeLocked(operation, directReceiver);
+        removeLocked(operation, directReceiver, REMOVE_REASON_UNDEFINED);
         incrementAlarmCount(a.uid);
         setImplLocked(a);
         MetricsHelper.pushAlarmScheduled(a);
@@ -2106,7 +2165,7 @@
         @Override
         public void removeAlarmsForUid(int uid) {
             synchronized (mLock) {
-                removeLocked(uid);
+                removeLocked(uid, REMOVE_REASON_DATA_CLEARED);
             }
         }
 
@@ -2342,7 +2401,7 @@
                 return;
             }
             synchronized (mLock) {
-                removeLocked(operation, listener);
+                removeLocked(operation, listener, REMOVE_REASON_ALARM_CANCELLED);
             }
         }
 
@@ -2688,6 +2747,7 @@
 
             pw.println("Allow while idle history:");
             mAllowWhileIdleHistory.dump(pw, nowELAPSED);
+            pw.println();
 
             if (mLastPriorityAlarmDispatch.size() > 0) {
                 pw.println("Last priority alarm dispatches:");
@@ -2702,6 +2762,23 @@
                 pw.decreaseIndent();
             }
 
+            if (mRemovalHistory.size() > 0) {
+                pw.println("Removal history: ");
+                pw.increaseIndent();
+                for (int i = 0; i < mRemovalHistory.size(); i++) {
+                    UserHandle.formatUid(pw, mRemovalHistory.keyAt(i));
+                    pw.println(":");
+                    pw.increaseIndent();
+                    final RemovedAlarm[] historyForUid = mRemovalHistory.valueAt(i).toArray();
+                    for (final RemovedAlarm removedAlarm : historyForUid) {
+                        removedAlarm.dump(pw, nowELAPSED, sdf);
+                    }
+                    pw.decreaseIndent();
+                }
+                pw.decreaseIndent();
+                pw.println();
+            }
+
             if (mLog.dump(pw, "Recent problems:")) {
                 pw.println();
             }
@@ -3239,7 +3316,7 @@
             }
             return !hasScheduleExactAlarmInternal(a.packageName, a.uid);
         };
-        removeAlarmsInternalLocked(whichAlarms);
+        removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
     }
 
     /**
@@ -3247,7 +3324,6 @@
      * that the app is no longer eligible to use.
      *
      * This is not expected to get called frequently.
-     * TODO (b/179541791): Add revocation history to dumpsys.
      */
     void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
         Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!");
@@ -3263,26 +3339,22 @@
             }
             return false;
         };
-        removeAlarmsInternalLocked(whichAlarms);
+        removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
     }
 
-    private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms) {
+    private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms, int reason) {
+        final long nowRtc = mInjector.getCurrentTimeMillis();
+        final long nowElapsed = mInjector.getElapsedRealtime();
+
         final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(whichAlarms);
-        final boolean didRemove = !removedAlarms.isEmpty();
-        if (didRemove) {
-            for (final Alarm removed : removedAlarms) {
-                decrementAlarmCount(removed.uid, 1);
-            }
-        }
+        final boolean removedFromStore = !removedAlarms.isEmpty();
 
         for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
             final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
             for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
                 final Alarm alarm = alarmsForUid.get(j);
                 if (whichAlarms.test(alarm)) {
-                    // Don't set didRemove, since this doesn't impact the scheduled alarms.
-                    alarmsForUid.remove(j);
-                    decrementAlarmCount(alarm.uid, 1);
+                    removedAlarms.add(alarmsForUid.remove(j));
                 }
             }
             if (alarmsForUid.size() == 0) {
@@ -3292,13 +3364,24 @@
         for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) {
             final Alarm a = mPendingNonWakeupAlarms.get(i);
             if (whichAlarms.test(a)) {
-                // Don't set didRemove, since this doesn't impact the scheduled alarms.
-                mPendingNonWakeupAlarms.remove(i);
-                decrementAlarmCount(a.uid, 1);
+                removedAlarms.add(mPendingNonWakeupAlarms.remove(i));
             }
         }
 
-        if (didRemove) {
+        for (final Alarm removed : removedAlarms) {
+            decrementAlarmCount(removed.uid, 1);
+            if (!RemovedAlarm.isLoggable(reason)) {
+                continue;
+            }
+            RingBuffer<RemovedAlarm> bufferForUid = mRemovalHistory.get(removed.uid);
+            if (bufferForUid == null) {
+                bufferForUid = new RingBuffer<>(RemovedAlarm.class, REMOVAL_HISTORY_SIZE_PER_UID);
+                mRemovalHistory.put(removed.uid, bufferForUid);
+            }
+            bufferForUid.append(new RemovedAlarm(removed, reason, nowRtc, nowElapsed));
+        }
+
+        if (removedFromStore) {
             boolean idleUntilUpdated = false;
             if (mPendingIdleUntil != null && whichAlarms.test(mPendingIdleUntil)) {
                 mPendingIdleUntil = null;
@@ -3320,7 +3403,7 @@
         }
     }
 
-    void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
+    void removeLocked(PendingIntent operation, IAlarmListener directReceiver, int reason) {
         if (operation == null && directReceiver == null) {
             if (localLOGV) {
                 Slog.w(TAG, "requested remove() of null operation",
@@ -3328,15 +3411,15 @@
             }
             return;
         }
-        removeAlarmsInternalLocked(a -> a.matches(operation, directReceiver));
+        removeAlarmsInternalLocked(a -> a.matches(operation, directReceiver), reason);
     }
 
-    void removeLocked(final int uid) {
+    void removeLocked(final int uid, int reason) {
         if (uid == Process.SYSTEM_UID) {
             // If a force-stop occurs for a system-uid package, ignore it.
             return;
         }
-        removeAlarmsInternalLocked(a -> a.uid == uid);
+        removeAlarmsInternalLocked(a -> a.uid == uid, reason);
     }
 
     void removeLocked(final String packageName) {
@@ -3347,7 +3430,7 @@
             }
             return;
         }
-        removeAlarmsInternalLocked(a -> a.matches(packageName));
+        removeAlarmsInternalLocked(a -> a.matches(packageName), REMOVE_REASON_UNDEFINED);
     }
 
     // Only called for ephemeral apps
@@ -3358,7 +3441,7 @@
         }
         final Predicate<Alarm> whichAlarms = (a) -> (a.uid == uid
                 && mActivityManagerInternal.isAppStartModeDisabled(uid, a.packageName));
-        removeAlarmsInternalLocked(whichAlarms);
+        removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_UNDEFINED);
     }
 
     void removeUserLocked(int userHandle) {
@@ -3368,13 +3451,18 @@
         }
         final Predicate<Alarm> whichAlarms =
                 (Alarm a) -> UserHandle.getUserId(a.uid) == userHandle;
-        removeAlarmsInternalLocked(whichAlarms);
+        removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_UNDEFINED);
 
         for (int i = mLastPriorityAlarmDispatch.size() - 1; i >= 0; i--) {
             if (UserHandle.getUserId(mLastPriorityAlarmDispatch.keyAt(i)) == userHandle) {
                 mLastPriorityAlarmDispatch.removeAt(i);
             }
         }
+        for (int i = mRemovalHistory.size() - 1; i >= 0; i--) {
+            if (UserHandle.getUserId(mRemovalHistory.keyAt(i)) == userHandle) {
+                mRemovalHistory.removeAt(i);
+            }
+        }
     }
 
     void interactiveStateChangedLocked(boolean interactive) {
@@ -4018,7 +4106,7 @@
                 case REMOVE_FOR_CANCELED:
                     final PendingIntent operation = (PendingIntent) msg.obj;
                     synchronized (mLock) {
-                        removeLocked(operation, null);
+                        removeLocked(operation, null, REMOVE_REASON_PI_CANCELLED);
                     }
                     break;
 
@@ -4198,6 +4286,7 @@
                         return;
                     case Intent.ACTION_UID_REMOVED:
                         mLastPriorityAlarmDispatch.delete(uid);
+                        mRemovalHistory.delete(uid);
                         return;
                     case Intent.ACTION_PACKAGE_REMOVED:
                         if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
@@ -4227,7 +4316,7 @@
                             // package-removed and package-restarted case
                             mAppWakeupHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
                             mAllowWhileIdleHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
-                            removeLocked(uid);
+                            removeLocked(uid, REMOVE_REASON_UNDEFINED);
                         } else {
                             // external-applications-unavailable case
                             removeLocked(pkg);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 313cd20..452be30 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1088,7 +1088,7 @@
             FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
                     uId, null, jobStatus.getBatteryName(),
                     FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
-                    JobProtoEnums.STOP_REASON_UNKNOWN, jobStatus.getStandbyBucket(),
+                    JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN, jobStatus.getStandbyBucket(),
                     jobStatus.getJobId(),
                     jobStatus.hasChargingConstraint(),
                     jobStatus.hasBatteryNotLowConstraint(),
@@ -1099,7 +1099,8 @@
                     jobStatus.hasConnectivityConstraint(),
                     jobStatus.hasContentTriggerConstraint(),
                     jobStatus.isRequestedExpeditedJob(),
-                    /* isRunningAsExpeditedJob */ false);
+                    /* isRunningAsExpeditedJob */ false,
+                    JobProtoEnums.STOP_REASON_UNDEFINED);
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 6ab2be3..3fa1c12 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -314,7 +314,8 @@
             FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
                     job.getSourceUid(), null, job.getBatteryName(),
                     FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
-                    JobProtoEnums.STOP_REASON_UNKNOWN, job.getStandbyBucket(), job.getJobId(),
+                    JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN,
+                    job.getStandbyBucket(), job.getJobId(),
                     job.hasChargingConstraint(),
                     job.hasBatteryNotLowConstraint(),
                     job.hasStorageNotLowConstraint(),
@@ -324,7 +325,8 @@
                     job.hasConnectivityConstraint(),
                     job.hasContentTriggerConstraint(),
                     job.isRequestedExpeditedJob(),
-                    job.shouldTreatAsExpeditedJob());
+                    job.shouldTreatAsExpeditedJob(),
+                    JobProtoEnums.STOP_REASON_UNDEFINED);
             try {
                 mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
             } catch (RemoteException e) {
@@ -923,7 +925,8 @@
                 completedJob.hasConnectivityConstraint(),
                 completedJob.hasContentTriggerConstraint(),
                 completedJob.isRequestedExpeditedJob(),
-                completedJob.startedAsExpeditedJob);
+                completedJob.startedAsExpeditedJob,
+                mParams.getStopReason());
         try {
             mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
                     internalStopReason);
diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
index 3d5321c..e347ebf 100644
--- a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
+++ b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
@@ -19,7 +19,7 @@
 import android.media.MediaParceledListSlice;
 
 /** {@hide} */
-interface IMediaCommunicationServiceCallback {
+oneway interface IMediaCommunicationServiceCallback {
     void onSession2Created(in Session2Token token);
     void onSession2Changed(in MediaParceledListSlice tokens);
 }
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
index 06de3f8..ed31aa3 100644
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -65,17 +65,17 @@
 
     final Context mContext;
 
-    private final Object mLock = new Object();
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    final Object mLock = new Object();
+    final Handler mHandler = new Handler(Looper.getMainLooper());
 
     @GuardedBy("mLock")
     private final SparseIntArray mFullUserIds = new SparseIntArray();
     @GuardedBy("mLock")
     private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<>();
 
-    private final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
+    final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
     @GuardedBy("mLock")
-    private final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
+    final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
     final NotificationManager mNotificationManager;
 
     public MediaCommunicationService(Context context) {
@@ -111,14 +111,14 @@
             FullUserRecord user = getFullUserRecordLocked(userId);
             if (user != null) {
                 if (user.getFullUserId() == userId) {
-                    user.destroySessionsForUserLocked(UserHandle.ALL.getIdentifier());
+                    user.destroyAllSessions();
                     mUserRecords.remove(userId);
                 } else {
-                    user.destroySessionsForUserLocked(userId);
+                    user.destroySessionsForUser(userId);
                 }
             }
-            updateUser();
         }
+        updateUser();
     }
 
     @Nullable
@@ -134,6 +134,22 @@
         return null;
     }
 
+    List<Session2Token> getSession2TokensLocked(int userId) {
+        List<Session2Token> list = new ArrayList<>();
+        if (userId == ALL.getIdentifier()) {
+            int size = mUserRecords.size();
+            for (int i = 0; i < size; i++) {
+                list.addAll(mUserRecords.valueAt(i).getAllSession2Tokens());
+            }
+        } else {
+            FullUserRecord user = getFullUserRecordLocked(userId);
+            if (user != null) {
+                list.addAll(user.getSession2Tokens(userId));
+            }
+        }
+        return list;
+    }
+
     private FullUserRecord getFullUserRecordLocked(int userId) {
         int fullUserId = mFullUserIds.get(userId, -1);
         if (fullUserId < 0) {
@@ -188,27 +204,54 @@
         }
     }
 
-    void dispatchSessionCreated(Session2Token token) {
-        for (CallbackRecord record : mCallbackRecords) {
-            if (record.mUserId != ALL.getIdentifier()
-                    && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
-                continue;
-            }
-            try {
-                record.mCallback.onSession2Created(token);
-            } catch (RemoteException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    void onSessionDied(Session2Record record) {
+    void dispatchSession2Created(Session2Token token) {
         synchronized (mLock) {
-            destroySessionLocked(record);
+            for (CallbackRecord record : mCallbackRecords) {
+                if (record.mUserId != ALL.getIdentifier()
+                        && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
+                    continue;
+                }
+                try {
+                    record.mCallback.onSession2Created(token);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to notify session2 token created " + record);
+                }
+            }
         }
     }
 
-    private void destroySessionLocked(Session2Record session) {
+    void dispatchSession2Changed(int userId) {
+        MediaParceledListSlice<Session2Token> allSession2Tokens;
+        MediaParceledListSlice<Session2Token> userSession2Tokens;
+
+        synchronized (mLock) {
+            allSession2Tokens =
+                    new MediaParceledListSlice<>(getSession2TokensLocked(ALL.getIdentifier()));
+            userSession2Tokens = new MediaParceledListSlice<>(getSession2TokensLocked(userId));
+        }
+        allSession2Tokens.setInlineCountLimit(1);
+        userSession2Tokens.setInlineCountLimit(1);
+
+        synchronized (mLock) {
+            for (CallbackRecord record : mCallbackRecords) {
+                if (record.mUserId == ALL.getIdentifier()) {
+                    try {
+                        record.mCallback.onSession2Changed(allSession2Tokens);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to notify session2 tokens changed " + record);
+                    }
+                } else if (record.mUserId == userId) {
+                    try {
+                        record.mCallback.onSession2Changed(userSession2Tokens);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to notify session2 tokens changed " + record);
+                    }
+                }
+            }
+        }
+    }
+
+    void onSessionDied(Session2Record session) {
         if (DEBUG) {
             Log.d(TAG, "Destroying " + session);
         }
@@ -217,12 +260,10 @@
             return;
         }
 
-        FullUserRecord user = getFullUserRecordLocked(session.getUserId());
-
+        FullUserRecord user = session.getFullUser();
         if (user != null) {
             user.removeSession(session);
         }
-
         session.close();
     }
 
@@ -241,17 +282,17 @@
                     throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
                             + " but actually=" + sessionToken.getUid());
                 }
+                FullUserRecord user;
+                int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
                 synchronized (mLock) {
-                    int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
-                    FullUserRecord user = getFullUserRecordLocked(userId);
-                    if (user == null) {
-                        Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
-                        return;
-                    }
-                    user.addSession(new Session2Record(MediaCommunicationService.this,
-                            sessionToken, mRecordExecutor));
-                    mHandler.post(() -> dispatchSessionCreated(sessionToken));
+                    user = getFullUserRecordLocked(userId);
                 }
+                if (user == null) {
+                    Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
+                    return;
+                }
+                user.addSession(new Session2Record(MediaCommunicationService.this,
+                        user, sessionToken, mRecordExecutor));
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -299,10 +340,11 @@
                 int resolvedUserId = handleIncomingUser(pid, uid, userId, null);
                 List<Session2Token> result;
                 synchronized (mLock) {
-                    FullUserRecord user = getFullUserRecordLocked(userId);
-                    result = user.getSession2Tokens(resolvedUserId);
+                    result = getSession2TokensLocked(resolvedUserId);
                 }
-                return new MediaParceledListSlice(result);
+                MediaParceledListSlice parceledListSlice = new MediaParceledListSlice<>(result);
+                parceledListSlice.setInlineCountLimit(1);
+                return parceledListSlice;
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -427,7 +469,8 @@
 
     final class FullUserRecord {
         private final int mFullUserId;
-        /** Sorted list of media sessions */
+        private final Object mUserLock = new Object();
+        @GuardedBy("mUserLock")
         private final List<Session2Record> mSessionRecords = new ArrayList<>();
 
         FullUserRecord(int fullUserId) {
@@ -435,11 +478,18 @@
         }
 
         public void addSession(Session2Record record) {
-            mSessionRecords.add(record);
+            synchronized (mUserLock) {
+                mSessionRecords.add(record);
+            }
+            mHandler.post(() -> dispatchSession2Created(record.mSessionToken));
+            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
         }
 
-        public void removeSession(Session2Record record) {
-            mSessionRecords.remove(record);
+        private void removeSession(Session2Record record) {
+            synchronized (mUserLock) {
+                mSessionRecords.remove(record);
+            }
+            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
             //TODO: Handle if the removed session was the media button session.
         }
 
@@ -447,42 +497,68 @@
             return mFullUserId;
         }
 
-        public List<Session2Token> getSession2Tokens(int userId) {
-            return mSessionRecords.stream()
-                    .filter(record -> record.isActive()
-                            && (userId == UserHandle.ALL.getIdentifier()
-                                    || record.getUserId() == userId))
-                    .map(Session2Record::getSessionToken)
-                    .collect(Collectors.toList());
+        public List<Session2Token> getAllSession2Tokens() {
+            synchronized (mUserLock) {
+                return mSessionRecords.stream()
+                        .map(Session2Record::getSessionToken)
+                        .collect(Collectors.toList());
+            }
         }
 
-        public void destroySessionsForUserLocked(int userId) {
-            synchronized (mLock) {
-                for (Session2Record record : mSessionRecords) {
-                    if (userId == UserHandle.ALL.getIdentifier()
-                            || record.getUserId() == userId) {
-                        destroySessionLocked(record);
+        public List<Session2Token> getSession2Tokens(int userId) {
+            synchronized (mUserLock) {
+                return mSessionRecords.stream()
+                        .filter(record -> record.getUserId() == userId)
+                        .map(Session2Record::getSessionToken)
+                        .collect(Collectors.toList());
+            }
+        }
+
+        public void destroyAllSessions() {
+            synchronized (mUserLock) {
+                for (Session2Record session : mSessionRecords) {
+                    session.close();
+                }
+                mSessionRecords.clear();
+            }
+            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
+        }
+
+        public void destroySessionsForUser(int userId) {
+            boolean changed = false;
+            synchronized (mUserLock) {
+                for (int i = mSessionRecords.size() - 1; i >= 0; i--) {
+                    Session2Record session = mSessionRecords.get(i);
+                    if (session.getUserId() == userId) {
+                        mSessionRecords.remove(i);
+                        session.close();
+                        changed = true;
                     }
                 }
             }
+            if (changed) {
+                mHandler.post(() -> dispatchSession2Changed(mFullUserId));
+            }
         }
     }
 
     static final class Session2Record {
-        private final Session2Token mSessionToken;
-        private final Object mLock = new Object();
-        private final WeakReference<MediaCommunicationService> mServiceRef;
-        @GuardedBy("mLock")
+        final Session2Token mSessionToken;
+        final Object mSession2RecordLock = new Object();
+        final WeakReference<MediaCommunicationService> mServiceRef;
+        final WeakReference<FullUserRecord> mFullUserRef;
+        @GuardedBy("mSession2RecordLock")
         private final MediaController2 mController;
 
-        @GuardedBy("mLock")
-        private boolean mIsConnected;
-        @GuardedBy("mLock")
+        @GuardedBy("mSession2RecordLock")
+        boolean mIsConnected;
+        @GuardedBy("mSession2RecordLock")
         private boolean mIsClosed;
 
-        Session2Record(MediaCommunicationService service, Session2Token token,
-                Executor controllerExecutor) {
+        Session2Record(MediaCommunicationService service, FullUserRecord fullUser,
+                Session2Token token, Executor controllerExecutor) {
             mServiceRef = new WeakReference<>(service);
+            mFullUserRef = new WeakReference<>(fullUser);
             mSessionToken = token;
             mController = new MediaController2.Builder(service.getContext(), token)
                     .setControllerCallback(controllerExecutor, new Controller2Callback())
@@ -493,23 +569,19 @@
             return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier();
         }
 
-        public boolean isActive() {
-            synchronized (mLock) {
-                return mIsConnected;
-            }
+        public FullUserRecord getFullUser() {
+            return mFullUserRef.get();
         }
 
         public boolean isClosed() {
-            synchronized (mLock) {
+            synchronized (mSession2RecordLock) {
                 return mIsClosed;
             }
         }
 
         public void close() {
-            synchronized (mLock) {
+            synchronized (mSession2RecordLock) {
                 mIsClosed = true;
-                // Call close regardless of the mIsConnected. This may be called when it's not yet
-                // connected.
                 mController.close();
             }
         }
@@ -525,13 +597,9 @@
                 if (DEBUG) {
                     Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands);
                 }
-                synchronized (mLock) {
+                synchronized (mSession2RecordLock) {
                     mIsConnected = true;
                 }
-                MediaCommunicationService service = mServiceRef.get();
-                if (service != null) {
-                    //TODO: notify session state changed
-                }
             }
 
             @Override
@@ -539,7 +607,7 @@
                 if (DEBUG) {
                     Log.d(TAG, "disconnected from " + mSessionToken);
                 }
-                synchronized (mLock) {
+                synchronized (mSession2RecordLock) {
                     mIsConnected = false;
                 }
                 MediaCommunicationService service = mServiceRef.get();
diff --git a/api/Android.bp b/api/Android.bp
index 6571270..5b73388 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -370,7 +370,7 @@
         {
             targets: ["sdk", "win_sdk"],
             dir: "apistubs/android/system-server/api",
-            dest: "merge-android.txt",
+            dest: "android.txt",
         },
     ],
 }
@@ -394,7 +394,7 @@
         {
             targets: ["sdk", "win_sdk"],
             dir: "apistubs/android/system-server/api",
-            dest: "merge-removed.txt",
+            dest: "removed.txt",
         },
     ],
 }
diff --git a/cmds/dpm/Android.bp b/cmds/dpm/Android.bp
index 665abcd..6819d09 100644
--- a/cmds/dpm/Android.bp
+++ b/cmds/dpm/Android.bp
@@ -18,8 +18,7 @@
     ],
 }
 
-java_binary {
+sh_binary {
     name: "dpm",
-    wrapper: "dpm",
-    srcs: ["**/*.java"],
+    src: "dpm",
 }
diff --git a/cmds/dpm/dpm b/cmds/dpm/dpm
index e0efdc1..784db5b 100755
--- a/cmds/dpm/dpm
+++ b/cmds/dpm/dpm
@@ -1,7 +1,5 @@
 #!/system/bin/sh
 # Script to start "dpm" on the device
 #
-base=/system
-export CLASSPATH=$base/framework/dpm.jar
-exec app_process $base/bin com.android.commands.dpm.Dpm "$@"
+cmd device_policy "$@"
 
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
deleted file mode 100644
index d0c2a24..0000000
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2014 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.commands.dpm;
-
-import android.app.ActivityManager;
-import android.app.IActivityManager;
-import android.app.admin.DevicePolicyManager;
-import android.app.admin.IDevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-
-import com.android.internal.os.BaseCommand;
-
-import java.io.PrintStream;
-
-public final class Dpm extends BaseCommand {
-
-    /**
-     * Command-line entry point.
-     *
-     * @param args The command-line arguments
-     */
-    public static void main(String[] args) {
-      (new Dpm()).run(args);
-    }
-
-    private static final String COMMAND_SET_ACTIVE_ADMIN = "set-active-admin";
-    private static final String COMMAND_SET_DEVICE_OWNER = "set-device-owner";
-    private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
-    private static final String COMMAND_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
-    private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
-    private static final String COMMAND_FORCE_NETWORK_LOGS = "force-network-logs";
-    private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
-    private static final String COMMAND_MARK_PO_ON_ORG_OWNED_DEVICE =
-            "mark-profile-owner-on-organization-owned-device";
-
-    private IDevicePolicyManager mDevicePolicyManager;
-    private int mUserId = UserHandle.USER_SYSTEM;
-    private String mName = "";
-    private ComponentName mComponent = null;
-
-    @Override
-    public void onShowUsage(PrintStream out) {
-        out.println(
-                "usage: dpm [subcommand] [options]\n" +
-                "usage: dpm set-active-admin [ --user <USER_ID> | current ] <COMPONENT>\n" +
-                // STOPSHIP Finalize it
-                "usage: dpm set-device-owner [ --user <USER_ID> | current *EXPERIMENTAL* ] " +
-                "[ --name <NAME> ] <COMPONENT>\n" +
-                "usage: dpm set-profile-owner [ --user <USER_ID> | current ] [ --name <NAME> ] " +
-                "<COMPONENT>\n" +
-                "usage: dpm remove-active-admin [ --user <USER_ID> | current ] [ --name <NAME> ] " +
-                "<COMPONENT>\n" +
-                "\n" +
-                "dpm set-active-admin: Sets the given component as active admin" +
-                " for an existing user.\n" +
-                "\n" +
-                "dpm set-device-owner: Sets the given component as active admin, and its" +
-                " package as device owner.\n" +
-                "\n" +
-                "dpm set-profile-owner: Sets the given component as active admin and profile" +
-                " owner for an existing user.\n" +
-                "\n" +
-                "dpm remove-active-admin: Disables an active admin, the admin must have declared" +
-                " android:testOnly in the application in its manifest. This will also remove" +
-                " device and profile owners.\n" +
-                "\n" +
-                "dpm " + COMMAND_CLEAR_FREEZE_PERIOD_RECORD + ": clears framework-maintained " +
-                "record of past freeze periods that the device went through. For use during " +
-                "feature development to prevent triggering restriction on setting freeze " +
-                "periods.\n" +
-                "\n" +
-                "dpm " + COMMAND_FORCE_NETWORK_LOGS + ": makes all network logs available to " +
-                "the DPC and triggers DeviceAdminReceiver.onNetworkLogsAvailable() if needed.\n" +
-                "\n" +
-                "dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
-                "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed."
-                + "\n"
-                + "usage: dpm " + COMMAND_MARK_PO_ON_ORG_OWNED_DEVICE + ": "
-                + "[ --user <USER_ID> | current ] <COMPONENT>\n");
-    }
-
-    @Override
-    public void onRun() throws Exception {
-        mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
-                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
-        if (mDevicePolicyManager == null) {
-            showError("Error: Could not access the Device Policy Manager. Is the system running?");
-            return;
-        }
-
-        String command = nextArgRequired();
-        switch (command) {
-            case COMMAND_SET_ACTIVE_ADMIN:
-                runSetActiveAdmin();
-                break;
-            case COMMAND_SET_DEVICE_OWNER:
-                runSetDeviceOwner();
-                break;
-            case COMMAND_SET_PROFILE_OWNER:
-                runSetProfileOwner();
-                break;
-            case COMMAND_REMOVE_ACTIVE_ADMIN:
-                runRemoveActiveAdmin();
-                break;
-            case COMMAND_CLEAR_FREEZE_PERIOD_RECORD:
-                runClearFreezePeriodRecord();
-                break;
-            case COMMAND_FORCE_NETWORK_LOGS:
-                runForceNetworkLogs();
-                break;
-            case COMMAND_FORCE_SECURITY_LOGS:
-                runForceSecurityLogs();
-                break;
-            case COMMAND_MARK_PO_ON_ORG_OWNED_DEVICE:
-                runMarkProfileOwnerOnOrganizationOwnedDevice();
-                break;
-            default:
-                throw new IllegalArgumentException ("unknown command '" + command + "'");
-        }
-    }
-
-    private void runForceNetworkLogs() throws RemoteException, InterruptedException {
-        while (true) {
-            final long toWait = mDevicePolicyManager.forceNetworkLogs();
-            if (toWait == 0) {
-                break;
-            }
-            System.out.println("We have to wait for " + toWait + " milliseconds...");
-            Thread.sleep(toWait);
-        }
-        System.out.println("Success");
-    }
-
-    private void runForceSecurityLogs() throws RemoteException, InterruptedException {
-        while (true) {
-            final long toWait = mDevicePolicyManager.forceSecurityLogs();
-            if (toWait == 0) {
-                break;
-            }
-            System.out.println("We have to wait for " + toWait + " milliseconds...");
-            Thread.sleep(toWait);
-        }
-        System.out.println("Success");
-    }
-
-    private void parseArgs(boolean canHaveName) {
-        String opt;
-        while ((opt = nextOption()) != null) {
-            if ("--user".equals(opt)) {
-                String arg = nextArgRequired();
-                if ("current".equals(arg) || "cur".equals(arg)) {
-                    mUserId = UserHandle.USER_CURRENT;
-                } else {
-                    mUserId = parseInt(arg);
-                }
-                if (mUserId == UserHandle.USER_CURRENT) {
-                    IActivityManager activityManager = ActivityManager.getService();
-                    try {
-                        mUserId = activityManager.getCurrentUser().id;
-                    } catch (RemoteException e) {
-                        e.rethrowAsRuntimeException();
-                    }
-                }
-            } else if (canHaveName && "--name".equals(opt)) {
-                mName = nextArgRequired();
-            } else {
-                throw new IllegalArgumentException("Unknown option: " + opt);
-            }
-        }
-        mComponent = parseComponentName(nextArgRequired());
-    }
-
-    private void runSetActiveAdmin() throws RemoteException {
-        parseArgs(/*canHaveName=*/ false);
-        mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
-
-        System.out.println("Success: Active admin set to component " + mComponent.toShortString());
-    }
-
-    private void runSetDeviceOwner() throws RemoteException {
-        parseArgs(/*canHaveName=*/ true);
-        mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
-
-        try {
-            if (!mDevicePolicyManager.setDeviceOwner(mComponent, mName, mUserId)) {
-                throw new RuntimeException(
-                        "Can't set package " + mComponent + " as device owner.");
-            }
-        } catch (Exception e) {
-            // Need to remove the admin that we just added.
-            mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
-            throw e;
-        }
-
-        mDevicePolicyManager.setUserProvisioningState(
-                DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
-
-        System.out.println("Success: Device owner set to package " + mComponent);
-        System.out.println("Active admin set to component " + mComponent.toShortString());
-    }
-
-    private void runRemoveActiveAdmin() throws RemoteException {
-        parseArgs(/*canHaveName=*/ false);
-        mDevicePolicyManager.forceRemoveActiveAdmin(mComponent, mUserId);
-        System.out.println("Success: Admin removed " + mComponent);
-    }
-
-    private void runSetProfileOwner() throws RemoteException {
-        parseArgs(/*canHaveName=*/ true);
-        mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
-
-        try {
-            if (!mDevicePolicyManager.setProfileOwner(mComponent, mName, mUserId)) {
-                throw new RuntimeException("Can't set component " + mComponent.toShortString() +
-                        " as profile owner for user " + mUserId);
-            }
-        } catch (Exception e) {
-            // Need to remove the admin that we just added.
-            mDevicePolicyManager.removeActiveAdmin(mComponent, mUserId);
-            throw e;
-        }
-
-        mDevicePolicyManager.setUserProvisioningState(
-                DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
-
-        System.out.println("Success: Active admin and profile owner set to "
-                + mComponent.toShortString() + " for user " + mUserId);
-    }
-
-    private void runClearFreezePeriodRecord() throws RemoteException {
-        mDevicePolicyManager.clearSystemUpdatePolicyFreezePeriodRecord();
-        System.out.println("Success");
-    }
-
-
-    private void runMarkProfileOwnerOnOrganizationOwnedDevice() throws RemoteException {
-        parseArgs(/*canHaveName=*/ false);
-        mDevicePolicyManager.markProfileOwnerOnOrganizationOwnedDevice(mComponent, mUserId);
-        System.out.println("Success");
-    }
-
-    private ComponentName parseComponentName(String component) {
-        ComponentName cn = ComponentName.unflattenFromString(component);
-        if (cn == null) {
-            throw new IllegalArgumentException ("Invalid component " + component);
-        }
-        return cn;
-    }
-
-    private int parseInt(String argument) {
-        try {
-            return Integer.parseInt(argument);
-        } catch (NumberFormatException e) {
-            throw new IllegalArgumentException ("Invalid integer argument '" + argument + "'", e);
-        }
-    }
-}
diff --git a/core/api/current.txt b/core/api/current.txt
index b223bc6..bbed3e6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8014,7 +8014,6 @@
     method @Nullable public String[] getTriggeredContentAuthorities();
     method @Nullable public android.net.Uri[] getTriggeredContentUris();
     method public boolean isExpeditedJob();
-    method @Deprecated public boolean isForegroundJob();
     method public boolean isOverrideDeadlineExpired();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.job.JobParameters> CREATOR;
@@ -17137,11 +17136,11 @@
 
 package android.hardware {
 
-  public abstract class Battery {
-    ctor public Battery();
+  public abstract class BatteryState {
+    ctor public BatteryState();
     method @FloatRange(from=-1.0F, to=1.0f) public abstract float getCapacity();
     method public abstract int getStatus();
-    method public abstract boolean hasBattery();
+    method public abstract boolean isPresent();
     field public static final int STATUS_CHARGING = 2; // 0x2
     field public static final int STATUS_DISCHARGING = 3; // 0x3
     field public static final int STATUS_FULL = 5; // 0x5
@@ -18992,7 +18991,6 @@
   }
 
   public abstract static class LightsManager.LightsSession implements java.lang.AutoCloseable {
-    ctor public LightsManager.LightsSession();
     method public abstract void close();
     method public abstract void requestLights(@NonNull android.hardware.lights.LightsRequest);
   }
@@ -27111,7 +27109,7 @@
     method @NonNull public int[] getExposedCapabilities();
     method @NonNull public String getGatewayConnectionName();
     method @IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) public int getMaxMtu();
-    method @NonNull public long[] getRetryIntervalsMs();
+    method @NonNull public long[] getRetryIntervalsMillis();
   }
 
   public static final class VcnGatewayConnectionConfig.Builder {
@@ -27120,7 +27118,7 @@
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) int);
-    method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMs(@NonNull long[]);
+    method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
   }
 
   public class VcnManager {
@@ -41991,7 +41989,6 @@
     method @IntRange(from=1, to=261) public int getBand();
     method @IntRange(from=1) public int getCellBandwidthDownlinkKhz();
     method @IntRange(from=1) public int getCellBandwidthUplinkKhz();
-    method @Deprecated public int getChannelNumber();
     method public int getConnectionStatus();
     method @IntRange(from=0) public int getDownlinkChannelNumber();
     method @IntRange(from=0) public int getDownlinkFrequencyKhz();
@@ -47132,7 +47129,7 @@
 
   public final class InputDevice implements android.os.Parcelable {
     method public int describeContents();
-    method @NonNull public android.hardware.Battery getBattery();
+    method @NonNull public android.hardware.BatteryState getBatteryState();
     method public int getControllerNumber();
     method public String getDescriptor();
     method public static android.view.InputDevice getDevice(int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 677da39..e48a1da 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -223,9 +223,9 @@
   public final class UnderlyingNetworkInfo implements android.os.Parcelable {
     ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
     method public int describeContents();
-    method @NonNull public String getIface();
+    method @NonNull public String getInterface();
     method public int getOwnerUid();
-    method @NonNull public java.util.List<java.lang.String> getUnderlyingIfaces();
+    method @NonNull public java.util.List<java.lang.String> getUnderlyingInterfaces();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR;
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 22af3f7..3537d8b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -317,6 +317,7 @@
 
   public static final class R.attr {
     field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+    field public static final int durationBetweenRequestsMillis;
     field public static final int hotwordDetectionService;
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int minExtensionVersion = 16844305; // 0x1010611
@@ -325,7 +326,6 @@
     field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
     field public static final int sdkVersion = 16844304; // 0x1010610
     field public static final int supportsAmbientMode = 16844173; // 0x101058d
-    field public static final int throttleDurationMillis;
     field public static final int userRestriction = 16844164; // 0x1010584
   }
 
@@ -1448,11 +1448,12 @@
 package android.app.search {
 
   public final class Query implements android.os.Parcelable {
-    ctor public Query(@NonNull String, long, @Nullable android.os.Bundle);
+    ctor public Query(@NonNull String, long, @NonNull android.os.Bundle);
+    ctor public Query(@NonNull String, long);
     method public int describeContents();
-    method @Nullable public android.os.Bundle getExtras();
+    method @NonNull public android.os.Bundle getExtras();
     method @NonNull public String getInput();
-    method @NonNull public long getTimestamp();
+    method public long getTimestampMillis();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.search.Query> CREATOR;
   }
@@ -1485,9 +1486,10 @@
   }
 
   public final class SearchContext implements android.os.Parcelable {
-    ctor public SearchContext(int, int, @Nullable android.os.Bundle);
+    ctor public SearchContext(int, int);
+    ctor public SearchContext(int, int, @NonNull android.os.Bundle);
     method public int describeContents();
-    method @Nullable public android.os.Bundle getExtras();
+    method @NonNull public android.os.Bundle getExtras();
     method @Nullable public String getPackageName();
     method @NonNull public int getResultTypes();
     method @NonNull public int getTimeoutMillis();
@@ -1497,7 +1499,6 @@
 
   public final class SearchSession implements java.lang.AutoCloseable {
     method public void close();
-    method public void destroy();
     method protected void finalize();
     method public void notifyEvent(@NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
     method @Nullable public void query(@NonNull android.app.search.Query, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
@@ -1512,7 +1513,7 @@
   public final class SearchTarget implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.appwidget.AppWidgetProviderInfo getAppWidgetProviderInfo();
-    method @Nullable public android.os.Bundle getExtras();
+    method @NonNull public android.os.Bundle getExtras();
     method @NonNull public String getId();
     method @NonNull public String getLayoutType();
     method @NonNull public String getPackageName();
@@ -1526,13 +1527,17 @@
     method public boolean shouldHide();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.search.SearchTarget> CREATOR;
+    field public static final int RESULT_TYPE_APPLICATION = 1; // 0x1
+    field public static final int RESULT_TYPE_SHORTCUT = 2; // 0x2
+    field public static final int RESULT_TYPE_SLICE = 4; // 0x4
+    field public static final int RESULT_TYPE_WIDGETS = 8; // 0x8
   }
 
   public static final class SearchTarget.Builder {
     ctor public SearchTarget.Builder(int, @NonNull String, @NonNull String);
     method @NonNull public android.app.search.SearchTarget build();
     method @NonNull public android.app.search.SearchTarget.Builder setAppWidgetProviderInfo(@NonNull android.appwidget.AppWidgetProviderInfo);
-    method @NonNull public android.app.search.SearchTarget.Builder setExtras(@Nullable android.os.Bundle);
+    method @NonNull public android.app.search.SearchTarget.Builder setExtras(@NonNull android.os.Bundle);
     method @NonNull public android.app.search.SearchTarget.Builder setPackageName(@NonNull String);
     method @NonNull public android.app.search.SearchTarget.Builder setParentId(@NonNull String);
     method @NonNull public android.app.search.SearchTarget.Builder setScore(float);
@@ -9867,7 +9872,7 @@
   public final class DisplayHashParams implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.util.Size getBufferSize();
-    method public boolean isUseGrayscale();
+    method public boolean isGrayscaleBuffer();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.service.displayhash.DisplayHashParams> CREATOR;
   }
@@ -9876,7 +9881,7 @@
     ctor public DisplayHashParams.Builder();
     method @NonNull public android.service.displayhash.DisplayHashParams build();
     method @NonNull public android.service.displayhash.DisplayHashParams.Builder setBufferSize(int, int);
-    method @NonNull public android.service.displayhash.DisplayHashParams.Builder setUseGrayscale(boolean);
+    method @NonNull public android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean);
   }
 
   public abstract class DisplayHashingService extends android.app.Service {
@@ -10260,10 +10265,10 @@
   public abstract class SearchUiService extends android.app.Service {
     ctor public SearchUiService();
     method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method public void onCreateSearchSession(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
     method @MainThread public abstract void onDestroy(@NonNull android.app.search.SearchSessionId);
     method @MainThread public abstract void onNotifyEvent(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
     method @MainThread public abstract void onQuery(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
+    method public void onSearchSessionCreated(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
   }
 
 }
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index f366a54..65a8780 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -48,6 +48,18 @@
 
 }
 
+package android.app.search {
+
+  public final class Query implements android.os.Parcelable {
+    method @Deprecated @NonNull public long getTimestamp();
+  }
+
+  public final class SearchSession implements java.lang.AutoCloseable {
+    method @Deprecated public void destroy();
+  }
+
+}
+
 package android.bluetooth {
 
   public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
@@ -163,6 +175,14 @@
 
 }
 
+package android.service.search {
+
+  public abstract class SearchUiService extends android.app.Service {
+    method @Deprecated public void onCreateSearchSession(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
+  }
+
+}
+
 package android.telecom {
 
   public class TelecomManager {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a24555f..6d2d023 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1947,8 +1947,9 @@
             pw.print(((baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0));
             pw.print(" activityType="); pw.print(activityTypeToString(getActivityType()));
             pw.print(" windowingMode="); pw.print(windowingModeToString(getWindowingMode()));
-            pw.print(" supportsSplitScreenMultiWindow=");
-            pw.println(supportsSplitScreenMultiWindow);
+            pw.print(" supportsSplitScreenMultiWindow="); pw.print(supportsSplitScreenMultiWindow);
+            pw.print(" supportsMultiWindow=");
+            pw.println(supportsMultiWindow);
             if (taskDescription != null) {
                 pw.print("   ");
                 final ActivityManager.TaskDescription td = taskDescription;
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6df9f4d..4a7fcd2 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -349,20 +349,6 @@
     }
 
     /**
-     * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
-     * if the request orientation is not met, and will be shown in size-compat mode if the container
-     * size has changed.
-     * @hide
-     */
-    public static boolean supportsNonResizableMultiWindow() {
-        try {
-            return ActivityTaskManager.getService().supportsNonResizableMultiWindow();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * @return whether the UI mode of the given config supports error dialogs (ANR, crash, etc).
      * @hide
      */
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1cb46b1..d798620 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -6511,13 +6511,21 @@
             NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, null);
             accessEvents.append(key, note);
             AttributedOpEntry access = new AttributedOpEntry(mOp, false, accessEvents, null);
-            for (int i = discreteAccesses.size() - 1; i >= 0; i--) {
-                if (discreteAccesses.get(i).getLastAccessTime(OP_FLAGS_ALL) < discreteAccessTime) {
-                    discreteAccesses.add(i + 1, access);
-                    return;
+            int insertionPoint = discreteAccesses.size() - 1;
+            for (; insertionPoint >= 0; insertionPoint--) {
+                if (discreteAccesses.get(insertionPoint).getLastAccessTime(OP_FLAGS_ALL)
+                        < discreteAccessTime) {
+                    break;
                 }
             }
-            discreteAccesses.add(0, access);
+            insertionPoint++;
+            if (insertionPoint < discreteAccesses.size() && discreteAccesses.get(
+                    insertionPoint).getLastAccessTime(OP_FLAGS_ALL) == discreteAccessTime) {
+                discreteAccesses.set(insertionPoint, mergeAttributedOpEntries(
+                        Arrays.asList(discreteAccesses.get(insertionPoint), access)));
+            } else {
+                discreteAccesses.add(insertionPoint, access);
+            }
         }
 
         /**
@@ -9858,7 +9866,10 @@
                 NoteOpEvent reject = a.getLastRejectEvent(uidState, uidState, flags);
 
                 if (access != null) {
-                    accessEvents.append(key, access);
+                    NoteOpEvent existingAccess = accessEvents.get(key);
+                    if (existingAccess == null || existingAccess.getDuration() == -1) {
+                        accessEvents.append(key, access);
+                    }
                 }
                 if (reject != null) {
                     rejectEvents.append(key, reject);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 656942d..9ce37e4 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3273,6 +3273,13 @@
                     dir = null;
                 }
             }
+            if (dir != null && !dir.canWrite()) {
+                // Older versions of the MediaProvider mainline module had a rare early boot race
+                // condition where app-private dirs could be created with the wrong permissions;
+                // fix this up here. This check should be very fast, because dir.exists() above
+                // will already have loaded the dentry in the cache.
+                sm.fixupAppDir(dir);
+            }
             result[i] = dir;
         }
         return result;
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index b75e89c..74d51a0 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -259,13 +259,6 @@
     void setSplitScreenResizing(boolean resizing);
     boolean supportsLocalVoiceInteraction();
 
-    /**
-     * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
-     * if the request orientation is not met, and will be shown in size-compat mode if the container
-     * size has changed.
-     */
-    boolean supportsNonResizableMultiWindow();
-
     // Get device configuration
     ConfigurationInfo getDeviceConfigurationInfo();
 
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 83d0246..ea6c874 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -58,6 +58,7 @@
 import android.util.SparseArray;
 import android.view.DisplayAdjustments;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
 
 import dalvik.system.BaseDexClassLoader;
@@ -156,6 +157,7 @@
     private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
         = new ArrayMap<>();
     private AppComponentFactory mAppComponentFactory;
+    private final Object mLock = new Object();
 
     Application getApplication() {
         return mApplication;
@@ -354,7 +356,7 @@
         } else {
             addedPaths.addAll(newPaths);
         }
-        synchronized (this) {
+        synchronized (mLock) {
             createOrUpdateClassLoaderLocked(addedPaths);
             if (mResources != null) {
                 final String[] splitPaths;
@@ -589,7 +591,9 @@
      * include the base APK in the list of splits.
      */
     private class SplitDependencyLoaderImpl extends SplitDependencyLoader<NameNotFoundException> {
+        @GuardedBy("mLock")
         private final String[][] mCachedResourcePaths;
+        @GuardedBy("mLock")
         private final ClassLoader[] mCachedClassLoaders;
 
         SplitDependencyLoaderImpl(@NonNull SparseArray<int[]> dependencies) {
@@ -600,37 +604,41 @@
 
         @Override
         protected boolean isSplitCached(int splitIdx) {
-            return mCachedClassLoaders[splitIdx] != null;
+            synchronized (mLock) {
+                return mCachedClassLoaders[splitIdx] != null;
+            }
         }
 
         @Override
         protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
                 int parentSplitIdx) throws NameNotFoundException {
-            final ArrayList<String> splitPaths = new ArrayList<>();
-            if (splitIdx == 0) {
-                createOrUpdateClassLoaderLocked(null);
-                mCachedClassLoaders[0] = mClassLoader;
+            synchronized (mLock) {
+                final ArrayList<String> splitPaths = new ArrayList<>();
+                if (splitIdx == 0) {
+                    createOrUpdateClassLoaderLocked(null);
+                    mCachedClassLoaders[0] = mClassLoader;
 
-                // Never add the base resources here, they always get added no matter what.
+                    // Never add the base resources here, they always get added no matter what.
+                    for (int configSplitIdx : configSplitIndices) {
+                        splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
+                    }
+                    mCachedResourcePaths[0] = splitPaths.toArray(new String[splitPaths.size()]);
+                    return;
+                }
+
+                // Since we handled the special base case above, parentSplitIdx is always valid.
+                final ClassLoader parent = mCachedClassLoaders[parentSplitIdx];
+                mCachedClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
+                        mSplitAppDirs[splitIdx - 1], getTargetSdkVersion(), false, null,
+                        null, parent, mSplitClassLoaderNames[splitIdx - 1]);
+
+                Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
+                splitPaths.add(mSplitResDirs[splitIdx - 1]);
                 for (int configSplitIdx : configSplitIndices) {
                     splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
                 }
-                mCachedResourcePaths[0] = splitPaths.toArray(new String[splitPaths.size()]);
-                return;
+                mCachedResourcePaths[splitIdx] = splitPaths.toArray(new String[splitPaths.size()]);
             }
-
-            // Since we handled the special base case above, parentSplitIdx is always valid.
-            final ClassLoader parent = mCachedClassLoaders[parentSplitIdx];
-            mCachedClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
-                    mSplitAppDirs[splitIdx - 1], getTargetSdkVersion(), false, null, null, parent,
-                    mSplitClassLoaderNames[splitIdx - 1]);
-
-            Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
-            splitPaths.add(mSplitResDirs[splitIdx - 1]);
-            for (int configSplitIdx : configSplitIndices) {
-                splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
-            }
-            mCachedResourcePaths[splitIdx] = splitPaths.toArray(new String[splitPaths.size()]);
         }
 
         private int ensureSplitLoaded(String splitName) throws NameNotFoundException {
@@ -648,11 +656,17 @@
         }
 
         ClassLoader getClassLoaderForSplit(String splitName) throws NameNotFoundException {
-            return mCachedClassLoaders[ensureSplitLoaded(splitName)];
+            final int idx = ensureSplitLoaded(splitName);
+            synchronized (mLock) {
+                return mCachedClassLoaders[idx];
+            }
         }
 
         String[] getSplitPathsForSplit(String splitName) throws NameNotFoundException {
-            return mCachedResourcePaths[ensureSplitLoaded(splitName)];
+            final int idx = ensureSplitLoaded(splitName);
+            synchronized (mLock) {
+                return mCachedResourcePaths[idx];
+            }
         }
     }
 
@@ -749,6 +763,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
         if (mPackageName.equals("android")) {
             // Note: This branch is taken for system server and we don't need to setup
@@ -1023,7 +1038,7 @@
 
     @UnsupportedAppUsage
     public ClassLoader getClassLoader() {
-        synchronized (this) {
+        synchronized (mLock) {
             if (mClassLoader == null) {
                 createOrUpdateClassLoaderLocked(null /*addedPaths*/);
             }
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 6ad5eea..b95412f 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -138,6 +138,13 @@
     public boolean supportsSplitScreenMultiWindow;
 
     /**
+     * Whether this task supports multi windowing modes based on the device settings and the
+     * root activity resizability and configuration.
+     * @hide
+     */
+    public boolean supportsMultiWindow;
+
+    /**
      * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
      * @hide
      */
@@ -329,6 +336,7 @@
         }
         return topActivityType == that.topActivityType
                 && isResizeable == that.isResizeable
+                && supportsMultiWindow == that.supportsMultiWindow
                 && Objects.equals(positionInParent, that.positionInParent)
                 && Objects.equals(pictureInPictureParams, that.pictureInPictureParams)
                 && getWindowingMode() == that.getWindowingMode()
@@ -375,6 +383,7 @@
 
         taskDescription = source.readTypedObject(ActivityManager.TaskDescription.CREATOR);
         supportsSplitScreenMultiWindow = source.readBoolean();
+        supportsMultiWindow = source.readBoolean();
         resizeMode = source.readInt();
         configuration.readFromParcel(source);
         token = WindowContainerToken.CREATOR.createFromParcel(source);
@@ -412,6 +421,7 @@
 
         dest.writeTypedObject(taskDescription, flags);
         dest.writeBoolean(supportsSplitScreenMultiWindow);
+        dest.writeBoolean(supportsMultiWindow);
         dest.writeInt(resizeMode);
         configuration.writeToParcel(dest, flags);
         token.writeToParcel(dest, flags);
@@ -440,6 +450,7 @@
                 + " numActivities=" + numActivities
                 + " lastActiveTime=" + lastActiveTime
                 + " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow
+                + " supportsMultiWindow=" + supportsMultiWindow
                 + " resizeMode=" + resizeMode
                 + " isResizeable=" + isResizeable
                 + " token=" + token
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c93a88f..cbf2d6a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10368,6 +10368,9 @@
     /**
      * Called by device owners to set the user's global location setting.
      *
+     * <p><b>Note: </b> this call is ignored on
+     * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive builds}.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with
      * @param locationEnabled whether location should be enabled or disabled
      * @throws SecurityException if {@code admin} is not a device owner.
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
index 59b3555..fad6cd3 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.content.pm.PackageInfo;
 import android.os.Parcel;
 
 import java.lang.annotation.Retention;
@@ -101,12 +102,20 @@
         return VALUE_UNDEFINED;
     }
 
-    /** Returns the minimum version code the override applies to. */
+    /**
+     * Returns the minimum APK version code the override applies to.
+     *
+     * @see PackageInfo#getLongVersionCode()
+     */
     public long getMinVersionCode() {
         return mMinVersionCode;
     }
 
-    /** Returns the minimum version code the override applies from. */
+    /**
+     * Returns the maximum APK version code the override applies from.
+     *
+     * @see PackageInfo#getLongVersionCode()
+     */
     public long getMaxVersionCode() {
         return mMaxVersionCode;
     }
@@ -146,9 +155,11 @@
         private boolean mEnabled;
 
         /**
-         * Sets the minimum version code the override should apply from.
+         * Sets the minimum APK version code the override should apply from.
          *
          * default value: {@code Long.MIN_VALUE}.
+         *
+         * @see PackageInfo#getLongVersionCode()
          */
         @NonNull
         public Builder setMinVersionCode(long minVersionCode) {
@@ -157,9 +168,11 @@
         }
 
         /**
-         * Sets the maximum version code the override should apply to.
+         * Sets the maximum APK version code the override should apply to.
          *
          * default value: {@code Long.MAX_VALUE}.
+         *
+         * @see PackageInfo#getLongVersionCode()
          */
         @NonNull
         public Builder setMaxVersionCode(long maxVersionCode) {
diff --git a/core/java/android/app/search/Query.java b/core/java/android/app/search/Query.java
index 3ab20bb..34ace48 100644
--- a/core/java/android/app/search/Query.java
+++ b/core/java/android/app/search/Query.java
@@ -16,63 +16,108 @@
 package android.app.search;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
+ * Query object is sent over from client to the service.
+ *
+ * Inside the query object, there is a timestamp that trackes when the query string was typed.
+ *
+ * If this object was created for the {@link SearchSession#query},
+ * the client expects first consumer to be returned
+ * within {@link #getTimestampMillis()} + {@link SearchContext#getTimeoutMillis()}
+ * Base of the timestamp should be SystemClock.elasedRealTime()
+ *
  * @hide
  */
 @SystemApi
 public final class Query implements Parcelable {
 
     /**
-     * Query string typed from the client.
+     * string typed from the client.
      */
     @NonNull
     private final String mInput;
 
-    /**
-     * The timestamp that the query string was typed. If this object was created for the
-     * {@link SearchSession#query}, the client expects first consumer to be returned
-     * within mTimestamp + {@link SearchContext#mTimeoutMillis}
-     */
-    private final long mTimestamp;
+    private final long mTimestampMillis;
 
-    @Nullable
+    /**
+     * Contains other client UI constraints related data
+     */
+    @NonNull
     private final Bundle mExtras;
 
+    /**
+     * Query object used to pass search box input from client to service.
+     *
+     * @param input string typed from the client
+     * @param timestampMillis timestamp that query string was typed.
+     * @param extras bundle that contains other client UI constraints data
+     */
     public Query(@NonNull String input,
-            long timestamp,
-            @SuppressLint("NullableCollection")
-            @Nullable Bundle extras) {
+            long timestampMillis,
+            @NonNull Bundle extras) {
         mInput = input;
-        mTimestamp = timestamp;
-        mExtras = extras;
+        mTimestampMillis = timestampMillis;
+        mExtras = extras == null ? extras : new Bundle();
+    }
+
+    /**
+     * Query object used to pass search box input from client to service.
+     *
+     * @param input string typed from the client
+     * @param timestampMillis timestamp that query string was typed
+     */
+    public Query(@NonNull String input, long timestampMillis) {
+        this(input, timestampMillis, new Bundle());
     }
 
     private Query(Parcel parcel) {
         mInput = parcel.readString();
-        mTimestamp = parcel.readLong();
+        mTimestampMillis = parcel.readLong();
         mExtras = parcel.readBundle();
     }
 
+    /**
+     * @return string typed from the client
+     */
     @NonNull
     public String getInput() {
         return mInput;
     }
 
+    /**
+     * @deprecated Will be replaced by {@link #getTimestampMillis()} as soon as
+     * new SDK is adopted.
+     *
+     * @removed
+     */
+    @Deprecated
     @NonNull
     public long getTimestamp() {
-        return mTimestamp;
+        return mTimestampMillis;
     }
 
-    @Nullable
-    @SuppressLint("NullableCollection")
+    /**
+     * Base of the timestamp should be SystemClock.elasedRealTime()
+     *
+     * @return timestamp that query string was typed
+     */
+    public long getTimestampMillis() {
+        return mTimestampMillis;
+    }
+
+    /**
+     * @return bundle that contains other client constraints related to the query
+     */
+    @NonNull
     public Bundle getExtras() {
+        if (mExtras == null) {
+            return new Bundle();
+        }
         return mExtras;
     }
 
@@ -84,7 +129,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mInput);
-        dest.writeLong(mTimestamp);
+        dest.writeLong(mTimestampMillis);
         dest.writeBundle(mExtras);
     }
 
diff --git a/core/java/android/app/search/SearchContext.java b/core/java/android/app/search/SearchContext.java
index 548b7da..3e345fa 100644
--- a/core/java/android/app/search/SearchContext.java
+++ b/core/java/android/app/search/SearchContext.java
@@ -17,13 +17,20 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
 /**
+ * When {@link SearchSession} is created, {@link SearchContext} object is created
+ * to pass the result types from the {@link SearchSession#query(Query, Executor, Consumer)}
+ * method that the client wants.
+ *
  * @hide
  */
 @SystemApi
@@ -51,12 +58,25 @@
     @Nullable
     private String mPackageName;
 
+    /**
+     * @param resultTypes {@link SearchTarget.SearchResultType}s combined using bit OR operation
+     * @param timeoutMillis timeout before client renders its own fallback result
+     */
+    public SearchContext(int resultTypes, int timeoutMillis) {
+        this(resultTypes, timeoutMillis, new Bundle());
+    }
+
+    /**
+     * @param resultTypes {@link SearchTarget.SearchResultType}s combined using bit OR operation
+     * @param timeoutMillis timeout before client renders its own fallback result
+     * @param extras other client constraints (e.g., height of the search surface)
+     */
     public SearchContext(int resultTypes,
-            int queryTimeoutMillis,
-            @SuppressLint("NullableCollection") @Nullable Bundle extras) {
+            int timeoutMillis,
+            @NonNull Bundle extras) {
         mResultTypes = resultTypes;
-        mTimeoutMillis = queryTimeoutMillis;
-        mExtras = extras;
+        mTimeoutMillis = timeoutMillis;
+        mExtras = Objects.requireNonNull(extras);
     }
 
     private SearchContext(Parcel parcel) {
@@ -74,7 +94,7 @@
     /**
      * @hide
      */
-    public void setPackageName(@Nullable String packageName) {
+    void setPackageName(@Nullable String packageName) {
         mPackageName = packageName;
     }
 
@@ -83,8 +103,7 @@
         return mTimeoutMillis;
     }
 
-    @Nullable
-    @SuppressLint("NullableCollection")
+    @NonNull
     public Bundle getExtras() {
         return mExtras;
     }
diff --git a/core/java/android/app/search/SearchSession.java b/core/java/android/app/search/SearchSession.java
index 7bd88d9..a5425a2 100644
--- a/core/java/android/app/search/SearchSession.java
+++ b/core/java/android/app/search/SearchSession.java
@@ -37,7 +37,9 @@
 import java.util.function.Consumer;
 
 /**
- * Client API to share information about the search UI state and execute query.
+ * Client needs to create {@link SearchSession} object from in order to execute
+ * {@link #query(Query, Executor, Consumer)} method and share client side signals
+ * back to the service using {@link #notifyEvent(Query, SearchTargetEvent)}.
  *
  * <p>
  * Usage: <pre> {@code
@@ -60,7 +62,7 @@
  *    }
  *
  *    void onDestroy() {
- *        mSearchSession.destroy();
+ *        mSearchSession.close();
  *    }
  *
  * }</pre>
@@ -108,7 +110,10 @@
     }
 
     /**
-     * Notifies the search service of an search target event.
+     * Notifies the search service of an search target event (e.g., user interaction
+     * and lifecycle event of the search surface).
+     *
+     * {@see SearchTargetEvent}
      *
      * @param query input object associated with the event.
      * @param event The {@link SearchTargetEvent} that represents the search target event.
@@ -153,7 +158,11 @@
     /**
      * Destroys the client and unregisters the callback. Any method on this class after this call
      * will throw {@link IllegalStateException}.
+     *
+     * @deprecated
+     * @removed
      */
+    @Deprecated
     public void destroy() {
         if (!mIsClosed.getAndSet(true)) {
             mCloseGuard.close();
@@ -188,6 +197,11 @@
         }
     }
 
+    /**
+     * Destroys the client and unregisters the callback. Any method on this class after this call
+     * will throw {@link IllegalStateException}.
+     *
+     */
     @Override
     public void close() {
         try {
diff --git a/core/java/android/app/search/SearchTarget.java b/core/java/android/app/search/SearchTarget.java
index 6a80f8b..56c5ddf 100644
--- a/core/java/android/app/search/SearchTarget.java
+++ b/core/java/android/app/search/SearchTarget.java
@@ -15,34 +15,73 @@
  */
 package android.app.search;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.app.slice.SliceManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
- * A representation of a searchable item info.
+ * A representation of a search result. Search result can be expressed in one of the following:
+ * app icon, shortcut, slice, widget, or a custom object using {@link SearchAction}. While
+ * app icon ({@link PackageManager}, shortcut {@link ShortcutManager}, slice {@link SliceManager},
+ * or widget (@link AppWidgetManager} are published content backed by the system service,
+ * {@link SearchAction} is a custom object that the service can use to send search result to the
+ * client.
+ *
+ * These various types of Android primitives could be defined as {@link SearchResultType}. Some
+ * times, the result type can define the layout type that that this object can be rendered in.
+ * (e.g., app widget). Most times, {@link #getLayoutType()} assigned by the service
+ * can recommend which layout this target should be rendered in.
+ *
+ * The service can also use fields such as {@link #getScore()} to indicate
+ * how confidence the search result is and {@link #shouldHide()} to indicate
+ * whether it is recommended to be shown by default.
+ *
+ * Finally, {@link #getId()} is the unique identifier of this search target and a single
+ * search target is defined by being able to express a single launcheable item. In case the
+ * service want to recommend how to combine multiple search target objects to render in a group
+ * (e.g., same row), {@link #getParentId()} can be assigned on the sub targets of the group
+ * using the primary search target's identifier.
  *
  * @hide
  */
 @SystemApi
 public final class SearchTarget implements Parcelable {
 
-
-    @NonNull
+    public static final int RESULT_TYPE_APPLICATION = 1 << 0;
+    public static final int RESULT_TYPE_SHORTCUT = 1 << 1;
+    public static final int RESULT_TYPE_SLICE = 1 << 2;
+    public static final int RESULT_TYPE_WIDGETS = 1 << 3;
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"RESULT_TYPE_"}, value = {
+            RESULT_TYPE_APPLICATION,
+            RESULT_TYPE_SHORTCUT,
+            RESULT_TYPE_SLICE,
+            RESULT_TYPE_WIDGETS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SearchResultType {}
     private final int mResultType;
 
     /**
-     * Constant to express how the group of {@link SearchTarget} should be laid out.
+     * Constant to express how the group of {@link SearchTarget} should be rendered on
+     * the client side. (e.g., "icon", "icon_row", "short_icon_row")
      */
     @NonNull
     private final String mLayoutType;
@@ -69,13 +108,13 @@
     private final AppWidgetProviderInfo mAppWidgetProviderInfo;
     @Nullable
     private final Uri mSliceUri;
-    @Nullable
+
+    @NonNull
     private final Bundle mExtras;
 
     private SearchTarget(Parcel parcel) {
         mResultType = parcel.readInt();
         mLayoutType = parcel.readString();
-
         mId = parcel.readString();
         mParentId = parcel.readString();
         mScore = parcel.readFloat();
@@ -102,7 +141,7 @@
             @Nullable ShortcutInfo shortcutInfo,
             @Nullable Uri sliceUri,
             @Nullable AppWidgetProviderInfo appWidgetProviderInfo,
-            @Nullable Bundle extras) {
+            @NonNull Bundle extras) {
         mResultType = resultType;
         mLayoutType = Objects.requireNonNull(layoutType);
         mId = Objects.requireNonNull(id);
@@ -129,9 +168,9 @@
     }
 
     /**
-     * Retrieves the result type.
+     * Retrieves the result type {@see SearchResultType}.
      */
-    public int getResultType() {
+    public @SearchResultType int getResultType() {
         return mResultType;
     }
 
@@ -167,7 +206,7 @@
     }
 
     /**
-     * TODO: add comment
+     * Indicates whether this object should be hidden and shown only on demand.
      */
     public boolean shouldHide() {
         return mShouldHide;
@@ -198,7 +237,7 @@
     }
 
     /**
-     * Return widget provider info.
+     * Return a widget provider info.
      */
     @Nullable
     public AppWidgetProviderInfo getAppWidgetProviderInfo() {
@@ -206,7 +245,7 @@
     }
 
     /**
-     * Return slice uri.
+     * Returns a slice uri.
      */
     @Nullable
     public Uri getSliceUri() {
@@ -214,7 +253,7 @@
     }
 
     /**
-     * Return search action.
+     * Returns a search action.
      */
     @Nullable
     public SearchAction getSearchAction() {
@@ -224,8 +263,7 @@
     /**
      * Return extra bundle.
      */
-    @Nullable
-    @SuppressLint("NullableCollection")
+    @NonNull
     public Bundle getExtras() {
         return mExtras;
     }
@@ -295,10 +333,10 @@
         private Uri mSliceUri;
         @Nullable
         private AppWidgetProviderInfo mAppWidgetProviderInfo;
-        @Nullable
+        @NonNull
         private Bundle mExtras;
 
-        public Builder(int resultType,
+        public Builder(@SearchResultType int resultType,
                 @NonNull String layoutType,
                 @NonNull String id) {
             mId = id;
@@ -369,32 +407,30 @@
          */
         @NonNull
         public Builder setSliceUri(@NonNull Uri sliceUri) {
-            // TODO: add packageName check
             mSliceUri = sliceUri;
             return this;
         }
 
         /**
-         * TODO: add comment
+         * Set the {@link SearchAction} object to this target.
          */
         @NonNull
-        public Builder setSearchAction(@Nullable SearchAction remoteAction) {
-            // TODO: add packageName check
-            mSearchAction = remoteAction;
+        public Builder setSearchAction(@Nullable SearchAction searchAction) {
+            mSearchAction = searchAction;
             return this;
         }
 
         /**
-         * TODO: add comment
+         * Set any extra information that needs to be shared between service and the client.
          */
         @NonNull
-        public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) {
-            mExtras = extras;
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = Objects.requireNonNull(extras);
             return this;
         }
 
         /**
-         * TODO: add comment
+         * Sets the score of the object.
          */
         @NonNull
         public Builder setScore(float score) {
@@ -403,7 +439,7 @@
         }
 
         /**
-         * TODO: add comment
+         * Sets whether the result should be hidden by default inside client.
          */
         @NonNull
         public Builder setShouldHide(boolean shouldHide) {
diff --git a/core/java/android/app/search/SearchTargetEvent.java b/core/java/android/app/search/SearchTargetEvent.java
index f478dc3..d4915af 100644
--- a/core/java/android/app/search/SearchTargetEvent.java
+++ b/core/java/android/app/search/SearchTargetEvent.java
@@ -29,7 +29,11 @@
 import java.util.Objects;
 
 /**
- * A representation of an app target event.
+ * A representation of an search target event.
+ *
+ * There are two types of events. First type of event correspends to the user interaction
+ * that happens on the search surface. (e.g., {@link #ACTION_TAP}. Second type of events
+ * correspends to the lifecycle event of the search surface {@link #ACTION_SURFACE_VISIBLE}.
  *
  * @hide
  */
diff --git a/core/java/android/app/search/SearchUiManager.java b/core/java/android/app/search/SearchUiManager.java
index 636bfe1..ce6d8b2 100644
--- a/core/java/android/app/search/SearchUiManager.java
+++ b/core/java/android/app/search/SearchUiManager.java
@@ -25,6 +25,12 @@
 /**
  * Class that provides methods to create search ui session clients.
  *
+ * Usage: <pre> {@code
+ *    mSearchUiManager = context.getSystemService(SearchUiManager.class);
+ *    mSearchSession.createSearchSession(searchContext)
+ *
+ * }</pre>
+ *
  * @hide
  */
 @SystemApi
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 9dc2d8e..3bf517c 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -31,6 +31,7 @@
 import android.content.AttributionSource;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -365,6 +366,15 @@
         mAdapter = adapter;
         mAttributionSource = adapter.getAttributionSource();
 
+        // Preserve legacy compatibility where apps were depending on
+        // registerStateChangeCallback() performing a permissions check which
+        // has been relaxed in modern platform versions
+        if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
+                && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
+                        != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Need BLUETOOTH permission");
+        }
+
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
         if (mgr != null) {
             try {
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 2600029..8ce01a3 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -136,6 +137,16 @@
         mServiceListener = l;
         mAdapter = adapter;
         mAttributionSource = adapter.getAttributionSource();
+
+        // Preserve legacy compatibility where apps were depending on
+        // registerStateChangeCallback() performing a permissions check which
+        // has been relaxed in modern platform versions
+        if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
+                && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
+                        != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Need BLUETOOTH permission");
+        }
+
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
         if (mgr != null) {
             try {
diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java
index b20ab75..beff841 100644
--- a/core/java/android/bluetooth/BluetoothProfileConnector.java
+++ b/core/java/android/bluetooth/BluetoothProfileConnector.java
@@ -21,6 +21,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -123,6 +125,16 @@
         mContext = context;
         mServiceListener = listener;
         IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
+
+        // Preserve legacy compatibility where apps were depending on
+        // registerStateChangeCallback() performing a permissions check which
+        // has been relaxed in modern platform versions
+        if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
+                && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
+                        != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Need BLUETOOTH permission");
+        }
+
         if (mgr != null) {
             try {
                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 7e1df1b..114ad87 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -1885,9 +1885,9 @@
      * in {@link android.provider.MediaStore.MediaColumns}.</p>
      *
      * @param uri The URI whose file is to be opened.
-     * @param mode Access mode for the file.  May be "r" for read-only access,
-     * "rw" for read and write access, or "rwt" for read and write access
-     * that truncates any existing file.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      *
      * @return Returns a new ParcelFileDescriptor which you can use to access
      * the file.
@@ -1948,10 +1948,9 @@
      * in {@link android.provider.MediaStore.MediaColumns}.</p>
      *
      * @param uri The URI whose file is to be opened.
-     * @param mode Access mode for the file. May be "r" for read-only access,
-     *            "w" for write-only access, "rw" for read and write access, or
-     *            "rwt" for read and write access that truncates any existing
-     *            file.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @param signal A signal to cancel the operation in progress, or
      *            {@code null} if none. For example, if you are downloading a
      *            file from the network to service a "rw" mode request, you
@@ -2011,11 +2010,9 @@
      * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
      *
      * @param uri The URI whose file is to be opened.
-     * @param mode Access mode for the file.  May be "r" for read-only access,
-     * "w" for write-only access (erasing whatever data is currently in
-     * the file), "wa" for write-only access to append to any existing data,
-     * "rw" for read and write access on any existing data, and "rwt" for read
-     * and write access that truncates any existing file.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      *
      * @return Returns a new AssetFileDescriptor which you can use to access
      * the file.
@@ -2068,11 +2065,9 @@
      * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
      *
      * @param uri The URI whose file is to be opened.
-     * @param mode Access mode for the file.  May be "r" for read-only access,
-     * "w" for write-only access (erasing whatever data is currently in
-     * the file), "wa" for write-only access to append to any existing data,
-     * "rw" for read and write access on any existing data, and "rwt" for read
-     * and write access that truncates any existing file.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @param signal A signal to cancel the operation in progress, or
      *            {@code null} if none. For example, if you are downloading a
      *            file from the network to service a "rw" mode request, you
@@ -2103,11 +2098,9 @@
      * by looking up a column named "_data" at the given URI.
      *
      * @param uri The URI to be opened.
-     * @param mode The file mode.  May be "r" for read-only access,
-     * "w" for write-only access (erasing whatever data is currently in
-     * the file), "wa" for write-only access to append to any existing data,
-     * "rw" for read and write access on any existing data, and "rwt" for read
-     * and write access that truncates any existing file.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      *
      * @return Returns a new ParcelFileDescriptor that can be used by the
      * client to access the file.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index aec39da..1132991 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -63,7 +63,9 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.system.ErrnoException;
 import android.system.Int64Ref;
+import android.system.Os;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -76,8 +78,10 @@
 import dalvik.system.CloseGuard;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -864,6 +868,20 @@
         return wrap((ContentInterface) wrapped);
     }
 
+    /**
+     * Offer to locally truncate the given file when opened using the write-only
+     * mode. This is typically used to preserve legacy compatibility behavior.
+     */
+    private static void maybeTruncate(FileDescriptor fd, String mode) throws FileNotFoundException {
+        if ("w".equals(mode)) {
+            try {
+                Os.ftruncate(fd, 0);
+            } catch (ErrnoException e) {
+                throw new FileNotFoundException("Failed to truncate: " + e.getMessage());
+            }
+        }
+    }
+
     /** @hide */
     @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
@@ -1525,8 +1543,20 @@
     }
 
     /**
-     * Synonym for {@link #openOutputStream(Uri, String)
-     * openOutputStream(uri, "w")}.
+     * Open a stream on to the content associated with a content URI.  If there
+     * is no data associated with the URI, FileNotFoundException is thrown.
+     *
+     * <h5>Accepts the following URI schemes:</h5>
+     * <ul>
+     * <li>content ({@link #SCHEME_CONTENT})</li>
+     * <li>file ({@link #SCHEME_FILE})</li>
+     * </ul>
+     *
+     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
+     * on these schemes.
+     *
+     * <p>This method behaves like {@link FileOutputStream} and automatically
+     * truncates any existing contents.
      *
      * @param uri The desired URI.
      * @return an OutputStream or {@code null} if the provider recently crashed.
@@ -1534,7 +1564,16 @@
      */
     public final @Nullable OutputStream openOutputStream(@NonNull Uri uri)
             throws FileNotFoundException {
-        return openOutputStream(uri, "w");
+        AssetFileDescriptor fd = openAssetFileDescriptor(uri, "w", null);
+        if (fd == null) return null;
+        try {
+            final FileOutputStream res = fd.createOutputStream();
+            // Unconditionally truncate to mirror FileOutputStream behavior
+            maybeTruncate(res.getFD(), "w");
+            return res;
+        } catch (IOException e) {
+            throw new FileNotFoundException("Unable to create stream");
+        }
     }
 
     /**
@@ -1551,7 +1590,9 @@
      * on these schemes.
      *
      * @param uri The desired URI.
-     * @param mode May be "w", "wa", "rw", or "rwt".
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @return an OutputStream or {@code null} if the provider recently crashed.
      * @throws FileNotFoundException if the provided URI could not be opened.
      * @see #openAssetFileDescriptor(Uri, String)
@@ -1559,8 +1600,14 @@
     public final @Nullable OutputStream openOutputStream(@NonNull Uri uri, @NonNull String mode)
             throws FileNotFoundException {
         AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode, null);
+        if (fd == null) return null;
         try {
-            return fd != null ? fd.createOutputStream() : null;
+            final FileOutputStream res = fd.createOutputStream();
+            // Preserve legacy behavior by offering to truncate
+            if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+                maybeTruncate(res.getFD(), mode);
+            }
+            return res;
         } catch (IOException e) {
             throw new FileNotFoundException("Unable to create stream");
         }
@@ -1607,8 +1654,9 @@
      * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
      *
      * @param uri The desired URI to open.
-     * @param mode The file mode to use, as per {@link ContentProvider#openFile
-     * ContentProvider.openFile}.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
      * provider recently crashed. You own this descriptor and are responsible for closing it
      * when done.
@@ -1650,8 +1698,9 @@
      * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
      *
      * @param uri The desired URI to open.
-     * @param mode The file mode to use, as per {@link ContentProvider#openFile
-     * ContentProvider.openFile}.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @param cancellationSignal A signal to cancel the operation in progress,
      *         or null if none. If the operation is canceled, then
      *         {@link OperationCanceledException} will be thrown.
@@ -1744,8 +1793,9 @@
      * from any built-in data conversion that a provider implements.
      *
      * @param uri The desired URI to open.
-     * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
-     * ContentProvider.openAssetFile}.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
      * provider recently crashed. You own this descriptor and are responsible for closing it
      * when done.
@@ -1798,8 +1848,9 @@
      * from any built-in data conversion that a provider implements.
      *
      * @param uri The desired URI to open.
-     * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
-     * ContentProvider.openAssetFile}.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @param cancellationSignal A signal to cancel the operation in progress, or null if
      *            none. If the operation is canceled, then
      *            {@link OperationCanceledException} will be thrown.
@@ -1835,6 +1886,10 @@
         } else if (SCHEME_FILE.equals(scheme)) {
             ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
                     new File(uri.getPath()), ParcelFileDescriptor.parseMode(mode));
+            // Preserve legacy behavior by offering to truncate
+            if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+                maybeTruncate(pfd.getFileDescriptor(), mode);
+            }
             return new AssetFileDescriptor(pfd, 0, -1);
         } else {
             if ("r".equals(mode)) {
@@ -1892,6 +1947,11 @@
                     // ParcelFileDescriptorInner do that when it is closed.
                     stableProvider = null;
 
+                    // Preserve legacy behavior by offering to truncate
+                    if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+                        maybeTruncate(pfd.getFileDescriptor(), mode);
+                    }
+
                     return new AssetFileDescriptor(pfd, fd.getStartOffset(),
                             fd.getDeclaredLength());
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b498325..7c7cfdb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6397,7 +6397,9 @@
     public static final int FLAG_ACTIVITY_NO_HISTORY = 0x40000000;
     /**
      * If set, the activity will not be launched if it is already running
-     * at the top of the history stack.
+     * at the top of the history stack.  See
+     * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html#TaskLaunchModes">
+     * Tasks and Back Stack</a> for more information.
      */
     public static final int FLAG_ACTIVITY_SINGLE_TOP = 0x20000000;
     /**
@@ -6537,8 +6539,7 @@
     public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0x00200000;
     /**
      * This flag is not normally set by application code, but set for you by
-     * the system if this activity is being launched from history
-     * (longpress home key).
+     * the system if this activity is being launched from history.
      */
     public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0x00100000;
     /**
@@ -6571,7 +6572,9 @@
      * equivalent of the Activity manifest specifying {@link
      * android.R.attr#documentLaunchMode}="intoExisting". When used with
      * FLAG_ACTIVITY_MULTIPLE_TASK it is the equivalent of the Activity manifest specifying
-     * {@link android.R.attr#documentLaunchMode}="always".
+     * {@link android.R.attr#documentLaunchMode}="always". The flag is ignored even in
+     * conjunction with {@link #FLAG_ACTIVITY_MULTIPLE_TASK} when the Activity manifest specifies
+     * {@link android.R.attr#documentLaunchMode}="never".
      *
      * Refer to {@link android.R.attr#documentLaunchMode} for more information.
      *
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
index f0def805..4e674f6 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -4,7 +4,8 @@
 toddke@google.com
 patb@google.com
 
-per-file PackageParser.java = chiuwinson@google.com
+per-file PackageParser.java = set noparent
+per-file PackageParser.java = chiuwinson@google.com,patb@google.com,toddke@google.com
 per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
 per-file AppSearchPerson.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
 per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 86a8a9d..4ff2624 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -214,7 +214,6 @@
     public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
     public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
             "android.activity_window_layout_affinity";
-    public static final String METADATA_ACTIVITY_LAUNCH_MODE = "android.activity.launch_mode";
 
     /**
      * Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 0fc6b2b..e3aca97 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -183,6 +183,7 @@
     public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
     public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
             "android.activity_window_layout_affinity";
+    public static final String METADATA_ACTIVITY_LAUNCH_MODE = "android.activity.launch_mode";
 
     public static final int SDK_VERSION = Build.VERSION.SDK_INT;
     public static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index ff6aaad..aa740bd 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -24,7 +24,6 @@
 import android.app.ActivityTaskManager;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.pm.PackageParser;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
@@ -414,9 +413,9 @@
 
         if (!isAlias && activity.launchMode != LAUNCH_SINGLE_INSTANCE_PER_TASK
                 && activity.metaData != null && activity.metaData.containsKey(
-                PackageParser.METADATA_ACTIVITY_LAUNCH_MODE)) {
+                ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE)) {
             final String launchMode = activity.metaData.getString(
-                    PackageParser.METADATA_ACTIVITY_LAUNCH_MODE);
+                    ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE);
             if (launchMode != null && launchMode.equals("singleInstancePerTask")) {
                 activity.launchMode = LAUNCH_SINGLE_INSTANCE_PER_TASK;
             }
diff --git a/core/java/android/hardware/Battery.java b/core/java/android/hardware/BatteryState.java
similarity index 91%
rename from core/java/android/hardware/Battery.java
rename to core/java/android/hardware/BatteryState.java
index 24c8d76..aa75359 100644
--- a/core/java/android/hardware/Battery.java
+++ b/core/java/android/hardware/BatteryState.java
@@ -24,9 +24,9 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * The Battery class is a representation of a single battery on a device.
+ * The BatteryState class is a representation of a single battery on a device.
  */
-public abstract class Battery {
+public abstract class BatteryState {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "STATUS_" }, value = {
@@ -55,7 +55,7 @@
      *
      * @return True if the hardware has a battery, else false.
      */
-    public abstract boolean hasBattery();
+    public abstract boolean isPresent();
 
     /**
      * Get the battery status.
@@ -66,7 +66,7 @@
 
     /**
      * Get remaining battery capacity as float percentage [0.0f, 1.0f] of total capacity
-     * Returns -1 when battery capacity can't be read.
+     * Returns NaN when battery capacity can't be read.
      *
      * @return the battery capacity.
      */
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 948da3f..f3a8342 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -34,6 +34,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.util.Slog;
 
@@ -557,7 +558,7 @@
      * @hide
      */
     public long[] getAuthenticatorIds() {
-        return getAuthenticatorIds(UserHandle.getCallingUserId());
+        return getAuthenticatorIds(UserHandle.myUserId());
     }
 
     /**
diff --git a/core/java/android/hardware/input/InputDeviceBattery.java b/core/java/android/hardware/input/InputDeviceBatteryState.java
similarity index 84%
rename from core/java/android/hardware/input/InputDeviceBattery.java
rename to core/java/android/hardware/input/InputDeviceBatteryState.java
index 0fe124e..d069eb0 100644
--- a/core/java/android/hardware/input/InputDeviceBattery.java
+++ b/core/java/android/hardware/input/InputDeviceBatteryState.java
@@ -19,28 +19,28 @@
 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
 import static android.os.IInputConstants.INVALID_BATTERY_CAPACITY;
 
-import android.hardware.Battery;
+import android.hardware.BatteryState;
 
 /**
  * Battery implementation for input devices.
  *
  * @hide
  */
-public final class InputDeviceBattery extends Battery {
-    private static final float NULL_BATTERY_CAPACITY = -1.0f;
+public final class InputDeviceBatteryState extends BatteryState {
+    private static final float NULL_BATTERY_CAPACITY = Float.NaN;
 
     private final InputManager mInputManager;
     private final int mDeviceId;
     private final boolean mHasBattery;
 
-    InputDeviceBattery(InputManager inputManager, int deviceId, boolean hasBattery) {
+    InputDeviceBatteryState(InputManager inputManager, int deviceId, boolean hasBattery) {
         mInputManager = inputManager;
         mDeviceId = deviceId;
         mHasBattery = hasBattery;
     }
 
     @Override
-    public boolean hasBattery() {
+    public boolean isPresent() {
         return mHasBattery;
     }
 
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 648fda7..51d196d 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1571,12 +1571,12 @@
     }
 
     /**
-     * Gets a battery object associated with an input device, assuming it has one.
+     * Gets a battery state object associated with an input device, assuming it has one.
      * @return The battery, never null.
      * @hide
      */
-    public InputDeviceBattery getInputDeviceBattery(int deviceId, boolean hasBattery) {
-        return new InputDeviceBattery(this, deviceId, hasBattery);
+    public InputDeviceBatteryState getInputDeviceBatteryState(int deviceId, boolean hasBattery) {
+        return new InputDeviceBatteryState(this, deviceId, hasBattery);
     }
 
     /**
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index 2c78fcb..2812868 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -141,6 +141,11 @@
         return mId;
     }
 
+    @Override
+    public String toString() {
+        return "[Name=" + mName + " Id=" + mId + " Type=" + mType + " Ordinal=" + mOrdinal + "]";
+    }
+
     /**
      * Returns the id of the light.
      *
diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java
index cbcef86..2d9bc0e 100644
--- a/core/java/android/hardware/lights/LightsManager.java
+++ b/core/java/android/hardware/lights/LightsManager.java
@@ -113,6 +113,12 @@
         private final IBinder mToken = new Binder();
 
         /**
+         * @hide to prevent subclassing from outside of the framework
+         */
+        public LightsSession() {
+        }
+
+        /**
          * Sends a request to modify the states of multiple lights.
          *
          * @param request the settings for lights that should change
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4defc55..d7b96df 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1947,12 +1947,12 @@
 
     public void showStatusIcon(@DrawableRes int iconResId) {
         mStatusIcon = iconResId;
-        mPrivOps.updateStatusIcon(getPackageName(), iconResId);
+        mPrivOps.updateStatusIconAsync(getPackageName(), iconResId);
     }
 
     public void hideStatusIcon() {
         mStatusIcon = 0;
-        mPrivOps.updateStatusIcon(null, 0);
+        mPrivOps.updateStatusIconAsync(null, 0);
     }
 
     /**
@@ -2312,7 +2312,7 @@
         if (setVisible) {
             cancelImeSurfaceRemoval();
         }
-        mPrivOps.applyImeVisibility(setVisible
+        mPrivOps.applyImeVisibilityAsync(setVisible
                 ? mCurShowInputToken : mCurHideInputToken, setVisible);
     }
 
@@ -3319,7 +3319,7 @@
             if (mNotifyUserActionSent) {
                 return;
             }
-            mPrivOps.notifyUserAction();
+            mPrivOps.notifyUserActionAsync();
             mNotifyUserActionSent = true;
         }
     }
diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java
index 459fdac..33f9375 100644
--- a/core/java/android/net/UnderlyingNetworkInfo.java
+++ b/core/java/android/net/UnderlyingNetworkInfo.java
@@ -71,13 +71,13 @@
 
     /** Get the interface name of this network. */
     @NonNull
-    public String getIface() {
+    public String getInterface() {
         return mIface;
     }
 
     /** Get the names of the interfaces underlying this network. */
     @NonNull
-    public List<String> getUnderlyingIfaces() {
+    public List<String> getUnderlyingInterfaces() {
         return mUnderlyingIfaces;
     }
 
@@ -124,8 +124,8 @@
         if (!(o instanceof UnderlyingNetworkInfo)) return false;
         final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o;
         return mOwnerUid == that.getOwnerUid()
-                && Objects.equals(mIface, that.getIface())
-                && Objects.equals(mUnderlyingIfaces, that.getUnderlyingIfaces());
+                && Objects.equals(mIface, that.getInterface())
+                && Objects.equals(mUnderlyingIfaces, that.getUnderlyingInterfaces());
     }
 
     @Override
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index eedeeb5..f02346b 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -141,7 +141,7 @@
      * <p>To ensure the device is not constantly being woken up, this retry interval MUST be greater
      * than this value.
      *
-     * @see {@link Builder#setRetryIntervalsMs()}
+     * @see {@link Builder#setRetryIntervalsMillis()}
      */
     private static final long MINIMUM_REPEATING_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15);
 
@@ -342,10 +342,10 @@
     /**
      * Retrieves the configured retry intervals.
      *
-     * @see Builder#setRetryIntervalsMs(long[])
+     * @see Builder#setRetryIntervalsMillis(long[])
      */
     @NonNull
-    public long[] getRetryIntervalsMs() {
+    public long[] getRetryIntervalsMillis() {
         return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
     }
 
@@ -555,7 +555,7 @@
          * @see VcnManager for additional discussion on fail-safe mode
          */
         @NonNull
-        public Builder setRetryIntervalsMs(@NonNull long[] retryIntervalsMs) {
+        public Builder setRetryIntervalsMillis(@NonNull long[] retryIntervalsMs) {
             validateRetryInterval(retryIntervalsMs);
 
             mRetryIntervalsMs = retryIntervalsMs;
diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java
index 4d8cf91..0e9ccf1 100644
--- a/core/java/android/net/vcn/VcnTransportInfo.java
+++ b/core/java/android/net/vcn/VcnTransportInfo.java
@@ -16,14 +16,23 @@
 
 package android.net.vcn;
 
+import static android.net.NetworkCapabilities.REDACT_ALL;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
 import android.net.TransportInfo;
 import android.net.wifi.WifiInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.SubscriptionManager;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Objects;
 
 /**
@@ -37,28 +46,41 @@
  * SubscriptionManager#INVALID_SUBSCRIPTION_ID}. If the underlying Network is Cellular, the WifiInfo
  * will be {@code null}.
  *
+ * <p>Receipt of a VcnTransportInfo requires the NETWORK_SETTINGS permission; else the entire
+ * VcnTransportInfo instance will be redacted.
+ *
  * @hide
  */
 public class VcnTransportInfo implements TransportInfo, Parcelable {
     @Nullable private final WifiInfo mWifiInfo;
     private final int mSubId;
 
+    /**
+     * The redaction scheme to use when parcelling.
+     *
+     * <p>The TransportInfo/NetworkCapabilities redaction mechanisms rely on redaction being
+     * performed at parcelling time. This means that the redaction scheme must be stored for later
+     * use.
+     *
+     * <p>Since the redaction scheme itself is not parcelled, this field is listed as a transient.
+     *
+     * <p>Defaults to REDACT_ALL when constructed using public constructors, or creating from
+     * parcels.
+     */
+    private final transient long mRedactions;
+
     public VcnTransportInfo(@NonNull WifiInfo wifiInfo) {
-        this(wifiInfo, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        this(wifiInfo, INVALID_SUBSCRIPTION_ID, REDACT_ALL);
     }
 
     public VcnTransportInfo(int subId) {
-        this(null /* wifiInfo */, subId);
+        this(null /* wifiInfo */, subId, REDACT_ALL);
     }
 
-    private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) {
-        if (wifiInfo == null && subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            throw new IllegalArgumentException(
-                    "VcnTransportInfo requires either non-null WifiInfo or valid subId");
-        }
-
+    private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId, long redactions) {
         mWifiInfo = wifiInfo;
         mSubId = subId;
+        mRedactions = redactions;
     }
 
     /**
@@ -86,8 +108,19 @@
         return mSubId;
     }
 
+    /**
+     * Gets the redaction scheme
+     *
+     * @hide
+     */
+    @VisibleForTesting(visibility = PRIVATE)
+    public long getRedaction() {
+        return mRedactions;
+    }
+
     @Override
     public int hashCode() {
+        // mRedactions not hashed, as it is a transient, for control of parcelling
         return Objects.hash(mWifiInfo, mSubId);
     }
 
@@ -96,6 +129,7 @@
         if (!(o instanceof VcnTransportInfo)) return false;
         final VcnTransportInfo that = (VcnTransportInfo) o;
 
+        // mRedactions not compared, as it is a transient, for control of parcelling
         return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId;
     }
 
@@ -105,17 +139,59 @@
         return 0;
     }
 
+    @Override
+    @NonNull
+    public TransportInfo makeCopy(long redactions) {
+        return new VcnTransportInfo(
+                mWifiInfo == null ? null : mWifiInfo.makeCopy(redactions), mSubId, redactions);
+    }
+
+    @Override
+    public long getApplicableRedactions() {
+        long redactions = REDACT_FOR_NETWORK_SETTINGS;
+
+        // Add additional wifi redactions if necessary
+        if (mWifiInfo != null) {
+            redactions |= mWifiInfo.getApplicableRedactions();
+        }
+
+        return redactions;
+    }
+
+    private boolean shouldParcelNetworkSettingsFields() {
+        return (mRedactions & NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS) == 0;
+    }
+
     /** {@inheritDoc} */
     @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {}
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(shouldParcelNetworkSettingsFields() ? mSubId : INVALID_SUBSCRIPTION_ID);
+        dest.writeParcelable(
+                shouldParcelNetworkSettingsFields() ? (Parcelable) mWifiInfo : null, flags);
+    }
+
+    @Override
+    public String toString() {
+        return "VcnTransportInfo { mWifiInfo = " + mWifiInfo + ", mSubId = " + mSubId + " }";
+    }
 
     /** Implement the Parcelable interface */
     public static final @NonNull Creator<VcnTransportInfo> CREATOR =
             new Creator<VcnTransportInfo>() {
                 public VcnTransportInfo createFromParcel(Parcel in) {
-                    // return null instead of a default VcnTransportInfo to avoid leaking
-                    // information about this being a VCN Network (instead of macro cellular, etc)
-                    return null;
+                    final int subId = in.readInt();
+                    final WifiInfo wifiInfo = in.readParcelable(null);
+
+                    // If all fields are their null values, return null TransportInfo to avoid
+                    // leaking information about this being a VCN Network (instead of macro
+                    // cellular, etc)
+                    if (wifiInfo == null && subId == INVALID_SUBSCRIPTION_ID) {
+                        return null;
+                    }
+
+                    // Prevent further forwarding by redacting everything in future parcels from
+                    // this VcnTransportInfo
+                    return new VcnTransportInfo(wifiInfo, subId, REDACT_ALL);
                 }
 
                 public VcnTransportInfo[] newArray(int size) {
diff --git a/core/java/android/nfc/NfcControllerAlwaysOnListener.java b/core/java/android/nfc/NfcControllerAlwaysOnListener.java
index 96707bb..6ae58fd 100644
--- a/core/java/android/nfc/NfcControllerAlwaysOnListener.java
+++ b/core/java/android/nfc/NfcControllerAlwaysOnListener.java
@@ -52,6 +52,14 @@
      */
     public void register(@NonNull Executor executor,
             @NonNull ControllerAlwaysOnListener listener) {
+        try {
+            if (!mAdapter.isControllerAlwaysOnSupported()) {
+                return;
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to register");
+            return;
+        }
         synchronized (this) {
             if (mListenerMap.containsKey(listener)) {
                 return;
@@ -75,6 +83,14 @@
      * @param listener user implementation of the {@link ControllerAlwaysOnListener}
      */
     public void unregister(@NonNull ControllerAlwaysOnListener listener) {
+        try {
+            if (!mAdapter.isControllerAlwaysOnSupported()) {
+                return;
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to unregister");
+            return;
+        }
         synchronized (this) {
             if (!mListenerMap.containsKey(listener)) {
                 return;
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index c084787..0c9ad1b 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -31,6 +31,7 @@
 import static android.system.OsConstants.S_ISREG;
 import static android.system.OsConstants.S_IWOTH;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -63,6 +64,8 @@
 import java.io.IOException;
 import java.io.InterruptedIOException;
 import java.io.UncheckedIOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.DatagramSocket;
 import java.net.Socket;
 import java.nio.ByteOrder;
@@ -110,6 +113,20 @@
 
     private final CloseGuard mGuard = CloseGuard.get();
 
+    /** @hide */
+    @IntDef(prefix = {"MODE_"}, value = {
+            MODE_WORLD_READABLE,
+            MODE_WORLD_WRITEABLE,
+            MODE_READ_ONLY,
+            MODE_WRITE_ONLY,
+            MODE_READ_WRITE,
+            MODE_CREATE,
+            MODE_TRUNCATE,
+            MODE_APPEND,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Mode { }
+
     /**
      * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied and
      * this file doesn't already exist, then create the file with permissions
@@ -227,7 +244,8 @@
      *             be opened with the requested mode.
      * @see #parseMode(String)
      */
-    public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException {
+    public static ParcelFileDescriptor open(File file, @Mode int mode)
+            throws FileNotFoundException {
         final FileDescriptor fd = openInternal(file, mode);
         if (fd == null) return null;
 
@@ -259,7 +277,7 @@
     // We can't accept a generic Executor here, since we need to use
     // MessageQueue.addOnFileDescriptorEventListener()
     @SuppressLint("ExecutorRegistration")
-    public static ParcelFileDescriptor open(File file, int mode, Handler handler,
+    public static ParcelFileDescriptor open(File file, @Mode int mode, Handler handler,
             final OnCloseListener listener) throws IOException {
         if (handler == null) {
             throw new IllegalArgumentException("Handler must not be null");
@@ -330,7 +348,8 @@
         return pfd;
     }
 
-    private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
+    private static FileDescriptor openInternal(File file, @Mode int mode)
+            throws FileNotFoundException {
         final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);
 
         int realMode = S_IRWXU | S_IRWXG;
@@ -623,15 +642,36 @@
     }
 
     /**
-     * Converts a string representing a file mode, such as "rw", into a bitmask suitable for use
-     * with {@link #open}.
+     * Converts a string representing a file mode, such as "rw", into a bitmask
+     * suitable for use with {@link #open}.
      * <p>
-     * @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
-     *             or "rwt".
+     * The argument must define at least one of the following base access modes:
+     * <ul>
+     * <li>"r" indicates the file should be opened in read-only mode, equivalent
+     * to {@link OsConstants#O_RDONLY}.
+     * <li>"w" indicates the file should be opened in write-only mode,
+     * equivalent to {@link OsConstants#O_WRONLY}.
+     * <li>"rw" indicates the file should be opened in read-write mode,
+     * equivalent to {@link OsConstants#O_RDWR}.
+     * </ul>
+     * In addition to a base access mode, the following additional modes may
+     * requested:
+     * <ul>
+     * <li>"a" indicates the file should be opened in append mode, equivalent to
+     * {@link OsConstants#O_APPEND}. Before each write, the file offset is
+     * positioned at the end of the file.
+     * <li>"t" indicates the file should be opened in truncate mode, equivalent
+     * to {@link OsConstants#O_TRUNC}. If the file already exists and is a
+     * regular file and is opened for writing, it will be truncated to length 0.
+     * </ul>
+     *
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt".
      * @return A bitmask representing the given file mode.
-     * @throws IllegalArgumentException if the given string does not match a known file mode.
+     * @throws IllegalArgumentException if the given string does not match a
+     *             known file mode.
      */
-    public static int parseMode(String mode) {
+    public static @Mode int parseMode(String mode) {
         return FileUtils.translateModePosixToPfd(FileUtils.translateModeStringToPosix(mode));
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 98abd96..c8e5e42 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8515,6 +8515,12 @@
                 "swipe_bottom_to_notification_enabled";
 
         /**
+         * Controls whether One-Handed mode is currently activated.
+         * @hide
+         */
+        public static final String ONE_HANDED_MODE_ACTIVATED = "one_handed_mode_activated";
+
+        /**
          * For user preference if One-Handed Mode enabled.
          * @hide
          */
diff --git a/core/java/android/service/displayhash/DisplayHashParams.java b/core/java/android/service/displayhash/DisplayHashParams.java
index 2ec9d5d..123c209 100644
--- a/core/java/android/service/displayhash/DisplayHashParams.java
+++ b/core/java/android/service/displayhash/DisplayHashParams.java
@@ -48,7 +48,7 @@
     /**
      * Whether the content will be captured in grayscale or color.
      */
-    private final boolean mUseGrayscale;
+    private final boolean mGrayscaleBuffer;
 
     /**
      * A builder for {@link DisplayHashParams}
@@ -56,7 +56,7 @@
     public static final class Builder {
         @Nullable
         private Size mBufferSize;
-        private boolean mUseGrayscale;
+        private boolean mGrayscaleBuffer;
 
         /**
          * Creates a new Builder.
@@ -77,15 +77,15 @@
          * Whether the content will be captured in grayscale or color.
          */
         @NonNull
-        public Builder setUseGrayscale(boolean useGrayscale) {
-            mUseGrayscale = useGrayscale;
+        public Builder setGrayscaleBuffer(boolean grayscaleBuffer) {
+            mGrayscaleBuffer = grayscaleBuffer;
             return this;
         }
 
         /** Builds the instance. This builder should not be touched after calling this! */
         @NonNull
         public DisplayHashParams build() {
-            return new DisplayHashParams(mBufferSize, mUseGrayscale);
+            return new DisplayHashParams(mBufferSize, mGrayscaleBuffer);
         }
     }
 
@@ -112,16 +112,16 @@
      *   buffer given to the {@link DisplayHashingService#onGenerateDisplayHash(byte[],
      *   HardwareBuffer, Rect, String, DisplayHashResultCallback)} will be stretched based on the
      *   value set here. If {@code null}, the buffer size will not be changed.
-     * @param useGrayscale
+     * @param grayscaleBuffer
      *   Whether the content will be captured in grayscale or color.
      * @hide
      */
     @DataClass.Generated.Member
     public DisplayHashParams(
             @Nullable Size bufferSize,
-            boolean useGrayscale) {
+            boolean grayscaleBuffer) {
         this.mBufferSize = bufferSize;
-        this.mUseGrayscale = useGrayscale;
+        this.mGrayscaleBuffer = grayscaleBuffer;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -141,8 +141,8 @@
      * Whether the content will be captured in grayscale or color.
      */
     @DataClass.Generated.Member
-    public boolean isUseGrayscale() {
-        return mUseGrayscale;
+    public boolean isGrayscaleBuffer() {
+        return mGrayscaleBuffer;
     }
 
     @Override
@@ -153,7 +153,7 @@
 
         return "DisplayHashParams { " +
                 "bufferSize = " + mBufferSize + ", " +
-                "useGrayscale = " + mUseGrayscale +
+                "grayscaleBuffer = " + mGrayscaleBuffer +
         " }";
     }
 
@@ -164,7 +164,7 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mUseGrayscale) flg |= 0x2;
+        if (mGrayscaleBuffer) flg |= 0x2;
         if (mBufferSize != null) flg |= 0x1;
         dest.writeByte(flg);
         if (mBufferSize != null) dest.writeSize(mBufferSize);
@@ -182,11 +182,11 @@
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         byte flg = in.readByte();
-        boolean useGrayscale = (flg & 0x2) != 0;
+        boolean grayscaleBuffer = (flg & 0x2) != 0;
         Size bufferSize = (flg & 0x1) == 0 ? null : (Size) in.readSize();
 
         this.mBufferSize = bufferSize;
-        this.mUseGrayscale = useGrayscale;
+        this.mGrayscaleBuffer = grayscaleBuffer;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -206,10 +206,10 @@
     };
 
     @DataClass.Generated(
-            time = 1618436855096L,
+            time = 1619638159453L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/service/displayhash/DisplayHashParams.java",
-            inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final  boolean mUseGrayscale\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate  boolean mUseGrayscale\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setUseGrayscale(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final  boolean mGrayscaleBuffer\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate  boolean mGrayscaleBuffer\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/service/rotationresolver/RotationResolverService.java b/core/java/android/service/rotationresolver/RotationResolverService.java
index 604dd0a..0932901 100644
--- a/core/java/android/service/rotationresolver/RotationResolverService.java
+++ b/core/java/android/service/rotationresolver/RotationResolverService.java
@@ -136,9 +136,6 @@
     @MainThread
     private void resolveRotation(IRotationResolverCallback callback,
             RotationResolutionRequest request, ICancellationSignal transport) {
-        // TODO(b/175334753): The current behavior of preempted failures is based on the design we
-        //  had in Smart OS exploration. We should revisit it once we have implemented the whole
-        //  feature and tested on devices.
         if (mPendingCallback != null
                 && (mCancellationSignal == null || !mCancellationSignal.isCanceled())) {
             reportFailures(callback, ROTATION_RESULT_FAILURE_PREEMPTED);
diff --git a/core/java/android/service/search/SearchUiService.java b/core/java/android/service/search/SearchUiService.java
index d0e4195..c04dd9c 100644
--- a/core/java/android/service/search/SearchUiService.java
+++ b/core/java/android/service/search/SearchUiService.java
@@ -45,6 +45,9 @@
  * A service used to share the lifecycle of search UI (open, close, interaction)
  * and also to return search result on a query.
  *
+ * To understand the lifecycle of search session and how a query get issued,
+ * {@see SearchSession}
+ *
  * @hide
  */
 @SystemApi
@@ -71,6 +74,10 @@
         @Override
         public void onCreateSearchSession(SearchContext context, SearchSessionId sessionId) {
             mHandler.sendMessage(
+                    obtainMessage(SearchUiService::onSearchSessionCreated,
+                            SearchUiService.this, context, sessionId));
+            // to be removed
+            mHandler.sendMessage(
                     obtainMessage(SearchUiService::onCreateSearchSession,
                             SearchUiService.this, context, sessionId));
         }
@@ -119,11 +126,24 @@
 
     /**
      * Creates a new search session.
+     *
+     * @deprecated this is method will be removed as soon as
+     * {@link #onSearchSessionCreated(SearchContext, SearchSessionId)}
+     * is adopted by the service.
+     *
+     * @removed
      */
+    @Deprecated
     public void onCreateSearchSession(@NonNull SearchContext context,
             @NonNull SearchSessionId sessionId) {}
 
     /**
+     * A new search session is created.
+     */
+    public void onSearchSessionCreated(@NonNull SearchContext context,
+            @NonNull SearchSessionId sessionId) {}
+
+    /**
      * Called by the client to request search results using a query string.
      */
     @MainThread
@@ -132,7 +152,10 @@
             @NonNull Consumer<List<SearchTarget>> callback);
 
     /**
-     * Called by a client to indicate an interaction (tap, long press, drag, etc) on target(s).
+     * Called by a client to indicate an interaction (tap, long press, drag, etc) on target(s)
+     * and lifecycle event on the search surface (e.g., visibility change).
+     *
+     * {@see SearchTargetEvent}
      */
     @MainThread
     public abstract void onNotifyEvent(@NonNull SearchSessionId sessionId,
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 3b1c8ec..4f1354d 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -22,7 +22,7 @@
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.hardware.Battery;
+import android.hardware.BatteryState;
 import android.hardware.SensorManager;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
@@ -88,7 +88,7 @@
     private SensorManager mSensorManager;
 
     @GuardedBy("mMotionRanges")
-    private Battery mBattery;
+    private BatteryState mBatteryState;
 
     @GuardedBy("mMotionRanges")
     private LightsManager mLightsManager;
@@ -848,19 +848,19 @@
     }
 
     /**
-     * Gets the battery object associated with the device, if there is one.
+     * Gets the battery state object associated with the device, if there is one.
      * Even if the device does not have a battery, the result is never null.
-     * Use {@link Battery#hasBattery} to determine whether a battery is
+     * Use {@link BatteryState#isPresent} to determine whether a battery is
      * present.
      *
      * @return The battery object associated with the device, never null.
      */
     @NonNull
-    public Battery getBattery() {
-        if (mBattery == null) {
-            mBattery = InputManager.getInstance().getInputDeviceBattery(mId, mHasBattery);
+    public BatteryState getBatteryState() {
+        if (mBatteryState == null) {
+            mBatteryState = InputManager.getInstance().getInputDeviceBatteryState(mId, mHasBattery);
         }
-        return mBattery;
+        return mBatteryState;
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5a738eb..0958f3f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5191,6 +5191,17 @@
 
         @Override
         public void handleMessage(Message msg) {
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.traceBegin(Trace.TRACE_TAG_VIEW, getMessageName(msg));
+            }
+            try {
+                handleMessageImpl(msg);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+            }
+        }
+
+        private void handleMessageImpl(Message msg) {
             switch (msg.what) {
                 case MSG_INVALIDATE:
                     ((View) msg.obj).invalidate();
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index b4c1dd2..20a0b0b 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1406,7 +1406,8 @@
      */
     @Deprecated
     public void showStatusIcon(IBinder imeToken, String packageName, @DrawableRes int iconId) {
-        InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
+        InputMethodPrivilegedOperationsRegistry.get(
+                imeToken).updateStatusIconAsync(packageName, iconId);
     }
 
     /**
@@ -1416,7 +1417,7 @@
      */
     @Deprecated
     public void hideStatusIcon(IBinder imeToken) {
-        InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(null, 0);
+        InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIconAsync(null, 0);
     }
 
     /**
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index b0d95b3..8dbce1b 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -254,6 +254,11 @@
     }
 
     /** @hide */
+    public TranslationContext getTranslationContext() {
+        return mTranslationContext;
+    }
+
+    /** @hide */
     public int getTranslatorId() {
         return mId;
     }
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 0425572..bdf217d 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -451,13 +451,14 @@
             int[] supportedFormats = getSupportedFormatsLocked();
             ArrayList<ViewRootImpl> roots =
                     WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
+            TranslationCapability capability =
+                    getTranslationCapability(translator.getTranslationContext());
             mActivity.runOnUiThread(() -> {
                 // traverse the hierarchy to collect ViewTranslationRequests
                 for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
                     View rootView = roots.get(rootNum).getView();
-                    // TODO(b/183589662): call getTranslationCapabilities() for capability
-                    rootView.dispatchRequestTranslation(viewIds, supportedFormats, /* capability */
-                            null, requests);
+                    rootView.dispatchRequestTranslation(viewIds, supportedFormats, capability,
+                            requests);
                 }
                 mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
                         UiTranslationController::sendTranslationRequest,
@@ -487,6 +488,15 @@
         return new int[] {TranslationSpec.DATA_FORMAT_TEXT};
     }
 
+    private TranslationCapability getTranslationCapability(TranslationContext translationContext) {
+        // We only support text to text capability now, we will query real status from service when
+        // we support more translation capabilities.
+        return new TranslationCapability(TranslationCapability.STATE_ON_DEVICE,
+                translationContext.getSourceSpec(),
+                translationContext.getTargetSpec(), /* uiTranslationEnabled= */ true,
+                /* supportedTranslationFlags= */ 0);
+    }
+
     private void findViewsTraversalByAutofillIds(IntArray sourceViewIds) {
         final ArrayList<ViewRootImpl> roots =
                 WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index c7eac6c..6b49569 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -226,6 +227,7 @@
      * WebViewChromiumFactoryProvider#create method was invoked.
      */
     @NonNull
+    @ElapsedRealtimeLong
     public long[] getTimestamps() {
         return WebViewFactory.getTimestamps();
     }
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index bc2b221..5fc5b29 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -83,16 +83,27 @@
     public @interface Timestamp {
     }
 
+    /** When the overall WebView provider load began. */
     public static final int WEBVIEW_LOAD_START = 0;
+    /** Before creating the WebView APK Context. */
     public static final int CREATE_CONTEXT_START = 1;
+    /** After creating the WebView APK Context. */
     public static final int CREATE_CONTEXT_END = 2;
+    /** Before adding WebView assets to AssetManager. */
     public static final int ADD_ASSETS_START = 3;
+    /** After adding WebView assets to AssetManager. */
     public static final int ADD_ASSETS_END = 4;
+    /** Before creating the WebView ClassLoader. */
     public static final int GET_CLASS_LOADER_START = 5;
+    /** After creating the WebView ClassLoader. */
     public static final int GET_CLASS_LOADER_END = 6;
+    /** Before preloading the WebView native library. */
     public static final int NATIVE_LOAD_START = 7;
+    /** After preloading the WebView native library. */
     public static final int NATIVE_LOAD_END = 8;
+    /** Before looking up the WebView provider class. */
     public static final int PROVIDER_CLASS_FOR_NAME_START = 9;
+    /** After looking up the WebView provider class. */
     public static final int PROVIDER_CLASS_FOR_NAME_END = 10;
     private static final int TIMESTAMPS_SIZE = 11;
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 166411e..e06d5f0 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -259,6 +259,20 @@
     // Used to highlight a word when it is corrected by the IME
     private CorrectionHighlighter mCorrectionHighlighter;
 
+    /**
+     * {@code true} when {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)}
+     * is being executed and {@link InputMethodManager#restartInput(View)} is scheduled to be
+     * called.
+     *
+     * <p>This is also used to avoid an unnecessary invocation of
+     * {@link InputMethodManager#updateSelection(View, int, int, int, int)} when
+     * {@link InputMethodManager#restartInput(View)} is scheduled to be called already
+     * See bug 186582769 for details.</p>
+     *
+     * <p>TODO(186582769): Come up with better way.</p>
+     */
+    private boolean mHasPendingRestartInputForSetText = false;
+
     InputContentType mInputContentType;
     InputMethodState mInputMethodState;
 
@@ -1825,6 +1839,29 @@
         }
     }
 
+    /**
+     * Called from {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)} to
+     * schedule {@link InputMethodManager#restartInput(View)}.
+     */
+    void scheduleRestartInputForSetText() {
+        mHasPendingRestartInputForSetText = true;
+    }
+
+    /**
+     * Called from {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)} to
+     * actually call {@link InputMethodManager#restartInput(View)} if it's scheduled.  Does nothing
+     * otherwise.
+     */
+    void maybeFireScheduledRestartInputForSetText() {
+        if (mHasPendingRestartInputForSetText) {
+            final InputMethodManager imm = getInputMethodManager();
+            if (imm != null) {
+                imm.restartInput(mTextView);
+            }
+            mHasPendingRestartInputForSetText = false;
+        }
+    }
+
     static final int EXTRACT_NOTHING = -2;
     static final int EXTRACT_UNKNOWN = -1;
 
@@ -1958,7 +1995,8 @@
     }
 
     private void sendUpdateSelection() {
-        if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0) {
+        if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0
+                && !mHasPendingRestartInputForSetText) {
             final InputMethodManager imm = getInputMethodManager();
             if (null != imm) {
                 final int selectionStart = mTextView.getSelectionStart();
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 105c714..b5a5848 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -706,8 +706,7 @@
                 if (getChildCount() == 0) {
                     return false;
                 }
-                if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowRight.isFinished()
-                        || !mEdgeGlowLeft.isFinished())) {
+                if (!mScroller.isFinished()) {
                     final ViewParent parent = getParent();
                     if (parent != null) {
                         parent.requestDisallowInterceptTouchEvent(true);
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 65f3da7..2dd7f02 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -763,8 +763,7 @@
                 if (getChildCount() == 0) {
                     return false;
                 }
-                if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowTop.isFinished()
-                        || !mEdgeGlowBottom.isFinished())) {
+                if (!mScroller.isFinished()) {
                     final ViewParent parent = getParent();
                     if (parent != null) {
                         parent.requestDisallowInterceptTouchEvent(true);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 07a9a5fa..1a37b59 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6285,11 +6285,10 @@
                 || needEditableForNotification) {
             createEditorIfNeeded();
             mEditor.forgetUndoRedo();
+            mEditor.scheduleRestartInputForSetText();
             Editable t = mEditableFactory.newEditable(text);
             text = t;
             setFilters(t, mFilters);
-            InputMethodManager imm = getInputMethodManager();
-            if (imm != null) imm.restartInput(this);
         } else if (precomputed != null) {
             if (mTextDir == null) {
                 mTextDir = getTextDirectionHeuristic();
@@ -6408,8 +6407,12 @@
             notifyListeningManagersAfterTextChanged();
         }
 
-        // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
-        if (mEditor != null) mEditor.prepareCursorControllers();
+        if (mEditor != null) {
+            // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
+            mEditor.prepareCursorControllers();
+
+            mEditor.maybeFireScheduledRestartInputForSetText();
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index 9d06bb9..f2d91ba 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -203,34 +203,33 @@
 
         final InvisibleToggleAllowListingFeatureTarget magnification =
                 new InvisibleToggleAllowListingFeatureTarget(context,
-                shortcutType,
-                isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
-                MAGNIFICATION_CONTROLLER_NAME,
-                context.getString(R.string.accessibility_magnification_chooser_text),
-                context.getDrawable(R.drawable.ic_accessibility_magnification),
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+                        shortcutType,
+                        isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
+                        MAGNIFICATION_CONTROLLER_NAME,
+                        context.getString(R.string.accessibility_magnification_chooser_text),
+                        context.getDrawable(R.drawable.ic_accessibility_magnification),
+                        Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
 
         final ToggleAllowListingFeatureTarget daltonizer =
                 new ToggleAllowListingFeatureTarget(context,
-                shortcutType,
-                isShortcutContained(context, shortcutType,
-                        DALTONIZER_COMPONENT_NAME.flattenToString()),
-                DALTONIZER_COMPONENT_NAME.flattenToString(),
-                context.getString(R.string.color_correction_feature_name),
-                context.getDrawable(R.drawable.ic_accessibility_color_correction),
-                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
+                        shortcutType,
+                        isShortcutContained(context, shortcutType,
+                                DALTONIZER_COMPONENT_NAME.flattenToString()),
+                        DALTONIZER_COMPONENT_NAME.flattenToString(),
+                        context.getString(R.string.color_correction_feature_name),
+                        context.getDrawable(R.drawable.ic_accessibility_color_correction),
+                        Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
 
         final ToggleAllowListingFeatureTarget colorInversion =
                 new ToggleAllowListingFeatureTarget(context,
-                shortcutType,
-                isShortcutContained(context, shortcutType,
-                        COLOR_INVERSION_COMPONENT_NAME.flattenToString()),
-                COLOR_INVERSION_COMPONENT_NAME.flattenToString(),
-                context.getString(R.string.color_inversion_feature_name),
-                context.getDrawable(R.drawable.ic_accessibility_color_inversion),
-                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+                        shortcutType,
+                        isShortcutContained(context, shortcutType,
+                                COLOR_INVERSION_COMPONENT_NAME.flattenToString()),
+                        COLOR_INVERSION_COMPONENT_NAME.flattenToString(),
+                        context.getString(R.string.color_inversion_feature_name),
+                        context.getDrawable(R.drawable.ic_accessibility_color_inversion),
+                        Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
 
-        // TODO: Update with shortcut icon
         final ToggleAllowListingFeatureTarget reduceBrightColors =
                 new ToggleAllowListingFeatureTarget(context,
                         shortcutType,
@@ -238,7 +237,7 @@
                                 REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString()),
                         REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString(),
                         context.getString(R.string.reduce_bright_colors_feature_name),
-                        null,
+                        context.getDrawable(R.drawable.ic_accessibility_reduce_bright_colors),
                         Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED);
 
         targets.add(magnification);
diff --git a/core/java/com/android/internal/graphics/OWNERS b/core/java/com/android/internal/graphics/OWNERS
new file mode 100644
index 0000000..5851cbb
--- /dev/null
+++ b/core/java/com/android/internal/graphics/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/OWNERS
\ No newline at end of file
diff --git a/core/java/com/android/internal/graphics/palette/LABCentroid.java b/core/java/com/android/internal/graphics/palette/LABCentroid.java
index 98d5d26..408cf1f 100644
--- a/core/java/com/android/internal/graphics/palette/LABCentroid.java
+++ b/core/java/com/android/internal/graphics/palette/LABCentroid.java
@@ -62,6 +62,6 @@
         double dL = a[0] - b[0];
         double dA = a[1] - b[1];
         double dB = a[2] - b[2];
-        return (float) (Math.pow(dL, 2) + Math.pow(dA, 2) + Math.pow(dB, 2));
+        return (float) (dL * dL + dA * dA + dB * dB);
     }
 }
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index fcb5d3b..11df5a8 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -39,11 +39,10 @@
             in IVoidResultCallback resultCallback);
     void hideMySoftInput(int flags, in IVoidResultCallback resultCallback);
     void showMySoftInput(int flags, in IVoidResultCallback resultCallback);
-    void updateStatusIcon(String packageName, int iconId, in IVoidResultCallback resultCallback);
+    void updateStatusIconAsync(String packageName, int iconId);
     void switchToPreviousInputMethod(in IBooleanResultCallback resultCallback);
     void switchToNextInputMethod(boolean onlyCurrentIme, in IBooleanResultCallback resultCallback);
     void shouldOfferSwitchingToNextInputMethod(in IBooleanResultCallback resultCallback);
-    void notifyUserAction(in IVoidResultCallback resultCallback);
-    void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible,
-            in IVoidResultCallback resultCallback);
+    void notifyUserActionAsync();
+    void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible);
 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 1691e13f..ed1fe1a 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -187,22 +187,19 @@
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#updateStatusIcon(String, int,
-     * IVoidResultCallback)}.
+     * Calls {@link IInputMethodPrivilegedOperations#updateStatusIconAsync(String, int)}.
      *
      * @param packageName package name from which the status icon should be loaded
      * @param iconResId resource ID of the icon to be loaded
      */
     @AnyThread
-    public void updateStatusIcon(String packageName, @DrawableRes int iconResId) {
+    public void updateStatusIconAsync(String packageName, @DrawableRes int iconResId) {
         final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
         if (ops == null) {
             return;
         }
         try {
-            final Completable.Void value = Completable.createVoid();
-            ops.updateStatusIcon(packageName, iconResId, ResultCallbacks.of(value));
-            Completable.getResult(value);
+            ops.updateStatusIconAsync(packageName, iconResId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -362,26 +359,23 @@
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#notifyUserAction(IVoidResultCallback)}
+     * Calls {@link IInputMethodPrivilegedOperations#notifyUserActionAsync()}
      */
     @AnyThread
-    public void notifyUserAction() {
+    public void notifyUserActionAsync() {
         final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
         if (ops == null) {
             return;
         }
         try {
-            final Completable.Void value = Completable.createVoid();
-            ops.notifyUserAction(ResultCallbacks.of(value));
-            Completable.getResult(value);
+            ops.notifyUserActionAsync();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean,
-     * IVoidResultCallback)}.
+     * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibilityAsync(IBinder, boolean)}.
      *
      * @param showOrHideInputToken placeholder token that maps to window requesting
      *        {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or
@@ -390,15 +384,13 @@
      * @param setVisible {@code true} to set IME visible, else hidden.
      */
     @AnyThread
-    public void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible) {
+    public void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible) {
         final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
         if (ops == null) {
             return;
         }
         try {
-            final Completable.Void value = Completable.createVoid();
-            ops.applyImeVisibility(showOrHideInputToken, setVisible, ResultCallbacks.of(value));
-            Completable.getResult(value);
+            ops.applyImeVisibilityAsync(showOrHideInputToken, setVisible);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 7ecf69b..1004e17 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -16,18 +16,25 @@
 
 package com.android.internal.os;
 
+import static com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IntArray;
+import android.util.KeyValueListParser;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -157,15 +164,20 @@
             return new Handler(Looper.getMainLooper());
         }
 
-        public BinderLatencyObserver getLatencyObserver() {
-            return new BinderLatencyObserver(new BinderLatencyObserver.Injector());
+        /** Create a latency observer for the specified process. */
+        public BinderLatencyObserver getLatencyObserver(int processSource) {
+            return new BinderLatencyObserver(new BinderLatencyObserver.Injector(), processSource);
         }
     }
 
     public BinderCallsStats(Injector injector) {
+        this(injector, SYSTEM_SERVER);
+    }
+
+    public BinderCallsStats(Injector injector, int processSource) {
         this.mRandom = injector.getRandomGenerator();
         this.mCallStatsObserverHandler = injector.getHandler();
-        this.mLatencyObserver = injector.getLatencyObserver();
+        this.mLatencyObserver = injector.getLatencyObserver(processSource);
     }
 
     public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
@@ -1074,4 +1086,120 @@
                 ? result
                 : Integer.compare(a.transactionCode, b.transactionCode);
     }
+
+
+    /**
+     * Settings observer for other processes (not system_server).
+     *
+     * We do not want to collect cpu data from other processes so only latency collection should be
+     * possible to enable.
+     */
+    public static class SettingsObserver extends ContentObserver {
+        // Settings for BinderCallsStats.
+        public static final String SETTINGS_ENABLED_KEY = "enabled";
+        public static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
+        public static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
+        public static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+        public static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state";
+        public static final String SETTINGS_TRACK_DIRECT_CALLING_UID_KEY = "track_calling_uid";
+        public static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count";
+        public static final String SETTINGS_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status";
+        // Settings for BinderLatencyObserver.
+        public static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_Latency_data";
+        public static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY =
+                "latency_observer_sampling_interval";
+        public static final String SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY =
+                "latency_observer_push_interval_minutes";
+        public static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY =
+                "latency_histogram_bucket_count";
+        public static final String SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY =
+                "latency_histogram_first_bucket_size";
+        public static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY =
+                "latency_histogram_bucket_scale_factor";
+
+        private boolean mEnabled;
+        private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
+        private final Context mContext;
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+        private final BinderCallsStats mBinderCallsStats;
+        private final int mProcessSource;
+
+        public SettingsObserver(Context context, BinderCallsStats binderCallsStats,
+                    int processSource, int userHandle) {
+            super(BackgroundThread.getHandler());
+            mContext = context;
+            context.getContentResolver().registerContentObserver(mUri, false, this,
+                    userHandle);
+            mBinderCallsStats = binderCallsStats;
+            mProcessSource = processSource;
+            // Always kick once to ensure that we match current state
+            onChange();
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            if (mUri.equals(uri)) {
+                onChange();
+            }
+        }
+
+        void onChange() {
+            try {
+                mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
+                        Settings.Global.BINDER_CALLS_STATS));
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Bad binder call stats settings", e);
+            }
+
+            // Cpu data collection should always be disabled for other processes.
+            mBinderCallsStats.setDetailedTracking(false);
+            mBinderCallsStats.setTrackScreenInteractive(false);
+            mBinderCallsStats.setTrackDirectCallerUid(false);
+
+            mBinderCallsStats.setIgnoreBatteryStatus(
+                    mParser.getBoolean(SETTINGS_IGNORE_BATTERY_STATUS_KEY,
+                            BinderCallsStats.DEFAULT_IGNORE_BATTERY_STATUS));
+            mBinderCallsStats.setCollectLatencyData(
+                    mParser.getBoolean(SETTINGS_COLLECT_LATENCY_DATA_KEY,
+                            BinderCallsStats.DEFAULT_COLLECT_LATENCY_DATA));
+
+            // Binder latency observer settings.
+            configureLatencyObserver(mParser, mBinderCallsStats.getLatencyObserver());
+
+            final boolean enabled =
+                    mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
+            if (mEnabled != enabled) {
+                if (enabled) {
+                    Binder.setObserver(mBinderCallsStats);
+                } else {
+                    Binder.setObserver(null);
+                }
+                mEnabled = enabled;
+                mBinderCallsStats.reset();
+                mBinderCallsStats.setAddDebugEntries(enabled);
+                mBinderCallsStats.getLatencyObserver().reset();
+            }
+        }
+
+        /** Configures the binder latency observer related settings. */
+        public static void configureLatencyObserver(
+                    KeyValueListParser mParser, BinderLatencyObserver binderLatencyObserver) {
+            binderLatencyObserver.setSamplingInterval(mParser.getInt(
+                    SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY,
+                    BinderLatencyObserver.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
+            binderLatencyObserver.setHistogramBucketsParams(
+                    mParser.getInt(
+                            SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY,
+                            BinderLatencyObserver.BUCKET_COUNT_DEFAULT),
+                    mParser.getInt(
+                            SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY,
+                            BinderLatencyObserver.FIRST_BUCKET_SIZE_DEFAULT),
+                    mParser.getFloat(
+                            SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY,
+                            BinderLatencyObserver.BUCKET_SCALE_FACTOR_DEFAULT));
+            binderLatencyObserver.setPushInterval(mParser.getInt(
+                    SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY,
+                    BinderLatencyObserver.STATSD_PUSH_INTERVAL_MINUTES_DEFAULT));
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java
index 0079801..5fa96bd 100644
--- a/core/java/com/android/internal/os/BinderLatencyObserver.java
+++ b/core/java/com/android/internal/os/BinderLatencyObserver.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.os;
 
-import static com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER;
-
 import android.annotation.Nullable;
 import android.os.Binder;
 import android.os.Handler;
@@ -68,9 +66,10 @@
     private int mStatsdPushIntervalMinutes = STATSD_PUSH_INTERVAL_MINUTES_DEFAULT;
 
     private final Random mRandom;
-    private BinderLatencyBuckets mLatencyBuckets;
-
     private final Handler mLatencyObserverHandler;
+    private final int mProcessSource;
+
+    private BinderLatencyBuckets mLatencyBuckets;
 
     private Runnable mLatencyObserverRunnable = new Runnable() {
         @Override
@@ -134,7 +133,7 @@
 
         // Write the dims.
         long dimsToken = proto.start(ApiStats.DIMS);
-        proto.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER);
+        proto.write(Dims.PROCESS_SOURCE, mProcessSource);
         proto.write(Dims.SERVICE_CLASS_NAME, dims.getBinderClass().getName());
         proto.write(Dims.SERVICE_METHOD_NAME, transactionName);
         proto.end(dimsToken);
@@ -180,11 +179,12 @@
         }
     }
 
-    public BinderLatencyObserver(Injector injector) {
+    public BinderLatencyObserver(Injector injector, int processSource) {
         mRandom = injector.getRandomGenerator();
         mLatencyObserverHandler = injector.getHandler();
         mLatencyBuckets = new BinderLatencyBuckets(
             mBucketCount, mFirstBucketSize, mBucketScaleFactor);
+        mProcessSource = processSource;
         noteLatencyDelayed();
     }
 
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 04528e9..c32951a 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -16,6 +16,8 @@
 
 #define ATRACE_TAG ATRACE_TAG_RESOURCES
 
+#include <mutex>
+
 #include "signal.h"
 
 #include "android-base/logging.h"
@@ -26,6 +28,7 @@
 #include "utils/misc.h"
 #include "utils/Trace.h"
 
+#include "android_content_res_ApkAssets.h"
 #include "android_util_AssetManager_private.h"
 #include "core_jni_helpers.h"
 #include "jni.h"
@@ -72,6 +75,19 @@
   FORMAT_DIRECTORY = 3,
 };
 
+Guarded<std::unique_ptr<const ApkAssets>>& ApkAssetsFromLong(jlong ptr) {
+    return *reinterpret_cast<Guarded<std::unique_ptr<const ApkAssets>>*>(ptr);
+}
+
+static jlong CreateGuardedApkAssets(std::unique_ptr<const ApkAssets> assets) {
+    auto guarded_assets = new Guarded<std::unique_ptr<const ApkAssets>>(std::move(assets));
+    return reinterpret_cast<jlong>(guarded_assets);
+}
+
+static void DeleteGuardedApkAssets(Guarded<std::unique_ptr<const ApkAssets>>& apk_assets) {
+    delete &apk_assets;
+}
+
 class LoaderAssetsProvider : public AssetsProvider {
  public:
   static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
@@ -227,7 +243,7 @@
     jniThrowException(env, "java/io/IOException", error_msg.c_str());
     return 0;
   }
-  return reinterpret_cast<jlong>(apk_assets.release());
+  return CreateGuardedApkAssets(std::move(apk_assets));
 }
 
 static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -279,7 +295,7 @@
     jniThrowException(env, "java/io/IOException", error_msg.c_str());
     return 0;
   }
-  return reinterpret_cast<jlong>(apk_assets.release());
+  return CreateGuardedApkAssets(std::move(apk_assets));
 }
 
 static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -347,12 +363,12 @@
     jniThrowException(env, "java/io/IOException", error_msg.c_str());
     return 0;
   }
-  return reinterpret_cast<jlong>(apk_assets.release());
+  return CreateGuardedApkAssets(std::move(apk_assets));
 }
 
 static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
   auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags);
-  return reinterpret_cast<jlong>(apk_assets.release());
+  return CreateGuardedApkAssets(std::move(apk_assets));
 }
 
 // STOPSHIP (b/159041693): Revert signal handler when reason for issue is found.
@@ -383,54 +399,60 @@
 }
 
 static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
-  auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
-  destroy_info << "{ptr=" << apk_assets;
-  if (apk_assets != nullptr) {
-    destroy_info << ", name='" << apk_assets->GetDebugName() << "'"
-                 << ", idmap=" << apk_assets->GetLoadedIdmap()
-                 << ", {arsc=" << apk_assets->GetLoadedArsc();
-    if (auto arsc = apk_assets->GetLoadedArsc()) {
-      destroy_info << ", strings=" << arsc->GetStringPool()
-                   << ", packages=" << &arsc->GetPackages()
-                   << " [";
-      for (auto& package : arsc->GetPackages()) {
-        destroy_info << "{unique_ptr=" << &package
-                     << ", package=" << package.get() << "},";
-      }
-      destroy_info << "]";
+    {
+        auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+        auto apk_assets = scoped_apk_assets->get();
+        destroy_info << "{ptr=" << apk_assets;
+        if (apk_assets != nullptr) {
+            destroy_info << ", name='" << apk_assets->GetDebugName() << "'"
+                         << ", idmap=" << apk_assets->GetLoadedIdmap()
+                         << ", {arsc=" << apk_assets->GetLoadedArsc();
+            if (auto arsc = apk_assets->GetLoadedArsc()) {
+                destroy_info << ", strings=" << arsc->GetStringPool()
+                             << ", packages=" << &arsc->GetPackages() << " [";
+                for (auto& package : arsc->GetPackages()) {
+                    destroy_info << "{unique_ptr=" << &package << ", package=" << package.get()
+                                 << "},";
+                }
+                destroy_info << "]";
+            }
+            destroy_info << "}";
+        }
+        destroy_info << "}";
     }
-    destroy_info << "}";
-  }
-  destroy_info << "}";
 
-  delete apk_assets;
+    DeleteGuardedApkAssets(ApkAssetsFromLong(ptr));
 
-  // Deleting the apk assets did not lead to a crash.
-  destroy_info.str("");
-  destroy_info.clear();
+    // Deleting the apk assets did not lead to a crash.
+    destroy_info.str("");
+    destroy_info.clear();
 }
 
 static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
-  auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
-  if (auto path = apk_assets->GetPath()) {
-    return env->NewStringUTF(path->data());
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
+    if (auto path = apk_assets->GetPath()) {
+        return env->NewStringUTF(path->data());
   }
   return nullptr;
 }
 
 static jstring NativeGetDebugName(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
-  auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
-  return env->NewStringUTF(apk_assets->GetDebugName().c_str());
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
+    return env->NewStringUTF(apk_assets->GetDebugName().c_str());
 }
 
 static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
-  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
-  return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
+    return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
 }
 
 static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
-  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
-  return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
+    return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
 }
 
 static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
@@ -439,7 +461,8 @@
     return 0;
   }
 
-  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+  auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+  auto apk_assets = scoped_apk_assets->get();
   std::unique_ptr<Asset> asset = apk_assets->GetAssetsProvider()->Open(
       path_utf8.c_str(),Asset::AccessMode::ACCESS_RANDOM);
   if (asset == nullptr) {
@@ -467,12 +490,13 @@
 
 static jobject NativeGetOverlayableInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
                                          jstring overlayable_name) {
-  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
 
-  const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
-  if (packages.empty()) {
-    jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
-    return 0;
+    const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
+    if (packages.empty()) {
+        jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
+        return 0;
   }
 
   // TODO(b/119899133): Convert this to a search for the info rather than assuming it's at index 0
@@ -502,13 +526,14 @@
 }
 
 static jboolean NativeDefinesOverlayable(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
-  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
 
-  const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
-  if (packages.empty()) {
-    // Must throw to prevent bypass by returning false
-    jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
-    return 0;
+    const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
+    if (packages.empty()) {
+        // Must throw to prevent bypass by returning false
+        jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
+        return 0;
   }
 
   const auto& overlayable_infos = packages[0]->GetOverlayableMap();
diff --git a/core/jni/android_content_res_ApkAssets.h b/core/jni/android_content_res_ApkAssets.h
new file mode 100644
index 0000000..7e525dc
--- /dev/null
+++ b/core/jni/android_content_res_ApkAssets.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_CONTENT_RES_APKASSETS_H
+#define ANDROID_CONTENT_RES_APKASSETS_H
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/MutexGuard.h"
+
+#include "jni.h"
+
+namespace android {
+
+Guarded<std::unique_ptr<const ApkAssets>>& ApkAssetsFromLong(jlong ptr);
+
+} // namespace android
+
+#endif // ANDROID_CONTENT_RES_APKASSETS_H
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b2c69a0..24f9abf 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -42,6 +42,7 @@
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/ResourceUtils.h"
 
+#include "android_content_res_ApkAssets.h"
 #include "android_util_AssetManager_private.h"
 #include "core_jni_helpers.h"
 #include "jni.h"
@@ -50,9 +51,9 @@
 #include "nativehelper/ScopedStringChars.h"
 #include "nativehelper/ScopedUtfChars.h"
 #include "utils/Log.h"
-#include "utils/misc.h"
 #include "utils/String8.h"
 #include "utils/Trace.h"
+#include "utils/misc.h"
 
 extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
 extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
@@ -307,7 +308,9 @@
     if (env->ExceptionCheck()) {
       return;
     }
-    apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
+
+    auto scoped_assets = ScopedLock(ApkAssetsFromLong(apk_assets_native_ptr));
+    apk_assets.push_back(scoped_assets->get());
   }
 
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
diff --git a/core/jni/include/android_runtime/AndroidRuntime.h b/core/jni/include/android_runtime/AndroidRuntime.h
index d86d934..bf2ba77 100644
--- a/core/jni/include/android_runtime/AndroidRuntime.h
+++ b/core/jni/include/android_runtime/AndroidRuntime.h
@@ -19,7 +19,6 @@
 #ifndef _RUNTIME_ANDROID_RUNTIME_H
 #define _RUNTIME_ANDROID_RUNTIME_H
 
-#include <binder/IBinder.h>
 #include <jni.h>
 #include <pthread.h>
 #include <utils/Errors.h>
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 7d68a0d..4c84944 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -642,7 +642,7 @@
         message ReasonCount {
             option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-            optional android.app.job.StopReasonEnum name = 1;
+            optional android.app.job.InternalStopReasonEnum name = 1;
             optional int32 count = 2;
         }
         repeated ReasonCount reason_count = 2;
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index ae6dcfd..1bba12f 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -430,6 +430,7 @@
         optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto one_handed_mode_activated = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional OneHanded onehanded = 80;
 
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 36d48e2..c4ff49c 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -51,7 +51,7 @@
     message JobRestriction {
         option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-        optional .android.app.job.StopReasonEnum reason = 1;
+        optional .android.app.job.InternalStopReasonEnum reason = 1;
         optional bool is_restricting = 2;
     }
 
@@ -856,7 +856,7 @@
         message StopReasonCount {
             option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-            optional .android.app.job.StopReasonEnum reason = 1;
+            optional .android.app.job.InternalStopReasonEnum reason = 1;
             optional int32 count = 2;
         }
         repeated StopReasonCount stop_reasons = 9;
@@ -907,7 +907,7 @@
         optional int32 job_id = 4;
         optional string tag = 5;
         // Only valid for STOP_JOB or STOP_PERIODIC_JOB Events.
-        optional .android.app.job.StopReasonEnum stop_reason = 6;
+        optional .android.app.job.InternalStopReasonEnum stop_reason = 6;
     }
     repeated HistoryEvent history_event = 1;
 }
diff --git a/core/res/res/drawable/ic_accessibility_color_correction.xml b/core/res/res/drawable/ic_accessibility_color_correction.xml
index ed09d57..f1dbfb2 100644
--- a/core/res/res/drawable/ic_accessibility_color_correction.xml
+++ b/core/res/res/drawable/ic_accessibility_color_correction.xml
@@ -1,5 +1,5 @@
 <!--
-    Copyright (C) 2020 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.
@@ -13,40 +13,12 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<vector android:height="24dp" android:viewportHeight="192"
-    android:viewportWidth="192" android:width="24dp"
-    xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#00BCD4" android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z"/>
-    <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
-        android:pathData="M13.58,69.14L87.15,12c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c3.36,2.68 5.27,6.67 5.41,10.82c0.15,-4.51 -1.79,-8.93 -5.41,-11.82l-73.81,-58.94C99.61,7.01 92.35,6.96 87.15,11L13.58,68.14c-3.71,2.88 -5.72,7.36 -5.56,11.94C8.17,75.85 10.14,71.81 13.58,69.14z" android:strokeAlpha="0.2"/>
-    <path android:fillAlpha="0.2" android:fillColor="#263238"
-        android:pathData="M183.35,84.63l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84c-6.31,0 -11.87,-4.17 -13.7,-10.26L8.61,82.77c-0.36,-1.22 -0.55,-2.46 -0.59,-3.69c-0.06,1.56 0.13,3.14 0.59,4.69l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.37c6.28,0 11.82,-4.13 13.67,-10.19l28.48,-88.18c0.48,-1.57 0.67,-3.17 0.61,-4.75C183.92,82.13 183.73,83.39 183.35,84.63z" android:strokeAlpha="0.2"/>
-    <path android:pathData="M60.01,135L60.01,135H60l48.04,49h33.17c6.28,0 11.82,-4.13 13.67,-10.19l18.68,-57.84L129.51,72.2l-0.01,0c0.27,0.27 0.52,0.5 0.77,0.77l0.55,0.55c1.57,1.56 1.57,4.08 -0.04,5.68l-12.56,12.49l6.36,6.3l1.38,1.37l-5.67,5.64l-5.71,-5.68L79.15,135H60.42H60.01z">
-        <aapt:attr name="android:fillColor">
-            <gradient android:endX="155.9627" android:endY="165.1493"
-                android:startX="98.4649" android:startY="107.6516" android:type="linear">
-                <item android:color="#19263238" android:offset="0"/>
-                <item android:color="#00212121" android:offset="1"/>
-            </gradient>
-        </aapt:attr>
-    </path>
-    <path android:fillColor="#0097A7" android:pathData="M68.55,120.35l32.173,-32.173l7.658,7.658l-32.173,32.173z"/>
-    <path android:fillColor="#FFFFFF" android:pathData="M130.83,73.52l-9.42,-9.36c-1.57,-1.56 -4.1,-1.56 -5.67,0l-12.56,12.48L95.42,69l-5.67,5.64l5.71,5.68L60,116.97V135h19.15l35.42,-35.68l5.71,5.68l5.67,-5.64l-7.73,-7.68l12.56,-12.48C132.4,77.6 132.4,75.08 130.83,73.52zM74.98,126.77l-6.43,-6.43l32.17,-32.17l6.43,6.43L74.98,126.77z"/>
-    <path android:fillAlpha="0.1" android:fillColor="#263238"
-        android:pathData="M120.28,105l-5.71,-5.68l-35.42,35.68l-19.15,0l1,1l19.15,0l35.42,-35.68l5.71,5.68l5.68,-5.64l-1,-1z" android:strokeAlpha="0.1"/>
-    <path android:fillAlpha="0.1" android:fillColor="#263238"
-        android:pathData="M90.75,75.64l-0.01,0l4.71,4.68l0.01,0z" android:strokeAlpha="0.1"/>
-    <path android:fillAlpha="0.1" android:fillColor="#263238"
-        android:pathData="M131.83,74.52l-0.97,-0.97c1.54,1.56 1.53,4.06 -0.07,5.65l-12.56,12.48l1,1l12.55,-12.48C133.4,78.6 133.4,76.08 131.83,74.52z" android:strokeAlpha="0.1"/>
-    <path android:fillAlpha="0.1" android:fillColor="#263238"
-        android:pathData="M101.72,89.17l6.67,6.66l0,0l-7.67,-7.66l-32.17,32.17l1,1z" android:strokeAlpha="0.1"/>
-    <path android:pathData="M37.13,173.7L8.62,83.69c-1.7,-5.8 0.3,-12 5,-15.6l73.52,-57.1c5.2,-4 12.5,-4 17.6,0.1l73.82,58.9c4.6,3.7 6.5,9.9 4.8,15.6l-28.51,88.21c-1.8,6.1 -7.4,10.2 -13.7,10.2H50.83C44.53,184 38.93,179.8 37.13,173.7z">
-        <aapt:attr name="android:fillColor">
-            <gradient android:centerX="21.977" android:centerY="23.8809"
-                android:gradientRadius="158.0384" android:type="radial">
-                <item android:color="#19FFFFFF" android:offset="0"/>
-                <item android:color="#00FFFFFF" android:offset="1"/>
-            </gradient>
-        </aapt:attr>
-    </path>
-</vector>
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/accessibility_daltonizer_background" />
+    <foreground>
+        <inset
+            android:drawable="@drawable/ic_accessibility_color_correction_foreground"
+            android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+    </foreground>
+</adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_color_correction_foreground.xml b/core/res/res/drawable/ic_accessibility_color_correction_foreground.xml
new file mode 100644
index 0000000..ecb5065
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_color_correction_foreground.xml
@@ -0,0 +1,26 @@
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"
+        android:pathData="M14.8417,6.6662C14.9562,6.5573 15.0995,6.5 15.2484,6.5C15.3917,6.5 15.5349,6.5573 15.6495,6.6662L16.9901,8.0068C17.2135,8.2302 17.2135,8.5912 16.9844,8.8203L15.1969,10.6078L16.2969,11.7078L15.4891,12.5156L14.6755,11.7021L9.5651,16.8125H6.8438V14.0911L11.9542,8.9807L11.1406,8.1672L11.9484,7.3594L13.0542,8.4537L14.8417,6.6662ZM7.9896,15.6667H9.0896L13.7073,11.049L12.6073,9.949L7.9896,14.5667V15.6667Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_accessibility_color_inversion.xml b/core/res/res/drawable/ic_accessibility_color_inversion.xml
index d69a169..f855880 100644
--- a/core/res/res/drawable/ic_accessibility_color_inversion.xml
+++ b/core/res/res/drawable/ic_accessibility_color_inversion.xml
@@ -1,5 +1,5 @@
 <!--
-    Copyright (C) 2020 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.
@@ -13,50 +13,12 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<vector android:height="24dp" android:viewportHeight="192"
-    android:viewportWidth="192" android:width="24dp"
-    xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#546E7A" android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z"/>
-    <path android:fillAlpha="0.2" android:fillColor="#263238"
-        android:pathData="M183.37,84.63l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84c-6.31,0 -11.87,-4.17 -13.7,-10.26L8.61,82.77c-0.36,-1.22 -0.55,-2.46 -0.59,-3.69c-0.06,1.56 0.13,3.14 0.59,4.69l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.38c6.28,0 11.82,-4.13 13.67,-10.19l28.48,-88.18c0.48,-1.57 0.67,-3.17 0.61,-4.75C183.94,82.13 183.74,83.39 183.37,84.63z" android:strokeAlpha="0.2"/>
-    <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
-        android:pathData="M13.58,69.14L87.15,12c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c3.36,2.68 5.27,6.67 5.41,10.82c0.15,-4.51 -1.79,-8.93 -5.41,-11.82l-73.81,-58.94C99.61,7.01 92.35,6.96 87.15,11L13.58,68.14c-3.71,2.88 -5.72,7.36 -5.56,11.94C8.17,75.85 10.14,71.81 13.58,69.14z" android:strokeAlpha="0.2"/>
-    <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
-        android:pathData="M53,130.05l5.03,-4.79L44.16,112c-0.05,0.78 -0.14,1.52 -0.15,2.34C43.62,136.61 61.27,154.56 84,156v-5.31C70.13,149.64 58.56,141.54 53,130.05z" android:strokeAlpha="0.2"/>
-    <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
-        android:pathData="M109,52v5.31c13.65,1.05 24.67,9.15 30.11,20.64l-4.92,4.79L147.81,96c0.09,-0.78 0.17,-1.53 0.19,-2.34C148.38,71.39 131.36,53.44 109,52z" android:strokeAlpha="0.2"/>
-    <path android:pathData="M154.89,173.81l13.57,-42.02l-46.53,-46.56C125.75,90.5 128,96.98 128,104c0,17.7 -14.3,32 -32,32c-8.64,0 -16.47,-3.42 -22.22,-8.97l0.9,0.91L130.73,184h10.49C147.5,184 153.04,179.87 154.89,173.81z">
-        <aapt:attr name="android:fillColor">
-            <gradient android:endX="153.3523" android:endY="161.6371"
-                android:startX="90.6075" android:startY="98.8923" android:type="linear">
-                <item android:color="#33263238" android:offset="0"/>
-                <item android:color="#05263238" android:offset="1"/>
-            </gradient>
-        </aapt:attr>
-    </path>
-    <path android:pathData="M96,129.6V78.4c-14.11,0 -25.6,11.49 -25.6,25.6S81.89,129.6 96,129.6z">
-        <aapt:attr name="android:fillColor">
-            <gradient android:endX="150.8492" android:endY="164.1401"
-                android:startX="88.1044" android:startY="101.3954" android:type="linear">
-                <item android:color="#33263238" android:offset="0"/>
-                <item android:color="#05263238" android:offset="1"/>
-            </gradient>
-        </aapt:attr>
-    </path>
-    <path android:fillAlpha="0.2" android:fillColor="#263238"
-        android:pathData="M96,136c-17.53,0 -31.72,-14.04 -31.99,-31.5c0,0.17 -0.01,0.33 -0.01,0.5c0,17.7 14.3,32 32,32s32,-14.3 32,-32c0,-0.17 -0.01,-0.33 -0.01,-0.5C127.72,121.96 113.53,136 96,136z" android:strokeAlpha="0.2"/>
-    <path android:fillAlpha="0.2" android:fillColor="#263238"
-        android:pathData="M70.4,104c0,0.17 0.01,0.33 0.01,0.5C70.68,90.62 82.06,79.4 96,79.4v-1C81.89,78.4 70.4,89.88 70.4,104z" android:strokeAlpha="0.2"/>
-    <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
-        android:pathData="M96,72c-17.7,0 -32,14.3 -32,32s14.3,32 32,32s32,-14.3 32,-32S113.7,72 96,72zM70.4,104c0,-14.11 11.49,-25.6 25.6,-25.6v51.2C81.89,129.6 70.4,118.11 70.4,104z" android:strokeAlpha="0.2"/>
-    <path android:fillColor="#FFFFFF" android:pathData="M96,72c-17.7,0 -32,14.3 -32,32s14.3,32 32,32s32,-14.3 32,-32S113.7,72 96,72zM70.4,104c0,-14.11 11.49,-25.6 25.6,-25.6v51.2C81.89,129.6 70.4,118.11 70.4,104z"/>
-    <path android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z">
-        <aapt:attr name="android:fillColor">
-            <gradient android:endX="156.2451" android:endY="171.4516"
-                android:startX="37.0633" android:startY="52.269802" android:type="linear">
-                <item android:color="#19FFFFFF" android:offset="0"/>
-                <item android:color="#00FFFFFF" android:offset="1"/>
-            </gradient>
-        </aapt:attr>
-    </path>
-</vector>
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/accessibility_color_inversion_background" />
+    <foreground>
+        <inset
+            android:drawable="@drawable/ic_accessibility_color_inversion_foreground"
+            android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+    </foreground>
+</adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_color_inversion_foreground.xml b/core/res/res/drawable/ic_accessibility_color_inversion_foreground.xml
new file mode 100644
index 0000000..7fc3aa6
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_color_inversion_foreground.xml
@@ -0,0 +1,54 @@
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="192"
+    android:viewportHeight="192">
+    <path
+        android:fillAlpha="0.2"
+        android:fillColor="#212121"
+        android:pathData="M96,183L96,183c-48.23,0 -87.72,-39.33 -87.99,-87.5C8.01,95.67 8,95.83 8,96v0c0,48.4 39.6,88 88,88h0c48.4,0 88,-39.6 88,-88v0c0,-0.17 -0.01,-0.33 -0.01,-0.5C183.72,143.67 144.23,183 96,183z" />
+    <path
+        android:fillAlpha="0.2"
+        android:fillColor="#FFFFFF"
+        android:pathData="M184,96c0,-48.4 -39.6,-88 -88,-88h0C47.6,8 8,47.6 8,96v0c0,0.17 0.01,0.33 0.01,0.5C8.28,48.33 47.77,9 96,9h0c48.23,0 87.72,39.33 87.99,87.5C183.99,96.33 184,96.17 184,96L184,96z"
+        android:strokeAlpha="0.2" />
+    <path
+        android:fillAlpha="0.2"
+        android:fillColor="#FFFFFF"
+        android:pathData="M53,122.2l5.03,-4.79l-13.88,-13.26c-0.05,0.78 -0.14,1.52 -0.15,2.34c-0.39,22.27 17.27,40.22 39.99,41.66v-5.31C70.13,141.78 58.56,133.69 53,122.2z"
+        android:strokeAlpha="0.2" />
+    <path
+        android:fillAlpha="0.2"
+        android:fillColor="#FFFFFF"
+        android:pathData="M109,44.15v5.31c13.65,1.05 24.67,9.15 30.11,20.64l-4.92,4.79l13.62,13.26c0.09,-0.78 0.17,-1.53 0.19,-2.34C148.38,63.53 131.36,45.59 109,44.15z"
+        android:strokeAlpha="0.2" />
+    <path
+        android:fillAlpha="0.2"
+        android:fillColor="#263238"
+        android:pathData="M70.4,96.18c0,0.17 0.01,0.33 0.01,0.5c0.27,-13.88 11.64,-25.1 25.59,-25.1v-1C81.89,70.57 70.4,82.06 70.4,96.18z"
+        android:strokeAlpha="0.2" />
+    <path
+        android:fillAlpha="0.2"
+        android:fillColor="#263238"
+        android:pathData="M128,96.67c-0.27,17.47 -14.46,31.5 -31.99,31.5c-8.79,0 -16.75,-3.53 -22.53,-9.26l-0.04,0l5.03,5.04c5.03,3.3 11.05,5.22 17.53,5.22c17.7,0 32,-14.31 32,-32C128.01,97.01 128,96.84 128,96.67z"
+        android:strokeAlpha="0.2" />
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M96,64.16c-17.7,0 -32,14.3 -32,32c0,17.7 14.3,32 32,32c17.7,0 32,-14.3 32,-32C128,78.47 113.7,64.16 96,64.16zM70.4,96.16c0,-14.11 11.49,-25.6 25.6,-25.6v51.2C81.89,121.77 70.4,110.28 70.4,96.16z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_accessibility_magnification.xml b/core/res/res/drawable/ic_accessibility_magnification.xml
index 5dab479..f3b2887 100644
--- a/core/res/res/drawable/ic_accessibility_magnification.xml
+++ b/core/res/res/drawable/ic_accessibility_magnification.xml
@@ -1,5 +1,5 @@
 <!--
-    Copyright (C) 2020 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.
@@ -13,102 +13,13 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="192"
-    android:viewportHeight="192">
-  <path
-      android:pathData="M96,104m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
-      android:fillColor="#F50057"/>
-  <path
-      android:pathData="M178.57,70.06l-73.81,-58.94C99.6,7.01 92.35,6.96 87.15,11L13.59,68.14C8.87,71.8 6.9,78.02 8.61,83.77l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.38c6.28,0 11.82,-4.13 13.67,-10.19l0.69,-2.13l-34.94,-34.88v-4.7l-0.96,-0.99c-6.33,5.54 -14.61,8.9 -23.68,8.9c-19.89,0 -36.02,-16.12 -36.02,-36.01S76.11,68 96,68s36.01,16.12 36.01,36.01c0,8.68 -3.08,16.65 -8.2,22.87l1.05,1.01h4.7l30.34,30.39l23.47,-72.65C185.1,79.94 183.2,73.76 178.57,70.06z"
-      android:fillColor="#F50057"/>
-  <path
-      android:pathData="M65.25,73c0,0 -16.75,31.96 -9.25,45.1s21.02,29.15 40.01,32.65s32.99,10.5 39.99,14s19.58,6.93 19.58,6.93s-5.34,-9.49 4.32,-13.4c0.73,-3.53 -11.9,-42.35 -11.9,-42.35L127.92,73L81.79,62.25L65.25,73z"
-      android:fillColor="#F50057"/>
-  <path
-      android:pathData="M155.33,171.43l-0.44,1.37c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84c-6.31,0 -11.87,-4.17 -13.7,-10.26L8.61,82.77c-0.36,-1.22 -0.55,-2.46 -0.59,-3.69c-0.06,1.56 0.13,3.14 0.59,4.69l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.38c6.28,0 11.82,-4.13 13.67,-10.19l0.69,-2.13L155.33,171.43z"
-      android:strokeAlpha="0.2"
-      android:fillColor="#3E2723"
-      android:fillAlpha="0.2"/>
-  <path
-      android:pathData="M183.37,84.63l-23.71,73.41l0.24,0.24l23.47,-72.66c0.48,-1.57 0.67,-3.17 0.61,-4.75C183.94,82.13 183.74,83.39 183.37,84.63z"
-      android:strokeAlpha="0.2"
-      android:fillColor="#3E2723"
-      android:fillAlpha="0.2"/>
-  <path
-      android:pathData="M155.57,171.67l-34.93,-34.87v-4.7l-0.96,-0.99c-6.33,5.54 -14.61,8.9 -23.68,8.9c-9.81,0 -18.71,-3.93 -25.2,-10.29L125.07,184h16.14c6.28,0 11.81,-4.13 13.67,-10.19L155.57,171.67z">
-    <aapt:attr name="android:fillColor">
-      <gradient
-          android:startY="104.215"
-          android:startX="69.035"
-          android:endY="173.8946"
-          android:endX="138.7146"
-          android:type="linear">
-        <item android:offset="0" android:color="#333E2723"/>
-        <item android:offset="1" android:color="#003E2723"/>
-      </gradient>
-    </aapt:attr>
-  </path>
-  <path
-      android:pathData="M132.01,104.01c0,8.68 -3.08,16.65 -8.2,22.87l1.05,1.01h4.7l30.34,30.39L170,127l-49,-49.03l-0.19,-0.04C127.71,84.49 132.01,93.74 132.01,104.01z">
-    <aapt:attr name="android:fillColor">
-      <gradient
-          android:startY="83.635"
-          android:startX="103.615"
-          android:endY="137.0219"
-          android:endX="157.0018"
-          android:type="linear">
-        <item android:offset="0" android:color="#333E2723"/>
-        <item android:offset="1" android:color="#003E2723"/>
-      </gradient>
-    </aapt:attr>
-  </path>
-  <path
-      android:pathData="M124.27,127.32c4.85,-6.13 7.75,-13.88 7.75,-22.3c0,-0.16 -0.01,-0.31 -0.01,-0.47c-0.12,8.47 -3.17,16.24 -8.19,22.34L124.27,127.32z"
-      android:strokeAlpha="0.2"
-      android:fillColor="#3E2723"
-      android:fillAlpha="0.2"/>
-  <path
-      android:pathData="M96.01,80.01c-13.25,0 -24,10.75 -24,24c0,0.17 0.01,0.33 0.01,0.5c0.27,-13.02 10.91,-23.5 23.99,-23.5s23.72,10.48 23.99,23.5c0,-0.17 0.01,-0.33 0.01,-0.5C120.01,90.76 109.26,80.01 96.01,80.01z"
-      android:strokeAlpha="0.2"
-      android:fillColor="#3E2723"
-      android:fillAlpha="0.2"/>
-  <path
-      android:pathData="M155.58,171.68l-34.93,-34.87l0,1l34.68,34.62z"
-      android:strokeAlpha="0.2"
-      android:fillColor="#3E2723"
-      android:fillAlpha="0.2"/>
-  <path
-      android:pathData="M119.69,131.12c-6.33,5.54 -14.61,8.9 -23.68,8.9c-9.97,0 -19,-4.06 -25.52,-10.61h-0.01l5.59,5.59c5.71,3.8 12.57,6.03 19.94,6.03c9.07,0 17.35,-3.36 23.68,-8.9l0.96,0.99v-1L119.69,131.12z"
-      android:strokeAlpha="0.2"
-      android:fillColor="#3E2723"
-      android:fillAlpha="0.2"/>
-  <path
-      android:pathData="M13.59,69.14L87.15,12c5.2,-4.04 12.45,-3.99 17.61,0.12l73.81,58.94c3.36,2.68 5.27,6.67 5.41,10.82c0.15,-4.51 -1.79,-8.93 -5.41,-11.82l-73.81,-58.94C99.6,7.01 92.35,6.96 87.15,11L13.59,68.14c-3.72,2.88 -5.72,7.36 -5.57,11.94C8.17,75.85 10.14,71.81 13.59,69.14z"
-      android:strokeAlpha="0.2"
-      android:fillColor="#FFFFFF"
-      android:fillAlpha="0.2"/>
-  <path
-      android:pathData="M112,108h-12v12h-8v-12H80v-8h12V88h8v12h12V108z"
-      android:fillColor="#F8BBD0"/>
-  <path
-      android:pathData="M129.57,127.9h-4.7l-1.05,-1.01c5.12,-6.22 8.2,-14.19 8.2,-22.87c0,-19.89 -16.12,-36.01 -36.01,-36.01s-36.02,16.11 -36.02,36s16.13,36.01 36.02,36.01c9.07,0 17.35,-3.36 23.68,-8.9l0.96,0.99v4.7l34.93,34.87l4.33,-13.39L129.57,127.9zM96.01,128.01c-13.25,0 -24,-10.75 -24,-24s10.75,-24 24,-24s24,10.75 24,24S109.26,128.01 96.01,128.01z"
-      android:fillColor="#FFFFFF"/>
-  <path
-      android:pathData="M37.14,173.74L8.61,83.77C6.9,78.02 8.87,71.8 13.59,68.14L87.15,11c5.2,-4.04 12.45,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z">
-    <aapt:attr name="android:fillColor">
-      <gradient
-          android:startY="53.3534"
-          android:startX="38.1466"
-          android:endY="178.712"
-          android:endX="163.5051"
-          android:type="linear">
-        <item android:offset="0" android:color="#19FFFFFF"/>
-        <item android:offset="1" android:color="#00FFFFFF"/>
-      </gradient>
-    </aapt:attr>
-  </path>
-</vector>
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+  <background android:drawable="@color/accessibility_magnification_background" />
+  <foreground>
+    <inset
+        android:drawable="@drawable/ic_accessibility_magnification_foreground"
+        android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+  </foreground>
+
+</adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_magnification_foreground.xml b/core/res/res/drawable/ic_accessibility_magnification_foreground.xml
new file mode 100644
index 0000000..7ce1880
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_magnification_foreground.xml
@@ -0,0 +1,26 @@
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"
+        android:pathData="M17.6942,16.7552L14.0831,13.1441C14.5684,12.4698 14.8646,11.6568 14.8646,10.7682C14.8646,8.5058 13.0307,6.6719 10.7682,6.6719C8.5058,6.6719 6.6719,8.5058 6.6719,10.7682C6.6719,13.0307 8.5058,14.8646 10.7682,14.8646C11.6568,14.8646 12.4698,14.5684 13.1441,14.0831L16.7552,17.6942L17.6942,16.7552ZM10.7682,13.6042C9.199,13.6042 7.9323,12.3374 7.9323,10.7682C7.9323,9.199 9.199,7.9323 10.7682,7.9323C12.3374,7.9323 13.6042,9.199 13.6042,10.7682C13.6042,12.3374 12.3374,13.6042 10.7682,13.6042ZM12.6589,10.138H11.3984V8.8776H10.138V10.138H8.8776V11.3984H10.138V12.6589H11.3984V11.3984H12.6589V10.138Z" />
+</vector>
diff --git a/core/res/res/drawable/ic_accessibility_reduce_bright_colors.xml b/core/res/res/drawable/ic_accessibility_reduce_bright_colors.xml
new file mode 100644
index 0000000..4eab97b
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_reduce_bright_colors.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/accessibility_feature_background" />
+    <foreground>
+        <inset
+            android:drawable="@drawable/ic_accessibility_reduce_bright_colors_foreground"
+            android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+    </foreground>
+</adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_reduce_bright_colors_foreground.xml b/core/res/res/drawable/ic_accessibility_reduce_bright_colors_foreground.xml
new file mode 100644
index 0000000..58609da
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_reduce_bright_colors_foreground.xml
@@ -0,0 +1,54 @@
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <group android:scaleX="2.97"
+        android:scaleY="2.97"
+        android:translateX="18.36"
+        android:translateY="18.36">
+        <path
+            android:pathData="M17,12.1L15.59,10.69L13.05,13.22V7.05H11.05V13.22L8.51,10.69L7.1,12.1L12.05,17.05L17,12.1Z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M2.05,13.05H4.05C4.6,13.05 5.05,12.6 5.05,12.05C5.05,11.5 4.6,11.05 4.05,11.05H2.05C1.5,11.05 1.05,11.5 1.05,12.05C1.05,12.6 1.5,13.05 2.05,13.05Z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M20.05,13.05H22.05C22.6,13.05 23.05,12.6 23.05,12.05C23.05,11.5 22.6,11.05 22.05,11.05H20.05C19.5,11.05 19.05,11.5 19.05,12.05C19.05,12.6 19.5,13.05 20.05,13.05Z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M11.05,2.05V4.05C11.05,4.6 11.5,5.05 12.05,5.05C12.6,5.05 13.05,4.6 13.05,4.05V2.05C13.05,1.5 12.6,1.05 12.05,1.05C11.5,1.05 11.05,1.5 11.05,2.05Z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M11.05,20.05V22.05C11.05,22.6 11.5,23.05 12.05,23.05C12.6,23.05 13.05,22.6 13.05,22.05V20.05C13.05,19.5 12.6,19.05 12.05,19.05C11.5,19.05 11.05,19.5 11.05,20.05Z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M6.04,4.63C5.65,4.24 5.01,4.24 4.63,4.63C4.24,5.02 4.24,5.66 4.63,6.04L5.69,7.1C6.08,7.49 6.72,7.49 7.1,7.1C7.49,6.71 7.49,6.07 7.1,5.69L6.04,4.63Z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M18.41,17C18.02,16.61 17.38,16.61 17,17C16.61,17.39 16.61,18.03 17,18.41L18.06,19.47C18.45,19.86 19.09,19.86 19.47,19.47C19.86,19.08 19.86,18.44 19.47,18.06L18.41,17Z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M19.47,6.04C19.86,5.65 19.86,5.01 19.47,4.63C19.08,4.24 18.44,4.24 18.06,4.63L17,5.69C16.61,6.08 16.61,6.72 17,7.1C17.39,7.49 18.03,7.49 18.41,7.1L19.47,6.04Z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M7.1,18.41C7.49,18.02 7.49,17.38 7.1,17C6.71,16.61 6.07,16.61 5.69,17L4.63,18.06C4.24,18.45 4.24,19.09 4.63,19.47C5.02,19.86 5.66,19.86 6.04,19.47L7.1,18.41Z"
+            android:fillColor="#ffffff"/>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index 9e1692f..e7174cc 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -18,7 +18,7 @@
     android:id="@+id/work_widget_mask_frame"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="#F3374248"
+    android:background="?android:attr/colorSurfaceVariant"
     android:importantForAccessibility="noHideDescendants"
     android:clickable="true">
 
@@ -33,8 +33,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="bottom|right"
-        android:layout_marginBottom="4dp"
-        android:layout_marginRight="4dp"
+        android:layout_marginBottom="12dp"
+        android:layout_marginRight="12dp"
         android:src="@drawable/ic_corp_badge_off"
         android:clickable="false" />
 </FrameLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f097009..d4fa285 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9576,8 +9576,9 @@
     <attr name="lStar" format="float"/>
 
     <declare-styleable name="DisplayHashingService">
-        <!-- The throttle duration for display hash requests
+        <!-- The interval required between display hash requests. Requests made faster than this
+             delay will be throttled."
              @hide @SystemApi -->
-        <attr name="throttleDurationMillis" format="integer" />
+        <attr name="durationBetweenRequestsMillis" format="integer" />
     </declare-styleable>
 </resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 0213c60..36168e7 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -427,4 +427,10 @@
     <!-- Darkest shade of the secondary neutral color used by the system. Black.
      This value can be overlaid at runtime by OverlayManager RROs. -->
     <color name="system_neutral2_1000">#000000</color>
+
+    <!-- Accessibility shortcut icon background color -->
+    <color name="accessibility_feature_background">#5F6368</color> <!-- Google grey 700 -->
+    <color name="accessibility_magnification_background">#F50D60</color>
+    <color name="accessibility_daltonizer_background">#00BCD4</color>
+    <color name="accessibility_color_inversion_background">#546E7A</color>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fbc9678..d3ea52e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4892,4 +4892,7 @@
     <bool name="config_supportsMicToggle">false</bool>
     <!-- Whether this device is supporting the camera toggle -->
     <bool name="config_supportsCamToggle">false</bool>
+
+    <!-- List containing the allowed install sources for accessibility service. -->
+    <string-array name="config_accessibility_allowed_install_source" translatable="false"/>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index fc3ed63..6be6167 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -37,7 +37,7 @@
     <!-- Text size of the message within a toast -->
     <dimen name="toast_text_size">14sp</dimen>
     <!-- Elevation of toast view -->
-    <dimen name="toast_elevation">6dp</dimen>
+    <dimen name="toast_elevation">2dp</dimen>
 
     <!-- Height of the status bar -->
     <dimen name="status_bar_height">@dimen/status_bar_height_portrait</dimen>
@@ -576,6 +576,9 @@
     <!-- Width of the outline stroke used by the accessibility focus rectangle -->
     <dimen name="accessibility_focus_highlight_stroke_width">4dp</dimen>
 
+    <!-- The padding ratio of the Accessibility icon foreground drawable -->
+    <item name="accessibility_icon_foreground_padding_ratio" type="dimen">21.88%</item>
+
     <!-- Margin around the various security views -->
     <dimen name="keyguard_muliuser_selector_margin">8dp</dimen>
 
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index 3c65caf..e9b42d3 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -74,7 +74,7 @@
         <item>ar-TD-u-nu-latn</item> <!-- Arabic (Chad, Western Digits) -->
         <item>ar-TN</item> <!-- Arabic (Tunisia) -->
         <item>ar-TN-u-nu-arab</item> <!-- Arabic (Tunisia, Arabic-Indic Digits) -->
-        <item>ar-XB</item> <!-- Right-to-left pseudolocale -->
+        <item>ar-XB</item> <!-- Arabic (Pseudo-Bidi) -->
         <item>ar-YE</item> <!-- Arabic (Yemen) -->
         <item>ar-YE-u-nu-latn</item> <!-- Arabic (Yemen, Western Digits) -->
         <item>as-IN</item> <!-- Assamese (India) -->
@@ -104,6 +104,7 @@
         <item>ca-FR</item> <!-- Catalan (France) -->
         <item>ca-IT</item> <!-- Catalan (Italy) -->
         <item>ce-RU</item> <!-- Chechen (Russia) -->
+        <item>ceb-PH</item> <!-- Cebuano (Philippines) -->
         <item>cgg-UG</item> <!-- Chiga (Uganda) -->
         <item>chr-US</item> <!-- Cherokee (United States) -->
         <item>cs-CZ</item> <!-- Czech (Czechia) -->
@@ -119,15 +120,18 @@
         <item>de-LI</item> <!-- German (Liechtenstein) -->
         <item>de-LU</item> <!-- German (Luxembourg) -->
         <item>dje-NE</item> <!-- Zarma (Niger) -->
+        <item>doi-IN</item> <!-- Dogri (India) -->
         <item>dsb-DE</item> <!-- Lower Sorbian (Germany) -->
         <item>dua-CM</item> <!-- Duala (Cameroon) -->
         <item>dyo-SN</item> <!-- Jola-Fonyi (Senegal) -->
         <item>dz-BT</item> <!-- Dzongkha (Bhutan) -->
+        <item>dz-BT-u-nu-latn</item> <!-- Dzongkha (Bhutan, Western Digits) -->
         <item>ebu-KE</item> <!-- Embu (Kenya) -->
         <item>ee-GH</item> <!-- Ewe (Ghana) -->
         <item>ee-TG</item> <!-- Ewe (Togo) -->
         <item>el-CY</item> <!-- Greek (Cyprus) -->
         <item>el-GR</item> <!-- Greek (Greece) -->
+        <item>en-AE</item> <!-- English (United Arab Emirates) -->
         <item>en-AG</item> <!-- English (Antigua & Barbuda) -->
         <item>en-AI</item> <!-- English (Anguilla) -->
         <item>en-AS</item> <!-- English (American Samoa) -->
@@ -181,7 +185,7 @@
         <item>en-LS</item> <!-- English (Lesotho) -->
         <item>en-MG</item> <!-- English (Madagascar) -->
         <item>en-MH</item> <!-- English (Marshall Islands) -->
-        <item>en-MO</item> <!-- English (Macau) -->
+        <item>en-MO</item> <!-- English (Macao) -->
         <item>en-MP</item> <!-- English (Northern Mariana Islands) -->
         <item>en-MS</item> <!-- English (Montserrat) -->
         <item>en-MT</item> <!-- English (Malta) -->
@@ -212,7 +216,7 @@
         <item>en-SL</item> <!-- English (Sierra Leone) -->
         <item>en-SS</item> <!-- English (South Sudan) -->
         <item>en-SX</item> <!-- English (Sint Maarten) -->
-        <item>en-SZ</item> <!-- English (Swaziland) -->
+        <item>en-SZ</item> <!-- English (Eswatini) -->
         <item>en-TC</item> <!-- English (Turks & Caicos Islands) -->
         <item>en-TK</item> <!-- English (Tokelau) -->
         <item>en-TO</item> <!-- English (Tonga) -->
@@ -227,7 +231,7 @@
         <item>en-VI</item> <!-- English (U.S. Virgin Islands) -->
         <item>en-VU</item> <!-- English (Vanuatu) -->
         <item>en-WS</item> <!-- English (Samoa) -->
-        <item>en-XA</item> <!-- Left-to-right pseudolocale -->
+        <item>en-XA</item> <!-- English (Pseudo-Accents) -->
         <item>en-ZA</item> <!-- English (South Africa) -->
         <item>en-ZM</item> <!-- English (Zambia) -->
         <item>en-ZW</item> <!-- English (Zimbabwe) -->
@@ -265,10 +269,42 @@
         <item>fa-AF-u-nu-latn</item> <!-- Persian (Afghanistan, Western Digits) -->
         <item>fa-IR</item> <!-- Persian (Iran) -->
         <item>fa-IR-u-nu-latn</item> <!-- Persian (Iran, Western Digits) -->
-        <item>ff-CM</item> <!-- Fulah (Cameroon) -->
-        <item>ff-GN</item> <!-- Fulah (Guinea) -->
-        <item>ff-MR</item> <!-- Fulah (Mauritania) -->
-        <item>ff-SN</item> <!-- Fulah (Senegal) -->
+        <item>ff-Adlm-BF</item> <!-- Fulah (Adlam, Burkina Faso) -->
+        <item>ff-Adlm-BF-u-nu-latn</item> <!-- Fulah (Adlam, Burkina Faso, Western Digits) -->
+        <item>ff-Adlm-CM</item> <!-- Fulah (Adlam, Cameroon) -->
+        <item>ff-Adlm-CM-u-nu-latn</item> <!-- Fulah (Adlam, Cameroon, Western Digits) -->
+        <item>ff-Adlm-GH</item> <!-- Fulah (Adlam, Ghana) -->
+        <item>ff-Adlm-GH-u-nu-latn</item> <!-- Fulah (Adlam, Ghana, Western Digits) -->
+        <item>ff-Adlm-GM</item> <!-- Fulah (Adlam, Gambia) -->
+        <item>ff-Adlm-GM-u-nu-latn</item> <!-- Fulah (Adlam, Gambia, Western Digits) -->
+        <item>ff-Adlm-GN</item> <!-- Fulah (Adlam, Guinea) -->
+        <item>ff-Adlm-GN-u-nu-latn</item> <!-- Fulah (Adlam, Guinea, Western Digits) -->
+        <item>ff-Adlm-GW</item> <!-- Fulah (Adlam, Guinea-Bissau) -->
+        <item>ff-Adlm-GW-u-nu-latn</item> <!-- Fulah (Adlam, Guinea-Bissau, Western Digits) -->
+        <item>ff-Adlm-LR</item> <!-- Fulah (Adlam, Liberia) -->
+        <item>ff-Adlm-LR-u-nu-latn</item> <!-- Fulah (Adlam, Liberia, Western Digits) -->
+        <item>ff-Adlm-MR</item> <!-- Fulah (Adlam, Mauritania) -->
+        <item>ff-Adlm-MR-u-nu-latn</item> <!-- Fulah (Adlam, Mauritania, Western Digits) -->
+        <item>ff-Adlm-NE</item> <!-- Fulah (Adlam, Niger) -->
+        <item>ff-Adlm-NE-u-nu-latn</item> <!-- Fulah (Adlam, Niger, Western Digits) -->
+        <item>ff-Adlm-NG</item> <!-- Fulah (Adlam, Nigeria) -->
+        <item>ff-Adlm-NG-u-nu-latn</item> <!-- Fulah (Adlam, Nigeria, Western Digits) -->
+        <item>ff-Adlm-SL</item> <!-- Fulah (Adlam, Sierra Leone) -->
+        <item>ff-Adlm-SL-u-nu-latn</item> <!-- Fulah (Adlam, Sierra Leone, Western Digits) -->
+        <item>ff-Adlm-SN</item> <!-- Fulah (Adlam, Senegal) -->
+        <item>ff-Adlm-SN-u-nu-latn</item> <!-- Fulah (Adlam, Senegal, Western Digits) -->
+        <item>ff-Latn-BF</item> <!-- Fulah (Latin, Burkina Faso) -->
+        <item>ff-Latn-CM</item> <!-- Fulah (Latin, Cameroon) -->
+        <item>ff-Latn-GH</item> <!-- Fulah (Latin, Ghana) -->
+        <item>ff-Latn-GM</item> <!-- Fulah (Latin, Gambia) -->
+        <item>ff-Latn-GN</item> <!-- Fulah (Latin, Guinea) -->
+        <item>ff-Latn-GW</item> <!-- Fulah (Latin, Guinea-Bissau) -->
+        <item>ff-Latn-LR</item> <!-- Fulah (Latin, Liberia) -->
+        <item>ff-Latn-MR</item> <!-- Fulah (Latin, Mauritania) -->
+        <item>ff-Latn-NE</item> <!-- Fulah (Latin, Niger) -->
+        <item>ff-Latn-NG</item> <!-- Fulah (Latin, Nigeria) -->
+        <item>ff-Latn-SL</item> <!-- Fulah (Latin, Sierra Leone) -->
+        <item>ff-Latn-SN</item> <!-- Fulah (Latin, Senegal) -->
         <item>fi-FI</item> <!-- Finnish (Finland) -->
         <item>fil-PH</item> <!-- Filipino (Philippines) -->
         <item>fo-DK</item> <!-- Faroese (Denmark) -->
@@ -321,6 +357,7 @@
         <item>fr-YT</item> <!-- French (Mayotte) -->
         <item>fur-IT</item> <!-- Friulian (Italy) -->
         <item>fy-NL</item> <!-- Western Frisian (Netherlands) -->
+        <item>ga-GB</item> <!-- Irish (United Kingdom) -->
         <item>ga-IE</item> <!-- Irish (Ireland) -->
         <item>gd-GB</item> <!-- Scottish Gaelic (United Kingdom) -->
         <item>gl-ES</item> <!-- Galician (Spain) -->
@@ -340,6 +377,7 @@
         <item>hsb-DE</item> <!-- Upper Sorbian (Germany) -->
         <item>hu-HU</item> <!-- Hungarian (Hungary) -->
         <item>hy-AM</item> <!-- Armenian (Armenia) -->
+        <item>ia-001</item> <!-- Interlingua (World) -->
         <item>ig-NG</item> <!-- Igbo (Nigeria) -->
         <item>ii-CN</item> <!-- Sichuan Yi (China) -->
         <item>in-ID</item> <!-- Indonesian (Indonesia) -->
@@ -347,10 +385,12 @@
         <item>it-CH</item> <!-- Italian (Switzerland) -->
         <item>it-IT</item> <!-- Italian (Italy) -->
         <item>it-SM</item> <!-- Italian (San Marino) -->
+        <item>it-VA</item> <!-- Italian (Vatican City) -->
         <item>iw-IL</item> <!-- Hebrew (Israel) -->
         <item>ja-JP</item> <!-- Japanese (Japan) -->
         <item>jgo-CM</item> <!-- Ngomba (Cameroon) -->
         <item>jmc-TZ</item> <!-- Machame (Tanzania) -->
+        <item>jv-ID</item> <!-- Javanese (Indonesia) -->
         <item>ka-GE</item> <!-- Georgian (Georgia) -->
         <item>kab-DZ</item> <!-- Kabyle (Algeria) -->
         <item>kam-KE</item> <!-- Kamba (Kenya) -->
@@ -386,6 +426,7 @@
         <item>luo-KE</item> <!-- Luo (Kenya) -->
         <item>luy-KE</item> <!-- Luyia (Kenya) -->
         <item>lv-LV</item> <!-- Latvian (Latvia) -->
+        <item>mai-IN</item> <!-- Maithili (India) -->
         <item>mas-KE</item> <!-- Masai (Kenya) -->
         <item>mas-TZ</item> <!-- Masai (Tanzania) -->
         <item>mer-KE</item> <!-- Meru (Kenya) -->
@@ -393,23 +434,31 @@
         <item>mg-MG</item> <!-- Malagasy (Madagascar) -->
         <item>mgh-MZ</item> <!-- Makhuwa-Meetto (Mozambique) -->
         <item>mgo-CM</item> <!-- Metaʼ (Cameroon) -->
-        <item>mk-MK</item> <!-- Macedonian (Macedonia (FYROM)) -->
+        <item>mi-NZ</item> <!-- Maori (New Zealand) -->
+        <item>mk-MK</item> <!-- Macedonian (North Macedonia) -->
         <item>ml-IN</item> <!-- Malayalam (India) -->
         <item>mn-MN</item> <!-- Mongolian (Mongolia) -->
+        <item>mni-IN</item> <!-- Manipuri (India) -->
+        <item>mni-IN-u-nu-latn</item> <!-- Manipuri (India, Western Digits) -->
         <item>mr-IN</item> <!-- Marathi (India) -->
+        <item>mr-IN-u-nu-latn</item> <!-- Marathi (India, Western Digits) -->
         <item>ms-BN</item> <!-- Malay (Brunei) -->
+        <item>ms-ID</item> <!-- Malay (Indonesia) -->
         <item>ms-MY</item> <!-- Malay (Malaysia) -->
         <item>ms-SG</item> <!-- Malay (Singapore) -->
         <item>mt-MT</item> <!-- Maltese (Malta) -->
         <item>my-MM</item> <!-- Burmese (Myanmar (Burma)) -->
         <item>my-MM-u-nu-latn</item> <!-- Burmese (Myanmar (Burma), Western Digits) -->
         <item>mzn-IR</item> <!-- Mazanderani (Iran) -->
+        <item>mzn-IR-u-nu-latn</item> <!-- Mazanderani (Iran, Western Digits) -->
         <item>naq-NA</item> <!-- Nama (Namibia) -->
         <item>nb-NO</item> <!-- Norwegian Bokmål (Norway) -->
         <item>nb-SJ</item> <!-- Norwegian Bokmål (Svalbard & Jan Mayen) -->
         <item>nd-ZW</item> <!-- North Ndebele (Zimbabwe) -->
         <item>ne-IN</item> <!-- Nepali (India) -->
+        <item>ne-IN-u-nu-latn</item> <!-- Nepali (India, Western Digits) -->
         <item>ne-NP</item> <!-- Nepali (Nepal) -->
+        <item>ne-NP-u-nu-latn</item> <!-- Nepali (Nepal, Western Digits) -->
         <item>nl-AW</item> <!-- Dutch (Aruba) -->
         <item>nl-BE</item> <!-- Dutch (Belgium) -->
         <item>nl-BQ</item> <!-- Dutch (Caribbean Netherlands) -->
@@ -427,14 +476,22 @@
         <item>os-GE</item> <!-- Ossetic (Georgia) -->
         <item>os-RU</item> <!-- Ossetic (Russia) -->
         <item>pa-Arab-PK</item> <!-- Punjabi (Arabic, Pakistan) -->
+        <item>pa-Arab-PK-u-nu-latn</item> <!-- Punjabi (Arabic, Pakistan, Western Digits) -->
         <item>pa-Guru-IN</item> <!-- Punjabi (Gurmukhi, India) -->
+        <item>pcm-NG</item> <!-- Nigerian Pidgin (Nigeria) -->
         <item>pl-PL</item> <!-- Polish (Poland) -->
         <item>ps-AF</item> <!-- Pashto (Afghanistan) -->
+        <item>ps-AF-u-nu-latn</item> <!-- Pashto (Afghanistan, Western Digits) -->
+        <item>ps-PK</item> <!-- Pashto (Pakistan) -->
+        <item>ps-PK-u-nu-latn</item> <!-- Pashto (Pakistan, Western Digits) -->
         <item>pt-AO</item> <!-- Portuguese (Angola) -->
         <item>pt-BR</item> <!-- Portuguese (Brazil) -->
+        <item>pt-CH</item> <!-- Portuguese (Switzerland) -->
         <item>pt-CV</item> <!-- Portuguese (Cape Verde) -->
+        <item>pt-GQ</item> <!-- Portuguese (Equatorial Guinea) -->
         <item>pt-GW</item> <!-- Portuguese (Guinea-Bissau) -->
-        <item>pt-MO</item> <!-- Portuguese (Macau) -->
+        <item>pt-LU</item> <!-- Portuguese (Luxembourg) -->
+        <item>pt-MO</item> <!-- Portuguese (Macao) -->
         <item>pt-MZ</item> <!-- Portuguese (Mozambique) -->
         <item>pt-PT</item> <!-- Portuguese (Portugal) -->
         <item>pt-ST</item> <!-- Portuguese (São Tomé & Príncipe) -->
@@ -455,9 +512,15 @@
         <item>ru-UA</item> <!-- Russian (Ukraine) -->
         <item>rw-RW</item> <!-- Kinyarwanda (Rwanda) -->
         <item>rwk-TZ</item> <!-- Rwa (Tanzania) -->
+        <item>sa-IN</item> <!-- Sanskrit (India) -->
         <item>sah-RU</item> <!-- Sakha (Russia) -->
         <item>saq-KE</item> <!-- Samburu (Kenya) -->
+        <item>sat-IN</item> <!-- Santali (India) -->
+        <item>sat-IN-u-nu-latn</item> <!-- Santali (India, Western Digits) -->
         <item>sbp-TZ</item> <!-- Sangu (Tanzania) -->
+        <item>sd-Arab-PK</item> <!-- Sindhi (Arabic, Pakistan) -->
+        <item>sd-Arab-PK-u-nu-latn</item> <!-- Sindhi (Arabic, Pakistan, Western Digits) -->
+        <item>sd-Deva-IN</item> <!-- Sindhi (Devanagari, India) -->
         <item>se-FI</item> <!-- Northern Sami (Finland) -->
         <item>se-NO</item> <!-- Northern Sami (Norway) -->
         <item>se-SE</item> <!-- Northern Sami (Sweden) -->
@@ -474,7 +537,7 @@
         <item>so-KE</item> <!-- Somali (Kenya) -->
         <item>so-SO</item> <!-- Somali (Somalia) -->
         <item>sq-AL</item> <!-- Albanian (Albania) -->
-        <item>sq-MK</item> <!-- Albanian (Macedonia (FYROM)) -->
+        <item>sq-MK</item> <!-- Albanian (North Macedonia) -->
         <item>sq-XK</item> <!-- Albanian (Kosovo) -->
         <item>sr-Cyrl-BA</item> <!-- Serbian (Cyrillic, Bosnia & Herzegovina) -->
         <item>sr-Cyrl-ME</item> <!-- Serbian (Cyrillic, Montenegro) -->
@@ -484,6 +547,7 @@
         <item>sr-Latn-ME</item> <!-- Serbian (Latin, Montenegro) -->
         <item>sr-Latn-RS</item> <!-- Serbian (Latin, Serbia) -->
         <item>sr-Latn-XK</item> <!-- Serbian (Latin, Kosovo) -->
+        <item>su-ID</item> <!-- Sundanese (Indonesia) -->
         <item>sv-AX</item> <!-- Swedish (Åland Islands) -->
         <item>sv-FI</item> <!-- Swedish (Finland) -->
         <item>sv-SE</item> <!-- Swedish (Sweden) -->
@@ -498,10 +562,13 @@
         <item>te-IN</item> <!-- Telugu (India) -->
         <item>teo-KE</item> <!-- Teso (Kenya) -->
         <item>teo-UG</item> <!-- Teso (Uganda) -->
+        <item>tg-TJ</item> <!-- Tajik (Tajikistan) -->
         <item>th-TH</item> <!-- Thai (Thailand) -->
+        <item>tk-TM</item> <!-- Turkmen (Turkmenistan) -->
         <item>to-TO</item> <!-- Tongan (Tonga) -->
         <item>tr-CY</item> <!-- Turkish (Cyprus) -->
         <item>tr-TR</item> <!-- Turkish (Turkey) -->
+        <item>tt-RU</item> <!-- Tatar (Russia) -->
         <item>twq-NE</item> <!-- Tasawaq (Niger) -->
         <item>tzm-MA</item> <!-- Central Atlas Tamazight (Morocco) -->
         <item>ug-CN</item> <!-- Uyghur (China) -->
@@ -511,11 +578,13 @@
         <item>ur-PK</item> <!-- Urdu (Pakistan) -->
         <item>ur-PK-u-nu-arabext</item> <!-- Urdu (Pakistan, Extended Arabic-Indic Digits) -->
         <item>uz-Arab-AF</item> <!-- Uzbek (Arabic, Afghanistan) -->
+        <item>uz-Arab-AF-u-nu-latn</item> <!-- Uzbek (Arabic, Afghanistan, Western Digits) -->
         <item>uz-Cyrl-UZ</item> <!-- Uzbek (Cyrillic, Uzbekistan) -->
         <item>uz-Latn-UZ</item> <!-- Uzbek (Latin, Uzbekistan) -->
         <item>vi-VN</item> <!-- Vietnamese (Vietnam) -->
         <item>vun-TZ</item> <!-- Vunjo (Tanzania) -->
         <item>wae-CH</item> <!-- Walser (Switzerland) -->
+        <item>wo-SN</item> <!-- Wolof (Senegal) -->
         <item>xog-UG</item> <!-- Soga (Uganda) -->
         <item>yav-CM</item> <!-- Yangben (Cameroon) -->
         <item>yo-BJ</item> <!-- Yoruba (Benin) -->
@@ -525,10 +594,10 @@
         <item>zgh-MA</item> <!-- Standard Moroccan Tamazight (Morocco) -->
         <item>zh-Hans-CN</item> <!-- Chinese (Simplified, China) -->
         <item>zh-Hans-HK</item> <!-- Chinese (Simplified, Hong Kong) -->
-        <item>zh-Hans-MO</item> <!-- Chinese (Simplified, Macau) -->
+        <item>zh-Hans-MO</item> <!-- Chinese (Simplified, Macao) -->
         <item>zh-Hans-SG</item> <!-- Chinese (Simplified, Singapore) -->
         <item>zh-Hant-HK</item> <!-- Chinese (Traditional, Hong Kong) -->
-        <item>zh-Hant-MO</item> <!-- Chinese (Traditional, Macau) -->
+        <item>zh-Hant-MO</item> <!-- Chinese (Traditional, Macao) -->
         <item>zh-Hant-TW</item> <!-- Chinese (Traditional, Taiwan) -->
         <item>zu-ZA</item> <!-- Zulu (South Africa) -->
     </string-array>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c716000..0e9526a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3097,7 +3097,7 @@
     <public name="playHomeTransitionSound" />
     <public name="lStar" />
     <!-- @hide @SystemApi -->
-    <public name="throttleDurationMillis" />
+    <public name="durationBetweenRequestsMillis" />
     <public name="showInInputMethodPicker" />
     <public name="effectColor" />
   </staging-public-group>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fd33ccd..270b98e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6006,4 +6006,8 @@
     <string name="view_and_control_notification_title">Check access settings</string>
     <!-- Notification content to prompt the user that some accessibility service has view and control access. [CHAR LIMIT=none] -->
     <string name="view_and_control_notification_content"><xliff:g id="service_name" example="TalkBack">%s</xliff:g> can view and control your screen. Tap to review.</string>
+    <!-- Accessibility message announced when the view text displayed on the screen that has been translated to a different language by the system. [CHAR LIMIT=NONE] -->
+    <string name="ui_translation_accessibility_translated_text"><xliff:g id="message" example="Hello">%1$s</xliff:g> Translated.</string>
+    <!-- Accessibility message announced to notify the user when the system has finished translating the content displayed on the screen to a different language after the user requested translation. [CHAR LIMIT=NONE] -->
+    <string name="ui_translation_accessibility_translation_finished">Message translated from <xliff:g id="from_language" example="English">%1$s</xliff:g> to <xliff:g id="to_language" example="French">%2$s</xliff:g>.</string>
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index fbf67e0..bac9cf2 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -964,7 +964,7 @@
     </style>
 
     <style name="TextAppearance.Toast">
-        <item name="fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="fontFamily">@*android:string/config_bodyFontFamily</item>
         <item name="textSize">14sp</item>
         <item name="textColor">?android:attr/textColorPrimary</item>
     </style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d62f2bc..b9f1e20 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3404,6 +3404,7 @@
   <java-symbol type="drawable" name="ic_accessibility_color_inversion" />
   <java-symbol type="drawable" name="ic_accessibility_color_correction" />
   <java-symbol type="drawable" name="ic_accessibility_magnification" />
+  <java-symbol type="drawable" name="ic_accessibility_reduce_bright_colors" />
 
   <java-symbol type="string" name="reduce_bright_colors_feature_name" />
 
@@ -4356,6 +4357,11 @@
   <java-symbol type="drawable" name="ic_accessibility_24dp" />
   <java-symbol type="string" name="view_and_control_notification_title" />
   <java-symbol type="string" name="view_and_control_notification_content" />
+  <java-symbol type="array" name="config_accessibility_allowed_install_source" />
+  
+  <!-- Translation -->
+  <java-symbol type="string" name="ui_translation_accessibility_translated_text" />
+  <java-symbol type="string" name="ui_translation_accessibility_translation_finished" />
 
   <java-symbol type="layout" name="notification_expand_button"/>
 
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
index 412b367..8b39beb 100644
--- a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -33,6 +33,7 @@
 import android.hardware.lights.Light;
 import android.hardware.lights.LightState;
 import android.hardware.lights.LightsManager;
+import android.hardware.lights.LightsRequest;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
@@ -224,4 +225,25 @@
         session.close();
         verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
     }
+
+    @Test
+    public void testLightsRequest() throws Exception {
+        Light light = new Light(1 /* id */, 0 /* ordinal */,  Light.LIGHT_TYPE_INPUT_PLAYER_ID);
+        LightState state = new LightState(0xf1);
+        LightsRequest request = new Builder().addLight(light, state).build();
+
+        // Covers the LightsRequest.getLights
+        assertThat(request.getLights().size()).isEqualTo(1);
+        assertThat(request.getLights().get(0)).isEqualTo(1);
+
+        // Covers the LightsRequest.getLightStates
+        assertThat(request.getLightStates().size()).isEqualTo(1);
+        assertThat(request.getLightStates().get(0)).isEqualTo(state);
+
+        // Covers the LightsRequest.getLightsAndStates
+        assertThat(request.getLightsAndStates().size()).isEqualTo(1);
+        assertThat(request.getLightsAndStates().containsKey(1)).isTrue();
+        assertThat(request.getLightsAndStates().get(1)).isEqualTo(state);
+    }
+
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 8c5e0fc..e2e672e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -984,8 +984,9 @@
                     return mHandler;
                 }
 
-                public BinderLatencyObserver getLatencyObserver() {
-                    return new BinderLatencyObserverTest.TestBinderLatencyObserver();
+                @Override
+                public BinderLatencyObserver getLatencyObserver(int processSource) {
+                    return new BinderLatencyObserverTest.TestBinderLatencyObserver(processSource);
                 }
             });
             setSamplingInterval(1);
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
index bf87683..4157f5e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
@@ -245,18 +245,24 @@
         private ArrayList<String> mWrittenAtoms;
 
         TestBinderLatencyObserver() {
-            // Make random generator not random.
-            super(new Injector() {
-                public Random getRandomGenerator() {
-                    return new Random() {
-                        int mCallCount = 0;
+            this(SYSTEM_SERVER);
+        }
 
-                        public int nextInt() {
-                            return mCallCount++;
+        TestBinderLatencyObserver(int processSource) {
+            // Make random generator not random.
+            super(
+                    new Injector() {
+                        public Random getRandomGenerator() {
+                            return new Random() {
+                                int mCallCount = 0;
+
+                                public int nextInt() {
+                                    return mCallCount++;
+                                }
+                            };
                         }
-                    };
-                }
-            });
+                    },
+                    processSource);
             setSamplingInterval(1);
             mWrittenAtoms = new ArrayList<>();
         }
diff --git a/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java b/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java
index 43f9b6f..48f4288 100644
--- a/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java
+++ b/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java
@@ -19,6 +19,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -62,7 +63,36 @@
     }
 
     @Test
+    public void testRegister_RegisterUnregisterWhenNotSupported() throws RemoteException {
+        // isControllerAlwaysOnSupported() returns false, not supported.
+        doReturn(false).when(mNfcAdapter).isControllerAlwaysOnSupported();
+        NfcControllerAlwaysOnListener mListener =
+                new NfcControllerAlwaysOnListener(mNfcAdapter);
+        ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class);
+        ControllerAlwaysOnListener mockListener2 = mock(ControllerAlwaysOnListener.class);
+
+        // Verify that the state listener will not registered with the NFC Adapter
+        mListener.register(getExecutor(), mockListener1);
+        verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+
+        // Register a second client and no any call to NFC Adapter
+        mListener.register(getExecutor(), mockListener2);
+        verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+
+        // Unregister first listener, and no any call to NFC Adapter
+        mListener.unregister(mockListener1);
+        verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+        verify(mNfcAdapter, times(0)).unregisterControllerAlwaysOnListener(any());
+
+        // Unregister second listener, and no any call to NFC Adapter
+        mListener.unregister(mockListener2);
+        verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+        verify(mNfcAdapter, times(0)).unregisterControllerAlwaysOnListener(any());
+    }
+
+    @Test
     public void testRegister_RegisterUnregister() throws RemoteException {
+        doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
         NfcControllerAlwaysOnListener mListener =
                 new NfcControllerAlwaysOnListener(mNfcAdapter);
         ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class);
@@ -89,6 +119,7 @@
 
     @Test
     public void testRegister_FirstRegisterFails() throws RemoteException {
+        doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
         NfcControllerAlwaysOnListener mListener =
                 new NfcControllerAlwaysOnListener(mNfcAdapter);
         ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class);
@@ -116,6 +147,7 @@
 
     @Test
     public void testRegister_RegisterSameListenerTwice() throws RemoteException {
+        doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
         NfcControllerAlwaysOnListener mListener =
                 new NfcControllerAlwaysOnListener(mNfcAdapter);
         ControllerAlwaysOnListener mockListener = mock(ControllerAlwaysOnListener.class);
@@ -132,7 +164,7 @@
 
     @Test
     public void testNotify_AllListenersNotified() throws RemoteException {
-
+        doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
         NfcControllerAlwaysOnListener listener = new NfcControllerAlwaysOnListener(mNfcAdapter);
         List<ControllerAlwaysOnListener> mockListeners = new ArrayList<>();
         for (int i = 0; i < 10; i++) {
@@ -149,7 +181,8 @@
     }
 
     @Test
-    public void testStateChange_CorrectValue() {
+    public void testStateChange_CorrectValue() throws RemoteException {
+        doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
         runStateChangeValue(true, true);
         runStateChangeValue(false, false);
 
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index bd72d45..00219e7 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -74,16 +74,19 @@
      * @param locked            - whether it is a lock (true) or unlock (false) event
      * @param syntheticPassword - if it is an unlock event with the password, pass the synthetic
      *                            password provided by the LockSettingService
+     * @param unlockingSids     - KeyMint secure user IDs that should be permitted to unlock
+     *                            UNLOCKED_DEVICE_REQUIRED keys.
      *
      * @return 0 if successful or a {@code ResponseCode}.
      */
     public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
-            @Nullable byte[] syntheticPassword) {
+            @Nullable byte[] syntheticPassword, @Nullable long[] unlockingSids) {
         try {
             if (locked) {
-                getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null);
+                getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null, unlockingSids);
             } else {
-                getService().onLockScreenEvent(LockScreenEvent.UNLOCK, userId, syntheticPassword);
+                getService().onLockScreenEvent(
+                        LockScreenEvent.UNLOCK, userId, syntheticPassword, unlockingSids);
             }
             return 0;
         } catch (RemoteException | NullPointerException e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index b6d408a..eb82c6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -23,7 +23,6 @@
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
 
 import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 import android.window.WindowContainerToken;
@@ -89,11 +88,11 @@
         ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
                 task1.taskId, task2.taskId, this);
 
-        if ((!task1.isResizeable || !task2.isResizeable)
-                && !ActivityTaskManager.supportsNonResizableMultiWindow()) {
+        if (!task1.supportsMultiWindow || !task2.supportsMultiWindow) {
             ProtoLog.e(WM_SHELL_TASK_ORG,
-                    "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
-                    task1.isResizeable, task2.isResizeable);
+                    "Can't pair tasks that doesn't support multi window, "
+                            + "task1.supportsMultiWindow=%b, task2.supportsMultiWindow=%b",
+                    task1.supportsMultiWindow, task2.supportsMultiWindow);
             return false;
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 8434d66..6f526ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -575,6 +575,7 @@
             Log.d(TAG, "Overflowing: " + bubble);
         }
         mLogger.logOverflowAdd(bubble, reason);
+        mOverflowBubbles.remove(bubble);
         mOverflowBubbles.add(0, bubble);
         mStateChange.addedOverflowBubble = bubble;
         bubble.stopInflation();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index 8ee2b40..f7c7285 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -220,18 +220,25 @@
                     Log.d(TAG, "remove: " + toRemove);
                 }
                 toRemove.cleanupViews();
-                final int i = mOverflowBubbles.indexOf(toRemove);
+                final int indexToRemove = mOverflowBubbles.indexOf(toRemove);
                 mOverflowBubbles.remove(toRemove);
-                mAdapter.notifyItemRemoved(i);
+                mAdapter.notifyItemRemoved(indexToRemove);
             }
 
             Bubble toAdd = update.addedOverflowBubble;
             if (toAdd != null) {
+                final int indexToAdd = mOverflowBubbles.indexOf(toAdd);
                 if (DEBUG_OVERFLOW) {
-                    Log.d(TAG, "add: " + toAdd);
+                    Log.d(TAG, "add: " + toAdd + " prevIndex: " + indexToAdd);
                 }
-                mOverflowBubbles.add(0, toAdd);
-                mAdapter.notifyItemInserted(0);
+                if (indexToAdd > 0) {
+                    mOverflowBubbles.remove(toAdd);
+                    mOverflowBubbles.add(0, toAdd);
+                    mAdapter.notifyItemMoved(indexToAdd, 0);
+                } else {
+                    mOverflowBubbles.add(0, toAdd);
+                    mAdapter.notifyItemInserted(0);
+                }
             }
 
             updateEmptyStateVisibility();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 6719d74..0f5d0ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1604,6 +1604,7 @@
             // can start fresh.
             cancelAllExpandCollapseSwitchAnimations();
         }
+        showManageMenu(false /* show */);
 
         // If we're expanded, screenshot the currently expanded bubble (before expanding the newly
         // selected bubble) so we can animate it out.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index b9fdaa1..442e7a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -110,6 +110,10 @@
             return false;
         }
 
+        if (mDoubleTapDetector.onTouchEvent(event)) {
+            return true;
+        }
+
         final int action = event.getAction() & MotionEvent.ACTION_MASK;
         final boolean isLandscape = isLandscape();
         // Using raw xy to prevent lost track of motion events while moving divider bar.
@@ -136,21 +140,22 @@
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
                 mVelocityTracker.addMovement(event);
+                releaseTouching();
+
+                if (!mMoving) break;
+
                 mVelocityTracker.computeCurrentVelocity(1000 /* units */);
                 final float velocity = isLandscape
                         ? mVelocityTracker.getXVelocity()
                         : mVelocityTracker.getYVelocity();
-                releaseTouching();
-                mMoving = false;
-
                 final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
                 final DividerSnapAlgorithm.SnapTarget snapTarget =
                         mSplitLayout.findSnapTarget(position, velocity, false /* hardDismiss */);
                 mSplitLayout.snapToTarget(position, snapTarget);
+                mMoving = false;
                 break;
         }
 
-        mDoubleTapDetector.onTouchEvent(event);
         return true;
     }
 
@@ -229,7 +234,7 @@
             if (mSplitLayout != null) {
                 mSplitLayout.onDoubleTappedDivider();
             }
-            return false;
+            return true;
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index b64c796..d318a5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -231,6 +231,7 @@
     }
 
     private void flingDividePosition(int from, int to) {
+        if (from == to) return;
         ValueAnimator animator = ValueAnimator
                 .ofInt(from, to)
                 .setDuration(250);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index a18d106..60f7ee2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -49,7 +49,6 @@
 import android.view.View;
 import android.view.View.OnTouchListener;
 import android.view.ViewConfiguration;
-import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver.InternalInsetsInfo;
 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
 import android.view.WindowManager;
@@ -524,9 +523,10 @@
             case MotionEvent.ACTION_CANCEL:
                 mVelocityTracker.addMovement(event);
 
+                if (!mMoving) break;
+
                 x = (int) event.getRawX();
                 y = (int) event.getRawY();
-
                 mVelocityTracker.computeCurrentVelocity(1000);
                 int position = calculatePosition(x, y);
                 stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
index 27c56fd..d9409ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
@@ -30,7 +30,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
 import android.app.WindowConfiguration;
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -92,11 +91,10 @@
                         // is nothing behind it.
                         ((type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK)
                                 && triggerTask.parentTaskId == mListener.mPrimary.taskId)
-                        // if a non-resizable is launched when it is not supported in multi window,
+                        // if an activity that is not supported in multi window mode is launched,
                         // we also need to leave split-screen.
                         || ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)
-                                && !triggerTask.isResizeable
-                                && !ActivityTaskManager.supportsNonResizableMultiWindow());
+                                && !triggerTask.supportsMultiWindow);
                 // In both cases, dismiss the primary
                 if (shouldDismiss) {
                     WindowManagerProxy.buildDismissSplit(out, mListener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
index 5a2ef56..1072845 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
@@ -46,7 +46,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.BooleanSupplier;
 
 /**
  * Proxy to simplify calls into window manager/activity manager
@@ -209,17 +208,10 @@
             return false;
         }
         ActivityManager.RunningTaskInfo topHomeTask = null;
-        // One-time lazy wrapper to avoid duplicated IPC in loop. Not store as class variable
-        // because the value can be changed at runtime.
-        final BooleanSupplier supportsNonResizableMultiWindow =
-                createSupportsNonResizableMultiWindowSupplier();
         for (int i = rootTasks.size() - 1; i >= 0; --i) {
             final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
-            // Check whether to move resizeable task to split secondary.
-            // Also, we have an exception for non-resizable home because we will minimize to show
-            // it.
-            if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME
-                    && !supportsNonResizableMultiWindow.getAsBoolean()) {
+            // Check whether the task can be moved to split secondary.
+            if (!rootTask.supportsMultiWindow && rootTask.topActivityType != ACTIVITY_TYPE_HOME) {
                 continue;
             }
             // Only move fullscreen tasks to split secondary.
@@ -364,21 +356,6 @@
         outWct.setFocusable(tiles.mPrimary.token, true /* focusable */);
     }
 
-    /** Creates a lazy wrapper to get whether it supports non-resizable in multi window. */
-    private static BooleanSupplier createSupportsNonResizableMultiWindowSupplier() {
-        return new BooleanSupplier() {
-            private Boolean mSupportsNonResizableMultiWindow;
-            @Override
-            public boolean getAsBoolean() {
-                if (mSupportsNonResizableMultiWindow == null) {
-                    mSupportsNonResizableMultiWindow =
-                            ActivityTaskManager.supportsNonResizableMultiWindow();
-                }
-                return mSupportsNonResizableMultiWindow;
-            }
-        };
-    }
-
     /**
      * Utility to apply a sync transaction serially with other sync transactions.
      *
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 6bd9603..4b4d934 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -81,6 +81,14 @@
                 @Override
                 public void onOneHandedAnimationStart(
                         OneHandedAnimationController.OneHandedTransitionAnimator animator) {
+                    final boolean isEntering = animator.getTransitionDirection()
+                            == TRANSITION_DIRECTION_TRIGGER;
+                    if (!mTransitionCallbacks.isEmpty()) {
+                        for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
+                            final OneHandedTransitionCallback cb = mTransitionCallbacks.get(i);
+                            cb.onStartTransition(isEntering);
+                        }
+                    }
                 }
 
                 @Override
@@ -262,11 +270,12 @@
         mLastVisualDisplayBounds.offsetTo(0,
                 direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0);
         for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
-            final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
+            final OneHandedTransitionCallback cb = mTransitionCallbacks.get(i);
+            cb.onStartTransition(false /* isTransitioning */);
             if (direction == TRANSITION_DIRECTION_TRIGGER) {
-                callback.onStartFinished(getLastVisualDisplayBounds());
+                cb.onStartFinished(getLastVisualDisplayBounds());
             } else {
-                callback.onStopFinished(getLastVisualDisplayBounds());
+                cb.onStopFinished(getLastVisualDisplayBounds());
             }
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
index 3af7c4b..e829186 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
@@ -24,6 +24,12 @@
  */
 public interface OneHandedTransitionCallback {
     /**
+     * Called when one handed mode entering or exiting transition starting
+     */
+    default void onStartTransition(boolean isEntering) {
+    }
+
+    /**
      * Called when start one handed transition finished
      */
     default void onStartFinished(Rect bounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 0a148c4..c2ec1c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -232,7 +232,8 @@
         }
     }
 
-    private void onInputEvent(InputEvent ev) {
+    @VisibleForTesting
+    void onInputEvent(InputEvent ev) {
         // Don't allow resize when PiP is stashed.
         if (mPipBoundsState.isStashed()) {
             return;
@@ -366,7 +367,8 @@
         return mIsSysUiStateValid;
     }
 
-    private void onPinchResize(MotionEvent ev) {
+    @VisibleForTesting
+    void onPinchResize(MotionEvent ev) {
         int action = ev.getActionMasked();
 
         if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
@@ -411,7 +413,7 @@
             if (!mThresholdCrossed
                     && (distanceBetween(mDownSecondPoint, mLastSecondPoint) > mTouchSlop
                             || distanceBetween(mDownPoint, mLastPoint) > mTouchSlop)) {
-                mInputMonitor.pilferPointers();
+                pilferPointers();
                 mThresholdCrossed = true;
                 // Reset the down to begin resizing from this point
                 mDownPoint.set(mLastPoint);
@@ -548,6 +550,17 @@
         return mUserResizeBounds;
     }
 
+    @VisibleForTesting
+    Rect getLastResizeBounds() {
+        return mLastResizeBounds;
+    }
+
+    @VisibleForTesting
+    void pilferPointers() {
+        mInputMonitor.pilferPointers();
+    }
+
+
     @VisibleForTesting public void updateMaxSize(int maxX, int maxY) {
         mMaxSize.set(maxX, maxY);
     }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 134d00b..741773e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -22,6 +22,7 @@
 import android.system.helpers.ActivityHelper
 import android.util.Log
 import android.view.Surface
+import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.compatibility.common.util.SystemUtil
 import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -138,7 +139,7 @@
         append("$primaryApp $secondaryApp")
     }
 
-    @Presubmit
+    @FlakyTest(bugId = 186510496)
     @Test
     open fun navBarLayerIsAlwaysVisible() {
         testSpec.navBarLayerIsAlwaysVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index 58e9204..04f97c8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.provider.Settings
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -44,7 +44,6 @@
  *
  * To run this test: `atest WMShellFlickerTests:EnterSplitScreenNotSupportNonResizable`
  */
-@Postsubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -94,6 +93,7 @@
                 prevSupportNonResizableInMultiWindow)
     }
 
+    @Presubmit
     @Test
     fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index 91ca7c1..2832bb4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.provider.Settings
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -43,7 +43,6 @@
  *
  * To run this test: `atest WMShellFlickerTests:EnterSplitScreenSupportNonResizable`
  */
-@Postsubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -91,9 +90,11 @@
                 prevSupportNonResizableInMultiWindow)
     }
 
+    @Presubmit
     @Test
     fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
 
+    @Presubmit
     @Test
     fun appWindowIsVisible() {
         testSpec.assertWmEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index 968aff1..32afd19 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.provider.Settings
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -46,7 +46,6 @@
  * support non-resizable in multi window, it should trigger exit split screen.
  * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromIntentNotSupportNonResizable`
  */
-@Postsubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -95,25 +94,31 @@
                 prevSupportNonResizableInMultiWindow)
     }
 
+    @Presubmit
     @Test
     fun resizableAppLayerBecomesInvisible() =
             testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun nonResizableAppLayerBecomesVisible() =
             testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun resizableAppWindowBecomesInvisible() =
             testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun nonResizableAppWindowBecomesVisible() =
             testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
 
+    @Presubmit
     @Test
     fun onlyNonResizableAppWindowIsVisibleAtEnd() {
         testSpec.assertWmEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index 8d20673..af30758 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.provider.Settings
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -44,7 +44,6 @@
  * non-resizable in multi window, it should show the non-resizable app in split screen.
  * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromIntentSupportNonResizable`
  */
-@Postsubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -93,17 +92,21 @@
                 prevSupportNonResizableInMultiWindow)
     }
 
+    @Presubmit
     @Test
     fun nonResizableAppLayerBecomesVisible() =
             testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun nonResizableAppWindowBecomesVisible() =
             testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
 
+    @Presubmit
     @Test
     fun bothAppsWindowsAreVisibleAtEnd() {
         testSpec.assertWmEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index 4e291d9..8c62758 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.provider.Settings
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -47,7 +47,6 @@
  * not support non-resizable in multi window, it should trigger exit split screen.
  * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromRecentNotSupportNonResizable`
  */
-@Postsubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -96,25 +95,31 @@
                 prevSupportNonResizableInMultiWindow)
     }
 
+    @Presubmit
     @Test
     fun resizableAppLayerBecomesInvisible() =
             testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun nonResizableAppLayerBecomesVisible() =
             testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun resizableAppWindowBecomesInvisible() =
         testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun nonResizableAppWindowBecomesVisible() =
         testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
 
+    @Presubmit
     @Test
     fun onlyNonResizableAppWindowIsVisibleAtEnd() {
         testSpec.assertWmEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index 880dc55..5b48f8a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.provider.Settings
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -45,7 +45,6 @@
  * supports non-resizable in multi window, it should show the non-resizable app in split screen.
  * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromRecentSupportNonResizable`
  */
-@Postsubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -94,17 +93,21 @@
                 prevSupportNonResizableInMultiWindow)
     }
 
+    @Presubmit
     @Test
     fun nonResizableAppLayerBecomesVisible() =
             testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun nonResizableAppWindowBecomesVisible() =
         testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
 
+    @Presubmit
     @Test
     fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
 
+    @Presubmit
     @Test
     fun bothAppsWindowsAreVisibleAtEnd() {
         testSpec.assertWmEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index a7e1d0f..95672f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -65,7 +65,7 @@
     @Test
     fun pipLayerBecomesVisible() {
         testSpec.assertLayers {
-            this.isVisible(pipApp.launcherName)
+            this.isVisible(pipApp.windowName)
         }
     }
 
@@ -73,9 +73,11 @@
     @Test
     fun pipWindowBecomesVisible() {
         testSpec.assertWm {
-            invoke("pipWindowIsNotVisible") { !it.wmState.hasPipWindow() }
-                .then()
-                .invoke("pipWindowIsVisible") { it.wmState.hasPipWindow() }
+            invoke("pipWindowIsNotVisible") {
+                verify("Has no pip window").that(it.wmState.hasPipWindow()).isTrue()
+            }.then().invoke("pipWindowIsVisible") {
+                verify("Has pip window").that(it.wmState.hasPipWindow()).isTrue()
+            }
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 0d686f5..fb7dac3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -71,7 +71,7 @@
             }
         }
 
-    @Presubmit
+    @FlakyTest(bugId = 185400889)
     @Test
     override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
         testSpec.config.endRotation, allStates = false)
@@ -88,7 +88,7 @@
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
-    @Presubmit
+    @FlakyTest(bugId = 185400889)
     @Test
     fun appLayerRotates_StartingBounds() {
         testSpec.assertLayersStart {
@@ -97,7 +97,7 @@
         }
     }
 
-    @Presubmit
+    @FlakyTest(bugId = 185400889)
     @Test
     fun appLayerRotates_EndingBounds() {
         testSpec.assertLayersEnd {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index b0de029..2b5cd60 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -69,6 +69,7 @@
         info.configuration.windowConfiguration.setActivityType(mActivityType);
         info.token = mToken;
         info.isResizeable = true;
+        info.supportsMultiWindow = true;
         return info;
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 9a80a55..2bb7204 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -19,6 +19,8 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static junit.framework.TestCase.assertEquals;
+
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -799,6 +801,15 @@
         assertExpandedChangedTo(false);
     }
 
+    @Test
+    public void test_addToOverflow_doesntAllowDupes() {
+        assertEquals(0, mBubbleData.getOverflowBubbles().size());
+        mBubbleData.overflowBubble(Bubbles.DISMISS_AGED, mBubbleA1);
+        mBubbleData.overflowBubble(Bubbles.DISMISS_AGED, mBubbleA1);
+        mBubbleData.overflowBubble(Bubbles.DISMISS_AGED, mBubbleA1);
+        assertEquals(1, mBubbleData.getOverflowBubbles().size());
+    }
+
     private void verifyUpdateReceived() {
         verify(mListener).applyUpdate(mUpdateCaptor.capture());
         reset(mListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
new file mode 100644
index 0000000..dd10aa7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -0,0 +1,224 @@
+/*
+ * 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.wm.shell.pip.phone;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipSnapAlgorithm;
+import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipUiEventLogger;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests against {@link PipResizeGestureHandler}
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipResizeGestureHandlerTest extends ShellTestCase {
+    private static final int STEP_SIZE = 40;
+    private final MotionEvent.PointerProperties[] mPp = new MotionEvent.PointerProperties[2];
+
+    @Mock
+    private PhonePipMenuController mPhonePipMenuController;
+
+    @Mock
+    private PipTaskOrganizer mPipTaskOrganizer;
+
+    @Mock
+    private PipDismissTargetHandler mPipDismissTargetHandler;
+
+    @Mock
+    private PipTransitionController mMockPipTransitionController;
+
+    @Mock
+    private FloatingContentCoordinator mFloatingContentCoordinator;
+
+    @Mock
+    private PipUiEventLogger mPipUiEventLogger;
+
+    @Mock
+    private ShellExecutor mMainExecutor;
+
+    private PipResizeGestureHandler mPipResizeGestureHandler;
+
+    private PipBoundsState mPipBoundsState;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mPipBoundsState = new PipBoundsState(mContext);
+        final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm();
+        final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext,
+                mPipBoundsState, pipSnapAlgorithm);
+        final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
+                mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
+                mMockPipTransitionController, mFloatingContentCoordinator);
+        mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm,
+                mPipBoundsState, motionHelper, mPipTaskOrganizer, mPipDismissTargetHandler,
+                (Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController,
+                mMainExecutor) {
+            @Override
+            public void pilferPointers() {
+                // Overridden just to avoid calling into InputMonitor.
+            }
+        };
+
+        for (int i = 0; i < 2; i++) {
+            MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties();
+            pointerProperty.id = i;
+            pointerProperty.toolType = MotionEvent.TOOL_TYPE_FINGER;
+            mPp[i] = pointerProperty;
+        }
+
+        mPipResizeGestureHandler.init();
+        mPipResizeGestureHandler.onSystemUiStateChanged(true);
+    }
+
+    @Test
+    public void twoInput_triggersPinchResize_getBigger() {
+        assertTrue(mPipResizeGestureHandler.isUsingPinchToZoom());
+
+        int topLeft = 200;
+        int bottomRight = 500;
+        mPipBoundsState.setBounds(new Rect(topLeft, topLeft, bottomRight, bottomRight));
+
+        // Start inside the PiP bounds first.
+        topLeft += STEP_SIZE;
+        bottomRight -= STEP_SIZE;
+        MotionEvent downEvent =
+                obtainMotionEvent(MotionEvent.ACTION_POINTER_DOWN, topLeft, bottomRight);
+        assertTrue(mPipResizeGestureHandler.willStartResizeGesture(downEvent));
+
+        // Slowly move outward.
+        topLeft -= STEP_SIZE;
+        bottomRight += STEP_SIZE;
+        MotionEvent moveEvent1 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight);
+        mPipResizeGestureHandler.onPinchResize(moveEvent1);
+
+        // Move outward more.
+        topLeft -= STEP_SIZE;
+        bottomRight += STEP_SIZE;
+        MotionEvent moveEvent2 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight);
+        mPipResizeGestureHandler.onPinchResize(moveEvent2);
+
+        verify(mPipTaskOrganizer, times(2))
+                .scheduleUserResizePip(any(), any(), anyFloat(), any());
+
+        MotionEvent upEvent = obtainMotionEvent(MotionEvent.ACTION_UP, topLeft, bottomRight);
+        mPipResizeGestureHandler.onPinchResize(upEvent);
+
+        verify(mPipTaskOrganizer, times(1))
+                .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any());
+
+        assertTrue("The new size should be bigger than the original PiP size.",
+                mPipResizeGestureHandler.getLastResizeBounds().width()
+                        > mPipBoundsState.getBounds().width());
+    }
+
+    @Test
+    public void twoInput_triggersPinchResize_getSmaller() {
+        assertTrue(mPipResizeGestureHandler.isUsingPinchToZoom());
+
+        int topLeft = 200;
+        int bottomRight = 500;
+        mPipBoundsState.setBounds(new Rect(topLeft, topLeft, bottomRight, bottomRight));
+
+
+        topLeft += STEP_SIZE;
+        bottomRight -= STEP_SIZE;
+        MotionEvent downEvent =
+                obtainMotionEvent(MotionEvent.ACTION_POINTER_DOWN, topLeft, bottomRight);
+        assertTrue(mPipResizeGestureHandler.willStartResizeGesture(downEvent));
+
+        topLeft += STEP_SIZE;
+        bottomRight -= STEP_SIZE;
+        MotionEvent moveEvent1 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight);
+        mPipResizeGestureHandler.onPinchResize(moveEvent1);
+
+        topLeft += STEP_SIZE;
+        bottomRight -= STEP_SIZE;
+        MotionEvent moveEvent2 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight);
+        mPipResizeGestureHandler.onPinchResize(moveEvent2);
+
+        verify(mPipTaskOrganizer, times(2))
+                .scheduleUserResizePip(any(), any(), anyFloat(), any());
+
+        MotionEvent upEvent = obtainMotionEvent(MotionEvent.ACTION_UP, topLeft, bottomRight);
+        mPipResizeGestureHandler.onPinchResize(upEvent);
+
+        verify(mPipTaskOrganizer, times(1))
+                .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any());
+
+        assertTrue("The new size should be smaller than the original PiP size.",
+                mPipResizeGestureHandler.getLastResizeBounds().width()
+                        < mPipBoundsState.getBounds().width());
+    }
+
+    private MotionEvent obtainMotionEvent(int action, int topLeft, int bottomRight) {
+        final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[2];
+        for (int i = 0; i < 2; i++) {
+            MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords();
+            if (i == 0) {
+                pointerCoord.x = topLeft;
+                pointerCoord.y = topLeft;
+            } else {
+                pointerCoord.x = bottomRight;
+                pointerCoord.y = bottomRight;
+            }
+            pc[i] = pointerCoord;
+        }
+        return MotionEvent.obtain(0 /* downTime */,
+                System.currentTimeMillis(),
+                action,
+                2 /* pointerCount */,
+                mPp,
+                pc,
+                0 /* metaState */,
+                0 /* buttonState */,
+                0 /* xPrecision */,
+                0 /* yPrecision */,
+                0 /* deviceId */,
+                0 /* edgeFlags */,
+                0 /* source */,
+                0 /* flags */);
+    }
+}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 2a70f0d..cb620cc 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -70,9 +70,6 @@
 
 }  // namespace
 
-LoadedPackage::LoadedPackage() = default;
-LoadedPackage::~LoadedPackage() = default;
-
 // Precondition: The header passed in has already been verified, so reading any fields and trusting
 // the ResChunk_header is safe.
 static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 119f531..10666ad 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -94,6 +94,7 @@
   };
 
   AssetManager2();
+  explicit AssetManager2(AssetManager2&& other) = default;
 
   // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
   // are not owned by the AssetManager, and must have a longer lifetime.
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index d9225cd..3b222c5 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -153,8 +153,6 @@
   static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
                                                    package_property_t property_flags);
 
-  ~LoadedPackage();
-
   // Finds the entry with the specified type name and entry name. The names are in UTF-16 because
   // the underlying ResStringPool API expects this. For now this is acceptable, but since
   // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
@@ -275,7 +273,7 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
 
-  LoadedPackage();
+  LoadedPackage() = default;
 
   ResStringPool type_string_pool_;
   ResStringPool key_string_pool_;
diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h
index 64924f4..6fc6d64 100644
--- a/libs/androidfw/include/androidfw/MutexGuard.h
+++ b/libs/androidfw/include/androidfw/MutexGuard.h
@@ -18,6 +18,7 @@
 #define ANDROIDFW_MUTEXGUARD_H
 
 #include <mutex>
+#include <optional>
 #include <type_traits>
 
 #include "android-base/macros.h"
@@ -47,34 +48,32 @@
   static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
 
  public:
-  explicit Guarded() : guarded_() {
+  Guarded() : guarded_(std::in_place, T()) {
   }
 
-  template <typename U = T>
-  explicit Guarded(const T& guarded,
-                   typename std::enable_if<std::is_copy_constructible<U>::value>::type = void())
-      : guarded_(guarded) {
+  explicit Guarded(const T& guarded) : guarded_(std::in_place, guarded) {
   }
 
-  template <typename U = T>
-  explicit Guarded(T&& guarded,
-                   typename std::enable_if<std::is_move_constructible<U>::value>::type = void())
-      : guarded_(std::move(guarded)) {
+  explicit Guarded(T&& guarded) : guarded_(std::in_place, std::forward<T>(guarded)) {
+  }
+
+  ~Guarded() {
+    std::lock_guard<std::mutex> scoped_lock(lock_);
+    guarded_.reset();
   }
 
  private:
   friend class ScopedLock<T>;
-
   DISALLOW_COPY_AND_ASSIGN(Guarded);
 
   std::mutex lock_;
-  T guarded_;
+  std::optional<T> guarded_;
 };
 
 template <typename T>
 class ScopedLock {
  public:
-  explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) {
+  explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(*guarded.guarded_) {
   }
 
   T& operator*() {
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 087837a..7540a14 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -449,7 +449,7 @@
     sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
     Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
 
-    transaction->setCrop(surfaceControl, sourceRect);
+    transaction->setBufferCrop(surfaceControl, sourceRect);
 
     float dsdx = (destination.right - destination.left) /
             static_cast<float>(sourceRect.right - sourceRect.left);
diff --git a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
index 2a95cfe..44d5a0c 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
@@ -85,14 +85,14 @@
 key 9 {
     label:                              '9'
     base:                               '\u0669'
-    shift:                              '('
+    shift:                              ')'
     capslock:                           '9'
 }
 
 key 0 {
     label:                              '0'
     base:                               '\u0660'
-    shift:                              ')'
+    shift:                              '('
     capslock:                           '0'
 }
 
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index f5641bd..83a838f 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -26,6 +26,7 @@
 import androidx.annotation.Nullable;
 
 import com.google.android.material.appbar.CollapsingToolbarLayout;
+import com.google.android.material.resources.TextAppearanceConfig;
 
 /**
  * A base Activity that has a collapsing toolbar layout is used for the activities intending to
@@ -39,7 +40,8 @@
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
+        // Force loading font synchronously for collapsing toolbar layout
+        TextAppearanceConfig.setShouldLoadFontSynchronously(true);
         super.setContentView(R.layout.collapsing_toolbar_base_layout);
         mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
 
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index 0287b1f..4d6e1b7 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -19,9 +19,10 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeight"
-    android:paddingStart="20dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingBottom="16dp"
+    android:paddingTop="8dp"
     android:background="?android:attr/selectableItemBackground"
     android:clipToPadding="false">
 
@@ -29,8 +30,6 @@
         android:id="@android:id/title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:paddingBottom="16dp"
-        android:paddingTop="16dp"
         android:clickable="false"
         android:longClickable="false"
         android:maxLines="10"
diff --git a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
index e7eb9f4..65869b5 100644
--- a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
@@ -17,7 +17,7 @@
 <resources>
     <style name="TextAppearance.TopIntroText"
            parent="@*android:style/TextAppearance.DeviceDefault">
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
         <item name="android:textSize">14sp</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
     </style>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index fbb84fd..ccbcb89 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -256,4 +256,7 @@
     <!-- Default for Settings.Secure.ACCESSIBILITY_BUTTON_MODE -->
     <integer name="def_accessibility_button_mode">1</integer>
 
+    <!-- Default for Settings.Secure.ONE_HANDED_MODE_ACTIVATED -->
+    <bool name="def_one_handed_mode_activated">false</bool>
+
 </resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 72fb237..1cfdff8 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -173,6 +173,7 @@
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
         Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
+        Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
         Settings.Secure.ONE_HANDED_MODE_ENABLED,
         Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
         Settings.Secure.TAPS_APP_TO_EXIT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 4872aa4..36f5dba 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -262,6 +262,7 @@
         VALIDATORS.put(
                 Secure.ACCESSIBILITY_BUTTON_TARGETS,
                 ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
+        VALIDATORS.put(Secure.ONE_HANDED_MODE_ACTIVATED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.TAPS_APP_TO_EXIT, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2e90d36..941f47f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3401,7 +3401,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 201;
+            private static final int SETTINGS_VERSION = 202;
 
             private final int mUserId;
 
@@ -4959,6 +4959,22 @@
                     currentVersion = 201;
                 }
 
+                if (currentVersion == 201) {
+                    // Version 201: Set the default value for Secure Settings:
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting oneHandedModeActivated = secureSettings.getSettingLocked(
+                            Secure.ONE_HANDED_MODE_ACTIVATED);
+                    if (oneHandedModeActivated.isNull()) {
+                        final boolean defOneHandedModeActivated = getContext().getResources()
+                                .getBoolean(R.bool.def_one_handed_mode_activated);
+                        secureSettings.insertSettingLocked(
+                                Secure.ONE_HANDED_MODE_ACTIVATED,
+                                defOneHandedModeActivated ? "1" : "0", null, true,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 202;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index ca13204..2213d1c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -46,6 +46,15 @@
      * background circle/peripherals. To retrieve only the inner icon, use {@link #getIcon()}.
      */
     public abstract View getIconWithBackground();
+
+    /**
+     * Returns the {@link View} containing the icon on the right
+     *
+     * @see com.android.systemui.qs.tileimpl.QSTileViewHorizontal#sideView
+     */
+    public View getSecondaryIcon() {
+        return null;
+    }
     public abstract void init(QSTile tile);
     public abstract void onStateChanged(State state);
 
@@ -54,4 +63,8 @@
     public View getLabelContainer() {
         return null;
     }
+
+    public View getSecondaryLabel() {
+        return null;
+    }
 }
diff --git a/packages/SystemUI/res/layout/disabled_udfps_view.xml b/packages/SystemUI/res/drawable/qs_media_art_background.xml
similarity index 64%
copy from packages/SystemUI/res/layout/disabled_udfps_view.xml
copy to packages/SystemUI/res/drawable/qs_media_art_background.xml
index 13d3065..95a1870 100644
--- a/packages/SystemUI/res/layout/disabled_udfps_view.xml
+++ b/packages/SystemUI/res/drawable/qs_media_art_background.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2020 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.
@@ -12,11 +12,9 @@
   ~ 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.
+  ~ limitations under the License
   -->
-<com.android.keyguard.DisabledUdfpsView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/disabled_udfps_view"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-/>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <corners android:radius="@dimen/qs_media_album_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/qs_media_button_background.xml b/packages/SystemUI/res/drawable/qs_media_button_background.xml
new file mode 100644
index 0000000..2241abf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_media_button_background.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        android:shape="rectangle">
+        <stroke
+            android:color="?androidprv:attr/colorAccentPrimaryVariant"
+            android:width="1dp"/>
+        <corners android:radius="24dp"/>
+        <padding
+            android:left="16dp"
+            android:right="16dp"
+            android:top="8dp"
+            android:bottom="8dp" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/disabled_udfps_view.xml b/packages/SystemUI/res/drawable/qs_media_icon_background.xml
similarity index 64%
rename from packages/SystemUI/res/layout/disabled_udfps_view.xml
rename to packages/SystemUI/res/drawable/qs_media_icon_background.xml
index 13d3065..a3a2986 100644
--- a/packages/SystemUI/res/layout/disabled_udfps_view.xml
+++ b/packages/SystemUI/res/drawable/qs_media_icon_background.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2020 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.
@@ -12,11 +12,12 @@
   ~ 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.
+  ~ limitations under the License
   -->
-<com.android.keyguard.DisabledUdfpsView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/disabled_udfps_view"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-/>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid android:color="?android:attr/colorBackground" />
+    <size
+        android:width="20dp"
+        android:height="20dp" />
+</shape>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml b/packages/SystemUI/res/drawable/qs_media_seamless_background.xml
similarity index 85%
rename from packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml
rename to packages/SystemUI/res/drawable/qs_media_seamless_background.xml
index 8e37686..e71c3d3 100644
--- a/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml
+++ b/packages/SystemUI/res/drawable/qs_media_seamless_background.xml
@@ -20,6 +20,11 @@
         <shape android:shape="rectangle">
             <solid android:color="@color/media_seamless_border" />
             <corners android:radius="24dp"/>
+            <padding
+                android:left="8dp"
+                android:right="8dp"
+                android:top="4dp"
+                android:bottom="4dp" />
         </shape>
     </item>
 </ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index f4a7434..9210d05 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -89,30 +89,18 @@
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
-        android:id="@+id/recommendation_text"
-        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-        android:textColor="?android:attr/textColorSecondary"
-        android:text="@string/controls_media_title"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintBottom_toTopOf="@id/remove_text"
-        app:layout_constraintVertical_chainStyle="spread_inside"/>
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginTop="@dimen/qs_media_padding"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        android:layout_marginEnd="@dimen/qs_media_padding"
         android:id="@+id/remove_text"
         android:fontFamily="@*android:string/config_headlineFontFamily"
         android:singleLine="true"
         android:textColor="?android:attr/textColorPrimary"
         android:text="@string/controls_media_close_session"
-        app:layout_constraintTop_toBottomOf="@id/recommendation_text"
+        android:gravity="center_horizontal|top"
+        app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintBottom_toTopOf="@id/settings"/>
 
     <FrameLayout
@@ -120,8 +108,8 @@
         android:background="@drawable/qs_media_light_source"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
-        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        android:paddingBottom="@dimen/qs_media_padding"
         android:minWidth="48dp"
         android:minHeight="48dp"
         app:layout_constraintBottom_toBottomOf="parent"
@@ -132,6 +120,7 @@
             android:layout_gravity="bottom"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:background="@drawable/qs_media_button_background"
             android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
             android:textColor="?android:attr/textColorPrimary"
             android:text="@string/controls_media_settings_button" />
@@ -142,8 +131,8 @@
         android:background="@drawable/qs_media_light_source"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
-        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginEnd="8dp"
+        android:paddingBottom="@dimen/qs_media_padding"
         android:minWidth="48dp"
         android:minHeight="48dp"
         app:layout_constraintBottom_toBottomOf="parent"
@@ -153,6 +142,7 @@
             android:layout_gravity="bottom"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:background="@drawable/qs_media_button_background"
             android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
             android:textColor="?android:attr/textColorPrimary"
             android:text="@string/cancel" />
@@ -163,8 +153,8 @@
         android:background="@drawable/qs_media_light_source"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
-        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginEnd="@dimen/qs_media_padding"
+        android:paddingBottom="@dimen/qs_media_padding"
         android:minWidth="48dp"
         android:minHeight="48dp"
         app:layout_constraintBottom_toBottomOf="parent"
@@ -174,6 +164,7 @@
             android:layout_gravity="bottom"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:background="@drawable/qs_media_button_background"
             android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
             android:textColor="?android:attr/textColorPrimary"
             android:text="@string/controls_media_dismiss_button"
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index a4cf5ed..cdced5a 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -18,6 +18,7 @@
 <!-- Layout for media controls inside QSPanel carousel -->
 <com.android.systemui.util.animation.TransitionLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/qs_media_controls"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -32,8 +33,14 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        app:layout_constraintGuide_percent="0.5"
-        />
+        app:layout_constraintGuide_percent="0.6" />
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/center_horizontal_guideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_percent="0.5" />
 
     <!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
     <FrameLayout
@@ -50,7 +57,7 @@
             android:fontFamily="@*android:string/config_bodyFontFamily"
             android:textColor="?android:attr/textColorPrimary"
             android:gravity="start"
-            android:textSize="14sp" />
+            android:textSize="12sp" />
 
         <TextView
             android:id="@+id/media_total_time"
@@ -60,11 +67,11 @@
             android:fontFamily="@*android:string/config_bodyFontFamily"
             android:textColor="?android:attr/textColorPrimary"
             android:gravity="end"
-            android:textSize="14sp" />
+            android:textSize="12sp" />
     </FrameLayout>
 
     <!--  Actions must be ordered left-to-right even in RTL layout.  However, they appear in a chain
-    with the album art, and must as a group appear at the end of that chain.  This is
+    with the artist name, and must as a group appear at the end of that chain.  This is
     accomplished by having all actions appear in a LTR chain within the parent, and then biasing it
     to the right side, then this barrier is used to bound the text views.  -->
     <androidx.constraintlayout.widget.Barrier
@@ -72,10 +79,10 @@
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:orientation="vertical"
-        app:layout_constraintTop_toBottomOf="@id/header_artist"
+        app:layout_constraintTop_toBottomOf="@id/header_title"
         app:barrierDirection="start"
         app:constraint_referenced_ids="action0,action1,action2,action3,action4"
-        app:layout_constraintHorizontal_bias="0" />
+         />
 
     <ImageButton
         android:id="@+id/action0"
@@ -92,8 +99,8 @@
     <ImageButton
         android:id="@+id/action2"
         style="@style/MediaPlayer.Button"
-        android:layout_width="52dp"
-        android:layout_height="52dp" />
+        android:layout_width="48dp"
+        android:layout_height="48dp" />
 
     <ImageButton
         android:id="@+id/action3"
@@ -112,23 +119,27 @@
         android:id="@+id/album_art"
         android:layout_width="@dimen/qs_media_album_size"
         android:layout_height="@dimen/qs_media_album_size"
-        android:layout_gravity="center_vertical" />
+        android:layout_gravity="center_vertical"
+        android:background="@drawable/qs_media_art_background"
+        android:backgroundTint="?androidprv:attr/colorAccentSecondary"
+        android:clipToOutline="true" />
 
     <!-- Seamless Output Switcher -->
     <LinearLayout
         android:id="@+id/media_seamless"
         android:layout_width="0dp"
-        android:layout_height="wrap_content"
+        android:layout_height="48dp"
         android:orientation="horizontal"
-        android:gravity="center"
+        android:gravity="top|end"
+        android:paddingTop="@dimen/qs_media_padding"
+        android:paddingEnd="@dimen/qs_media_padding"
         android:background="@drawable/qs_media_light_source"
         android:forceHasOverlappingRendering="false">
         <LinearLayout
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="@dimen/qs_seamless_height"
             android:background="@drawable/qs_media_seamless_background"
             android:orientation="horizontal"
-            android:padding="6dp"
             android:contentDescription="@string/quick_settings_media_device_label">
             <ImageView
                 android:id="@+id/media_seamless_image"
@@ -138,25 +149,28 @@
                 android:tint="?android:attr/colorPrimary"
                 android:src="@*android:drawable/ic_media_seamless" />
             <TextView
-                android:visibility="gone"
                 android:id="@+id/media_seamless_text"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_vertical"
-                android:layout_marginStart="8dp"
+                android:layout_marginStart="4dp"
                 android:fontFamily="@*android:string/config_headlineFontFamily"
                 android:singleLine="true"
                 android:text="@*android:string/ext_media_seamless_action"
                 android:textColor="?android:attr/colorPrimary"
                 android:textDirection="locale"
-                android:textSize="14sp" />
+                android:textSize="12sp" />
         </LinearLayout>
     </LinearLayout>
 
     <ImageView
         android:id="@+id/media_seamless_fallback"
-        android:layout_width="@dimen/qs_seamless_icon_size"
-        android:layout_height="@dimen/qs_seamless_icon_size"
+        android:layout_width="@dimen/qs_seamless_fallback_icon_size"
+        android:layout_height="@dimen/qs_seamless_fallback_icon_size"
+        android:layout_marginTop="@dimen/qs_media_padding"
+        android:layout_marginBottom="@dimen/qs_media_padding"
+        android:layout_marginStart="@dimen/qs_center_guideline_padding"
+        android:layout_marginEnd="@dimen/qs_seamless_fallback_margin"
         android:tint="?android:attr/textColorPrimary"
         android:src="@drawable/ic_cast_connected"
         android:forceHasOverlappingRendering="false" />
@@ -170,27 +184,18 @@
         android:layout_height="wrap_content"
         android:clickable="true"
         android:maxHeight="@dimen/qs_media_enabled_seekbar_height"
-        android:paddingVertical="@dimen/qs_media_enabled_seekbar_vertical_padding"
+        android:paddingTop="@dimen/qs_media_enabled_seekbar_vertical_padding"
+        android:layout_marginTop="-22dp"
+        android:paddingBottom="0dp"
         android:thumbTint="?android:attr/textColorPrimary"
         android:progressTint="?android:attr/textColorPrimary"
-        android:progressBackgroundTint="?android:attr/colorBackground"
+        android:progressBackgroundTint="?android:attr/textColorTertiary"
         android:splitTrack="false" />
 
-    <!-- App name -->
-    <TextView
-        android:id="@+id/app_name"
-        android:textColor="?android:attr/textColorPrimary"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:singleLine="true"
-        android:fontFamily="@*android:string/config_headlineFontFamily"
-        android:textDirection="locale"
-        android:textSize="14sp" />
-
     <!-- Song name -->
     <TextView
         android:id="@+id/header_title"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
         android:singleLine="true"
@@ -200,7 +205,7 @@
     <!-- Artist name -->
     <TextView
         android:id="@+id/header_artist"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:fontFamily="@*android:string/config_headlineFontFamily"
         android:singleLine="true"
@@ -209,39 +214,28 @@
 
     <com.android.internal.widget.CachingIconView
         android:id="@+id/icon"
-        android:tint="?android:attr/textColorPrimary"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:layout_margin="6dp" />
+        android:tint="?android:attr/colorAccent"
+        android:layout_width="@dimen/qs_media_icon_size"
+        android:layout_height="@dimen/qs_media_icon_size"
+        android:background="@drawable/qs_media_icon_background"
+    />
 
-    <!-- Constraints are set here as they are the same regardless of host -->
+    <!-- Long press menu -->
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
-        android:id="@+id/media_text"
-        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-        android:textColor="?android:attr/textColorSecondary"
-        android:text="@string/controls_media_title"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintBottom_toTopOf="@id/remove_text"
-        app:layout_constraintVertical_chainStyle="spread_inside"/>
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginTop="@dimen/qs_media_padding"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        android:layout_marginEnd="@dimen/qs_media_padding"
         android:id="@+id/remove_text"
         android:fontFamily="@*android:string/config_headlineFontFamily"
         android:singleLine="true"
         android:textColor="?android:attr/textColorPrimary"
         android:text="@string/controls_media_close_session"
-        app:layout_constraintTop_toBottomOf="@id/media_text"
+        android:gravity="center_horizontal|top"
+        app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintBottom_toTopOf="@id/settings"/>
 
     <FrameLayout
@@ -249,8 +243,8 @@
         android:background="@drawable/qs_media_light_source"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
-        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        android:paddingBottom="@dimen/qs_media_padding"
         android:minWidth="48dp"
         android:minHeight="48dp"
         app:layout_constraintBottom_toBottomOf="parent"
@@ -261,6 +255,7 @@
             android:layout_gravity="bottom"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:background="@drawable/qs_media_button_background"
             android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
             android:textColor="?android:attr/textColorPrimary"
             android:text="@string/controls_media_settings_button" />
@@ -271,17 +266,19 @@
         android:background="@drawable/qs_media_light_source"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
-        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginEnd="8dp"
+        android:paddingBottom="@dimen/qs_media_padding"
         android:minWidth="48dp"
         android:minHeight="48dp"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/dismiss" >
+        app:layout_constraintEnd_toStartOf="@id/dismiss"
+        app:layout_constraintTop_toBottomOf="@id/remove_text">
 
         <TextView
             android:layout_gravity="bottom"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:background="@drawable/qs_media_button_background"
             android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
             android:textColor="?android:attr/textColorPrimary"
             android:text="@string/cancel" />
@@ -292,17 +289,19 @@
         android:background="@drawable/qs_media_light_source"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
-        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginEnd="@dimen/qs_media_padding"
+        android:paddingBottom="@dimen/qs_media_padding"
         android:minWidth="48dp"
         android:minHeight="48dp"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent">
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/remove_text">
 
         <TextView
             android:layout_gravity="bottom"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:background="@drawable/qs_media_button_background"
             android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
             android:textColor="?android:attr/textColorPrimary"
             android:text="@string/controls_media_dismiss_button"
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index c3f1113..666ec27 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -21,5 +21,6 @@
     android:layout_width="match_parent"
     android:layout_height="0dp"
     android:layout_weight="1"
-    android:clipChildren="true"
+    android:clipChildren="false"
+    android:clipToPadding="false"
     android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom" />
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 30e52e9..2cafd1b 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -36,6 +36,8 @@
         android:elevation="4dp"
         android:importantForAccessibility="no"
         android:scrollbars="none"
+        android:clipChildren="false"
+        android:clipToPadding="false"
         android:layout_weight="1">
         <com.android.systemui.qs.QSPanel
             android:id="@+id/quick_settings_panel"
@@ -43,7 +45,9 @@
             android:layout_height="wrap_content"
             android:background="@android:color/transparent"
             android:focusable="true"
-            android:accessibilityTraversalBefore="@android:id/edit">
+            android:accessibilityTraversalBefore="@android:id/edit"
+            android:clipToPadding="false"
+            android:clipChildren="false">
             <include layout="@layout/qs_footer_impl" />
             <include layout="@layout/qs_media_divider"
                 android:id="@+id/divider"/>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 5feb957..120b3f8a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -375,7 +375,7 @@
     <bool name="config_showNotificationGear">true</bool>
 
     <!-- Whether or not a background should be drawn behind a notification. -->
-    <bool name="config_drawNotificationBackground">true</bool>
+    <bool name="config_drawNotificationBackground">false</bool>
 
     <!-- Whether or the notifications can be shown and dismissed with a drag. -->
     <bool name="config_enableNotificationShadeDrag">true</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f7578cbd..b76a4ce 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -139,7 +139,7 @@
     <dimen name="notification_min_height_before_s">106dp</dimen>
 
     <!-- Height of a large notification in the status bar -->
-    <dimen name="notification_max_height">294dp</dimen>
+    <dimen name="notification_max_height">358dp</dimen>
 
     <!-- Height of a heads up notification in the status bar for legacy custom views -->
     <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
@@ -157,7 +157,7 @@
     <dimen name="notification_max_heads_up_height_increased">188dp</dimen>
 
     <!-- Side padding on the lockscreen on the side of notifications -->
-    <dimen name="notification_side_paddings">4dp</dimen>
+    <dimen name="notification_side_paddings">16dp</dimen>
 
     <!-- padding between the heads up and the statusbar -->
     <dimen name="heads_up_status_bar_padding">8dp</dimen>
@@ -179,10 +179,10 @@
     <dimen name="notification_min_interaction_height">40dp</dimen>
 
     <!-- Radius for notifications corners without adjacent notifications -->
-    <dimen name="notification_corner_radius">8dp</dimen>
+    <dimen name="notification_corner_radius">28dp</dimen>
 
     <!-- Radius for notifications corners with adjacent notifications -->
-    <dimen name="notification_corner_radius_small">0dp</dimen>
+    <dimen name="notification_corner_radius_small">4dp</dimen>
 
     <!-- the padding of the shelf icon container -->
     <dimen name="shelf_icon_container_padding">13dp</dimen>
@@ -667,7 +667,7 @@
     <dimen name="z_distance_between_notifications">0.5dp</dimen>
 
     <!-- The height of the divider between the individual notifications. -->
-    <dimen name="notification_divider_height">1dp</dimen>
+    <dimen name="notification_divider_height">2dp</dimen>
 
     <!-- The corner radius of the shadow behind the notification. -->
     <dimen name="notification_shadow_radius">0dp</dimen>
@@ -680,7 +680,7 @@
     <dimen name="notification_children_container_divider_height">0.5dp</dimen>
 
     <!-- The horizontal margin of the content in the notification shade -->
-    <dimen name="notification_shade_content_margin_horizontal">4dp</dimen>
+    <dimen name="notification_shade_content_margin_horizontal">16dp</dimen>
 
     <!-- The top margin for the notification children container in its non-expanded form. -->
     <dimen name="notification_children_container_margin_top">
@@ -1254,26 +1254,31 @@
 
     <!-- Size of media cards in the QSPanel carousel -->
     <dimen name="qs_media_padding">16dp</dimen>
-    <dimen name="qs_media_panel_outer_padding">16dp</dimen>
-    <dimen name="qs_media_album_size">120dp</dimen>
+    <dimen name="qs_media_album_size_small">72dp</dimen>
+    <dimen name="qs_media_album_size">92dp</dimen>
     <dimen name="qs_media_album_radius">14dp</dimen>
-    <dimen name="qs_media_icon_size">16dp</dimen>
+    <dimen name="qs_media_album_device_padding">26dp</dimen>
+    <dimen name="qs_media_info_margin">12dp</dimen>
+    <dimen name="qs_media_info_spacing">4dp</dimen>
+    <dimen name="qs_media_icon_size">20dp</dimen>
+    <dimen name="qs_media_icon_offset">4dp</dimen>
     <dimen name="qs_center_guideline_padding">10dp</dimen>
-    <dimen name="qs_seamless_icon_size">@dimen/qs_media_icon_size</dimen>
+    <dimen name="qs_media_action_spacing">4dp</dimen>
+    <dimen name="qs_media_action_top">8dp</dimen>
+    <dimen name="qs_seamless_height">24dp</dimen>
+    <dimen name="qs_seamless_icon_size">16dp</dimen>
     <dimen name="qs_seamless_fallback_icon_size">@dimen/qs_seamless_icon_size</dimen>
-    <dimen name="qs_seamless_fallback_end_margin">16dp</dimen>
-    <dimen name="qqs_media_spacing">16dp</dimen>
+    <dimen name="qs_seamless_fallback_margin">20dp</dimen>
     <dimen name="qs_footer_horizontal_margin">22dp</dimen>
     <dimen name="qs_media_disabled_seekbar_height">1dp</dimen>
-    <dimen name="qs_media_enabled_seekbar_height">3dp</dimen>
-    <dimen name="qs_media_enabled_seekbar_vertical_padding">15dp</dimen>
-    <dimen name="qs_media_disabled_seekbar_vertical_padding">16dp</dimen>
+    <dimen name="qs_media_enabled_seekbar_height">2dp</dimen>
+    <dimen name="qs_media_enabled_seekbar_vertical_padding">35dp</dimen>
+    <dimen name="qs_media_disabled_seekbar_vertical_padding">36dp</dimen>
 
     <!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
     <dimen name="qs_aa_media_rec_album_size">80dp</dimen>
     <dimen name="qs_aa_media_rec_icon_size">20dp</dimen>
 
-
     <!-- Window magnification -->
     <dimen name="magnification_border_drag_size">35dp</dimen>
     <dimen name="magnification_outer_border_margin">15dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 5827f4e..f4d4ab8 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -20,9 +20,9 @@
 
     <bool name="flag_notification_pipeline2">true</bool>
     <bool name="flag_notification_pipeline2_rendering">false</bool>
-    <bool name="flag_notif_updates">false</bool>
+    <bool name="flag_notif_updates">true</bool>
 
-    <bool name="flag_shade_is_opaque">false</bool>
+    <bool name="flag_shade_is_opaque">true</bool>
     <bool name="flag_monet">false</bool>
 
     <!-- b/171917882 -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a9f6946..bbb5519 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2727,7 +2727,7 @@
     <string name="accessibility_floating_button_action_move_bottom_right">Move bottom right</string>
     <!-- Action in accessibility menu to move the accessibility floating button to the edge and hide it to half. [CHAR LIMIT=30]-->
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half">Move to edge and hide</string>
-    <!-- Action in accessibility menu to move the accessibility floating button out the edge and show. [CHAR LIMIT=30]-->
+    <!-- Action in accessibility menu to move the accessibility floating button out the edge and show. [CHAR LIMIT=36]-->
     <string name="accessibility_floating_button_action_move_out_edge_and_show">Move out edge and show</string>
 
     <!-- Device Controls strings -->
@@ -2826,10 +2826,10 @@
 
     <!-- Title for media controls [CHAR_LIMIT=50] -->
     <string name="controls_media_title">Media</string>
-    <!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=NONE] -->
-    <string name="controls_media_close_session">Hide the current session.</string>
-    <!-- Explanation that controls associated with a specific media session are active [CHAR_LIMIT=NONE] -->
-    <string name="controls_media_active_session">Current session cannot be hidden.</string>
+    <!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=50] -->
+    <string name="controls_media_close_session">Hide this media session?</string>
+    <!-- Explanation that controls associated with a specific media session are active [CHAR_LIMIT=50] -->
+    <string name="controls_media_active_session">The current media session cannot be hidden.</string>
     <!-- Label for a button that will hide media controls [CHAR_LIMIT=30] -->
     <string name="controls_media_dismiss_button">Dismiss</string>
     <!-- Label for button to resume media playback [CHAR_LIMIT=NONE] -->
@@ -2971,4 +2971,7 @@
     <!-- Accessibility action for tapping on an affordance on an unlocked lock screen (ie: "Double
      tap to enter device") [CHAR LIMIT=NONE] -->
     <string name="accessibility_enter_hint">enter device</string>
+    <!-- Message shown to suggest authentication using [CHAR LIMIT=60]-->
+    <string name="keyguard_try_fingerprint">Use fingerprint to open</string>
+
 </resources>
diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml
index f83e3a1..9bd462e 100644
--- a/packages/SystemUI/res/xml/media_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_collapsed.xml
@@ -21,96 +21,82 @@
         android:id="@+id/icon"
         android:layout_width="@dimen/qs_media_icon_size"
         android:layout_height="@dimen/qs_media_icon_size"
-        android:layout_marginStart="18dp"
-        android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintStart_toEndOf="@id/album_art"
-        />
-
-    <Constraint
-        android:id="@+id/app_name"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/qs_center_guideline_padding"
-        android:layout_marginStart="8dp"
-        app:layout_constraintTop_toTopOf="@id/icon"
-        app:layout_constraintBottom_toBottomOf="@id/icon"
-        app:layout_constraintStart_toEndOf="@id/icon"
-        app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
-        app:layout_constrainedWidth="true"
-        app:layout_constraintHorizontal_bias="0"
+        android:translationY="@dimen/qs_media_icon_offset"
+        android:translationX="@dimen/qs_media_icon_offset"
+        app:layout_constraintEnd_toEndOf="@id/album_art"
+        app:layout_constraintBottom_toBottomOf="@id/album_art"
         />
 
     <Constraint
         android:id="@+id/media_seamless"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_height="48dp"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
+        app:layout_constraintBottom_toTopOf="@id/center_horizontal_guideline"
         app:layout_constraintHorizontal_chainStyle="spread_inside"
         app:layout_constraintHorizontal_bias="1"
         app:layout_constrainedWidth="true"
         app:layout_constraintWidth_min="48dp"
         app:layout_constraintHeight_min="48dp"
+        android:paddingTop="@dimen/qs_media_padding"
+        android:paddingEnd="@dimen/qs_media_padding"
         android:layout_marginStart="@dimen/qs_center_guideline_padding"
+        android:layout_marginBottom="4dp"
         />
 
     <Constraint
         android:id="@+id/media_seamless_fallback"
         android:layout_width="@dimen/qs_seamless_fallback_icon_size"
         android:layout_height="@dimen/qs_seamless_fallback_icon_size"
-        android:layout_marginEnd="@dimen/qs_seamless_fallback_end_margin"
+        android:layout_marginTop="@dimen/qs_media_padding"
+        android:layout_marginBottom="@dimen/qs_media_padding"
         android:layout_marginStart="@dimen/qs_center_guideline_padding"
+        android:layout_marginEnd="@dimen/qs_seamless_fallback_margin"
         android:alpha="0.5"
         android:visibility="gone"
         app:layout_constraintHorizontal_bias="1"
-        app:layout_constraintTop_toTopOf="@id/icon"
-        app:layout_constraintBottom_toBottomOf="@id/icon"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/center_horizontal_guideline"
         app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
         app:layout_constraintEnd_toEndOf="parent"
         />
 
     <Constraint
         android:id="@+id/album_art"
-        android:layout_width="@dimen/qs_media_album_size"
-        android:layout_height="@dimen/qs_media_album_size"
-        android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_width="@dimen/qs_media_album_size_small"
+        android:layout_height="@dimen/qs_media_album_size_small"
+        android:layout_marginTop="@dimen/qs_media_padding"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        android:layout_marginBottom="@dimen/qs_media_padding"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/media_action_barrier"
-        app:layout_constraintHorizontal_bias="0"
         />
 
     <!-- Song name -->
     <Constraint
         android:id="@+id/header_title"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/qqs_media_spacing"
-        android:layout_marginStart="@dimen/qqs_media_spacing"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginStart="@dimen/qs_media_info_margin"
+        android:layout_marginEnd="@dimen/qs_center_guideline_padding"
         app:layout_constrainedWidth="true"
-        app:layout_constraintTop_toBottomOf="@id/icon"
-        app:layout_constraintBottom_toTopOf="@id/header_artist"
+        app:layout_constraintBottom_toTopOf="@id/center_horizontal_guideline"
         app:layout_constraintStart_toEndOf="@id/album_art"
-        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
         app:layout_constraintHorizontal_bias="0"/>
 
     <!-- Artist name -->
     <Constraint
         android:id="@+id/header_artist"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginTop="3dp"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginBottom="@dimen/qqs_media_spacing"
         app:layout_constrainedWidth="true"
-        app:layout_constraintTop_toBottomOf="@id/header_title"
+        android:layout_marginTop="@dimen/qs_media_info_spacing"
+        app:layout_constraintTop_toTopOf="@id/center_horizontal_guideline"
         app:layout_constraintStart_toStartOf="@id/header_title"
-        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/media_action_barrier"
         app:layout_constraintHorizontal_bias="0"/>
 
     <!-- Seek Bar -->
@@ -130,9 +116,6 @@
         android:alpha="0.0"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginTop="35dp"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
         app:layout_constraintTop_toBottomOf="@id/album_art"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
@@ -143,15 +126,16 @@
         android:id="@+id/action0"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginStart="@dimen/qqs_media_spacing"
-        android:layout_marginEnd="4dp"
-        android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        android:layout_marginEnd="@dimen/qs_media_action_spacing"
+        android:layout_marginTop="@dimen/qs_media_action_spacing"
         android:visibility="gone"
         app:layout_constraintHorizontal_chainStyle="packed"
-        app:layout_constraintTop_toBottomOf="@id/header_artist"
+        app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
+        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toLeftOf="@id/action1"
-        app:layout_constraintHorizontal_bias="0"
+        app:layout_constraintHorizontal_bias="1"
         >
     </Constraint>
 
@@ -159,10 +143,10 @@
         android:id="@+id/action1"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginStart="4dp"
-        android:layout_marginEnd="4dp"
-        android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
-        app:layout_constraintTop_toBottomOf="@id/header_artist"
+        android:layout_marginStart="@dimen/qs_media_action_spacing"
+        android:layout_marginEnd="@dimen/qs_media_action_spacing"
+        android:layout_marginTop="@dimen/qs_media_action_spacing"
+        app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintLeft_toRightOf="@id/action0"
         app:layout_constraintRight_toLeftOf="@id/action2"
@@ -173,10 +157,10 @@
         android:id="@+id/action2"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginStart="4dp"
-        android:layout_marginEnd="4dp"
-        android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
-        app:layout_constraintTop_toBottomOf="@id/header_artist"
+        android:layout_marginStart="@dimen/qs_media_action_spacing"
+        android:layout_marginEnd="@dimen/qs_media_action_spacing"
+        android:layout_marginTop="@dimen/qs_media_action_spacing"
+        app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintLeft_toRightOf="@id/action1"
         app:layout_constraintRight_toLeftOf="@id/action3"
@@ -187,10 +171,10 @@
         android:id="@+id/action3"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginStart="4dp"
-        android:layout_marginEnd="4dp"
-        android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
-        app:layout_constraintTop_toBottomOf="@id/header_artist"
+        android:layout_marginStart="@dimen/qs_media_action_spacing"
+        android:layout_marginEnd="@dimen/qs_media_action_spacing"
+        android:layout_marginTop="@dimen/qs_media_action_spacing"
+        app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintLeft_toRightOf="@id/action2"
         app:layout_constraintRight_toLeftOf="@id/action4"
@@ -201,12 +185,12 @@
         android:id="@+id/action4"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginStart="4dp"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginStart="@dimen/qs_media_action_spacing"
+        android:layout_marginEnd="@dimen/qs_media_padding"
         android:visibility="gone"
-        android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginTop="@dimen/qs_media_action_spacing"
         app:layout_constraintHorizontal_chainStyle="packed"
-        app:layout_constraintTop_toBottomOf="@id/header_artist"
+        app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintLeft_toRightOf="@id/action3"
         app:layout_constraintRight_toRightOf="parent"
diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml
index 7c67720..fd04fa0 100644
--- a/packages/SystemUI/res/xml/media_expanded.xml
+++ b/packages/SystemUI/res/xml/media_expanded.xml
@@ -21,30 +21,16 @@
         android:id="@+id/icon"
         android:layout_width="@dimen/qs_media_icon_size"
         android:layout_height="@dimen/qs_media_icon_size"
-        android:layout_marginStart="18dp"
-        android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintStart_toEndOf="@id/album_art"
-        />
-
-    <Constraint
-        android:id="@+id/app_name"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/qs_center_guideline_padding"
-        android:layout_marginStart="8dp"
-        app:layout_constraintTop_toTopOf="@id/icon"
-        app:layout_constraintBottom_toBottomOf="@id/icon"
-        app:layout_constraintStart_toEndOf="@id/icon"
-        app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
-        app:layout_constrainedWidth="true"
-        app:layout_constraintHorizontal_bias="0"
-        />
+        android:translationY="@dimen/qs_media_icon_offset"
+        android:translationX="@dimen/qs_media_icon_offset"
+        app:layout_constraintEnd_toEndOf="@id/album_art"
+        app:layout_constraintBottom_toBottomOf="@id/album_art"
+    />
 
     <Constraint
         android:id="@+id/media_seamless"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_height="48dp"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
@@ -53,20 +39,23 @@
         app:layout_constrainedWidth="true"
         app:layout_constraintWidth_min="48dp"
         app:layout_constraintHeight_min="48dp"
+        android:paddingTop="@dimen/qs_media_padding"
+        android:paddingEnd="@dimen/qs_media_padding"
         android:layout_marginStart="@dimen/qs_center_guideline_padding"
-        />
+        android:layout_marginBottom="4dp" />
 
     <Constraint
         android:id="@+id/media_seamless_fallback"
         android:layout_width="@dimen/qs_seamless_fallback_icon_size"
         android:layout_height="@dimen/qs_seamless_fallback_icon_size"
-        android:layout_marginEnd="@dimen/qs_seamless_fallback_end_margin"
+        android:layout_marginTop="@dimen/qs_media_padding"
+        android:layout_marginBottom="16dp"
         android:layout_marginStart="@dimen/qs_center_guideline_padding"
+        android:layout_marginEnd="@dimen/qs_seamless_fallback_margin"
         android:alpha="0.5"
         android:visibility="gone"
         app:layout_constraintHorizontal_bias="1"
-        app:layout_constraintTop_toTopOf="@id/icon"
-        app:layout_constraintBottom_toBottomOf="@id/icon"
+        app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
         app:layout_constraintEnd_toEndOf="parent"
         />
@@ -75,9 +64,9 @@
         android:id="@+id/album_art"
         android:layout_width="@dimen/qs_media_album_size"
         android:layout_height="@dimen/qs_media_album_size"
-        android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginBottom="@dimen/qqs_media_spacing"
+        android:layout_marginTop="@dimen/qs_media_padding"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        android:layout_marginBottom="0dp"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         />
@@ -87,11 +76,11 @@
         android:id="@+id/header_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/qqs_media_spacing"
-        android:layout_marginStart="@dimen/qqs_media_spacing"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginTop="26dp"
+        android:layout_marginStart="@dimen/qs_media_info_margin"
+        android:layout_marginEnd="@dimen/qs_media_padding"
         app:layout_constrainedWidth="true"
-        app:layout_constraintTop_toBottomOf="@+id/icon"
+        app:layout_constraintTop_toTopOf="@+id/album_art"
         app:layout_constraintStart_toEndOf="@id/album_art"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintHorizontal_bias="0"/>
@@ -101,10 +90,10 @@
         android:id="@+id/header_artist"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginBottom="@dimen/qqs_media_spacing"
-        android:layout_marginTop="3dp"
+        android:layout_marginEnd="@dimen/qs_media_padding"
+        android:layout_marginBottom="@dimen/qs_media_padding"
         app:layout_constrainedWidth="true"
+        android:layout_marginTop="@dimen/qs_media_info_spacing"
         app:layout_constraintTop_toBottomOf="@id/header_title"
         app:layout_constraintStart_toStartOf="@id/header_title"
         app:layout_constraintEnd_toEndOf="parent"
@@ -115,7 +104,6 @@
         android:id="@+id/media_progress_bar"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginTop="35dp"
         app:layout_constraintTop_toBottomOf="@id/header_artist"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
@@ -125,10 +113,9 @@
         android:id="@+id/notification_media_progress_time"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginTop="70dp"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
-        app:layout_constraintTop_toBottomOf="@id/header_artist"
+        android:layout_marginEnd="@dimen/qs_media_padding"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         />
@@ -137,9 +124,10 @@
         android:id="@+id/action0"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginTop="5dp"
-        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginTop="@dimen/qs_media_action_top"
+        android:layout_marginStart="@dimen/qs_media_padding"
+        android:layout_marginEnd="@dimen/qs_media_action_spacing"
+        android:layout_marginBottom="@dimen/qs_media_padding"
         app:layout_constraintHorizontal_chainStyle="packed"
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toLeftOf="@id/action1"
@@ -151,12 +139,12 @@
         android:id="@+id/action1"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginStart="4dp"
-        android:layout_marginEnd="4dp"
-        android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginTop="@dimen/qs_media_action_top"
+        android:layout_marginStart="@dimen/qs_media_action_spacing"
+        android:layout_marginEnd="@dimen/qs_media_action_spacing"
+        android:layout_marginBottom="@dimen/qs_media_padding"
         app:layout_constraintLeft_toRightOf="@id/action0"
         app:layout_constraintRight_toLeftOf="@id/action2"
-        app:layout_constraintTop_toTopOf="@id/action0"
         app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
         app:layout_constraintBottom_toBottomOf="parent">
     </Constraint>
@@ -165,9 +153,10 @@
         android:id="@+id/action2"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginStart="4dp"
-        android:layout_marginEnd="4dp"
-        android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginTop="@dimen/qs_media_action_top"
+        android:layout_marginStart="@dimen/qs_media_action_spacing"
+        android:layout_marginEnd="@dimen/qs_media_action_spacing"
+        android:layout_marginBottom="@dimen/qs_media_padding"
         app:layout_constraintLeft_toRightOf="@id/action1"
         app:layout_constraintRight_toLeftOf="@id/action3"
         app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
@@ -178,9 +167,10 @@
         android:id="@+id/action3"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginStart="4dp"
-        android:layout_marginEnd="4dp"
-        android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginTop="@dimen/qs_media_action_top"
+        android:layout_marginStart="@dimen/qs_media_action_spacing"
+        android:layout_marginEnd="@dimen/qs_media_action_spacing"
+        android:layout_marginBottom="@dimen/qs_media_padding"
         app:layout_constraintLeft_toRightOf="@id/action2"
         app:layout_constraintRight_toLeftOf="@id/action4"
         app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
@@ -191,9 +181,10 @@
         android:id="@+id/action4"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginStart="4dp"
-        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
-        android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginTop="@dimen/qs_media_action_top"
+        android:layout_marginStart="@dimen/qs_media_action_spacing"
+        android:layout_marginEnd="@dimen/qs_media_padding"
+        android:layout_marginBottom="@dimen/qs_media_padding"
         app:layout_constraintHorizontal_chainStyle="packed"
         app:layout_constraintLeft_toRightOf="@id/action3"
         app:layout_constraintRight_toRightOf="parent"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 351dfd5..5708855 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -101,6 +101,9 @@
     public RemoteTransitionCompat(RecentsAnimationListener recents,
             RecentsAnimationControllerCompat controller) {
         mTransition = new IRemoteTransition.Stub() {
+            final RecentsControllerWrap mRecentsSession = new RecentsControllerWrap();
+            IBinder mToken = null;
+
             @Override
             public void startAnimation(IBinder transition, TransitionInfo info,
                     SurfaceControl.Transaction t,
@@ -110,6 +113,7 @@
                 final RemoteAnimationTargetCompat[] wallpapers =
                         RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */);
                 // TODO(b/177438007): Move this set-up logic into launcher's animation impl.
+                mToken = transition;
                 // This transition is for opening recents, so recents is on-top. We want to draw
                 // the current going-away task on top of recents, though, so move it to front
                 WindowContainerToken pausingTask = null;
@@ -127,9 +131,8 @@
                     t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);
                 }
                 t.apply();
-                final RecentsAnimationControllerCompat wrapControl =
-                        new RecentsControllerWrap(controller, info, finishedCallback, pausingTask);
-                recents.onAnimationStart(wrapControl, apps, wallpapers, new Rect(0, 0, 0, 0),
+                mRecentsSession.setup(controller, info, finishedCallback, pausingTask);
+                recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
                         new Rect());
             }
 
@@ -137,7 +140,13 @@
             public void mergeAnimation(IBinder transition, TransitionInfo info,
                     SurfaceControl.Transaction t, IBinder mergeTarget,
                     IRemoteTransitionFinishedCallback finishedCallback) {
-                // TODO: hook up merge to onTaskAppeared. Until then, just ignore incoming merges.
+                if (!mergeTarget.equals(mToken)) return;
+                if (!mRecentsSession.merge(info, t, recents)) return;
+                try {
+                    finishedCallback.onTransitionFinished(null /* wct */);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error merging transition.", e);
+                }
             }
         };
     }
@@ -159,19 +168,57 @@
      */
     @VisibleForTesting
     static class RecentsControllerWrap extends RecentsAnimationControllerCompat {
-        private final RecentsAnimationControllerCompat mWrapped;
-        private final IRemoteTransitionFinishedCallback mFinishCB;
-        private final WindowContainerToken mPausingTask;
-        private final TransitionInfo mInfo;
+        private RecentsAnimationControllerCompat mWrapped = null;
+        private IRemoteTransitionFinishedCallback mFinishCB = null;
+        private WindowContainerToken mPausingTask = null;
+        private TransitionInfo mInfo = null;
+        private SurfaceControl mOpeningLeash = null;
 
-        RecentsControllerWrap(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
+        void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
                 IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask) {
+            if (mInfo != null) {
+                throw new IllegalStateException("Trying to run a new recents animation while"
+                        + " recents is already active.");
+            }
             mWrapped = wrapped;
             mInfo = info;
             mFinishCB = finishCB;
             mPausingTask = pausingTask;
         }
 
+        @SuppressLint("NewApi")
+        boolean merge(TransitionInfo info, SurfaceControl.Transaction t,
+                RecentsAnimationListener recents) {
+            TransitionInfo.Change openingTask = null;
+            for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+                final TransitionInfo.Change change = info.getChanges().get(i);
+                if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
+                    if (change.getTaskInfo() != null) {
+                        if (openingTask != null) {
+                            Log.w(TAG, " Expecting to merge a task-open, but got >1 opening "
+                                    + "tasks");
+                        }
+                        openingTask = change;
+                    }
+                }
+            }
+            if (openingTask == null) return false;
+            mOpeningLeash = openingTask.getLeash();
+            if (openingTask.getContainer().equals(mPausingTask)) {
+                // In this case, we are "returning" to the already running app, so just consume
+                // the merge and do nothing.
+                return true;
+            }
+            // We are receiving a new opening task, so convert to onTaskAppeared.
+            final int layer = mInfo.getChanges().size() * 3;
+            t.reparent(mOpeningLeash, mInfo.getRootLeash());
+            t.setLayer(mOpeningLeash, layer);
+            t.hide(mOpeningLeash);
+            t.apply();
+            recents.onTaskAppeared(new RemoteAnimationTargetCompat(openingTask, layer));
+            return true;
+        }
+
         @Override public ThumbnailData screenshotTask(int taskId) {
             return mWrapped != null ? mWrapped.screenshotTask(taskId) : null;
         }
@@ -198,25 +245,42 @@
         @Override
         @SuppressLint("NewApi")
         public void finish(boolean toHome, boolean sendUserLeaveHint) {
+            if (mFinishCB == null) {
+                Log.e(TAG, "Duplicate call to finish", new RuntimeException());
+                return;
+            }
+            if (mWrapped != null) mWrapped.finish(toHome, sendUserLeaveHint);
             try {
-                if (!toHome && mPausingTask != null) {
+                if (!toHome && mPausingTask != null && mOpeningLeash == null) {
                     // The gesture went back to opening the app rather than continuing with
                     // recents, so end the transition by moving the app back to the top.
                     final WindowContainerTransaction wct = new WindowContainerTransaction();
                     wct.reorder(mPausingTask, true /* onTop */);
                     mFinishCB.onTransitionFinished(wct);
                 } else {
+                    if (mOpeningLeash != null) {
+                        // TODO: the launcher animation should handle this
+                        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+                        t.show(mOpeningLeash);
+                        t.setAlpha(mOpeningLeash, 1.f);
+                        t.apply();
+                    }
                     mFinishCB.onTransitionFinished(null /* wct */);
                 }
             } catch (RemoteException e) {
                 Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
             }
-            if (mWrapped != null) mWrapped.finish(toHome, sendUserLeaveHint);
             // Release surface references now. This is apparently to free GPU
             // memory while doing quick operations (eg. during CTS).
             for (int i = 0; i < mInfo.getChanges().size(); ++i) {
                 mInfo.getChanges().get(i).getLeash().release();
             }
+            // Reset all members.
+            mWrapped = null;
+            mFinishCB = null;
+            mPausingTask = null;
+            mInfo = null;
+            mOpeningLeash = null;
         }
 
         @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 60b677a..825e008 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -21,12 +21,14 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.Color;
+import android.hardware.biometrics.BiometricSourceType;
 import android.icu.text.NumberFormat;
 
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.ViewController;
 
@@ -35,13 +37,15 @@
 import java.util.TimeZone;
 
 /**
- * Controller for an AnimatableClockView.
+ * Controller for an AnimatableClockView. Instantiated by {@link KeyguardClockSwitchController}.
  */
 public class AnimatableClockController extends ViewController<AnimatableClockView> {
     private static final int FORMAT_NUMBER = 1234567890;
 
     private final StatusBarStateController mStatusBarStateController;
     private final BroadcastDispatcher mBroadcastDispatcher;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final KeyguardBypassController mBypassController;
     private final int mDozingColor = Color.WHITE;
     private int mLockScreenColor;
 
@@ -59,12 +63,16 @@
             AnimatableClockView view,
             StatusBarStateController statusBarStateController,
             BroadcastDispatcher broadcastDispatcher,
-            BatteryController batteryController) {
+            BatteryController batteryController,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            KeyguardBypassController bypassController) {
         super(view);
         mStatusBarStateController = statusBarStateController;
         mIsDozing = mStatusBarStateController.isDozing();
         mDozeAmount = mStatusBarStateController.getDozeAmount();
         mBroadcastDispatcher = broadcastDispatcher;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mBypassController = bypassController;
 
         mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
         mBurmeseLineSpacing = getContext().getResources().getFloat(
@@ -98,14 +106,29 @@
         mStatusBarStateController.addCallback(mStatusBarStateListener);
         mIsDozing = mStatusBarStateController.isDozing();
         mDozeAmount = mStatusBarStateController.getDozeAmount();
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+
         refreshTime();
         initColors();
     }
 
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+                boolean isStrongBiometric) {
+            if (biometricSourceType == BiometricSourceType.FACE
+                    && mBypassController.canBypass()) {
+                mView.animateDisappear();
+            }
+        }
+    };
+
     @Override
     protected void onViewDetached() {
         mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
+        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index 0d6f64f..dc14f82 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -27,6 +27,7 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 
 import java.util.Calendar;
 import java.util.TimeZone;
@@ -155,6 +156,21 @@
         mLockScreenColor = lockScreenColor;
     }
 
+    void animateDisappear() {
+        if (mTextAnimator == null) {
+            return;
+        }
+
+        setTextStyle(
+                0 /* weight */,
+                -1 /* text size, no update */,
+                null /* color, no update */,
+                true /* animate */,
+                KeyguardBypassController.BYPASS_FADE_DURATION /* duration */,
+                0 /* delay */,
+                null /* onAnimationEnd */);
+    }
+
     void animateCharge(boolean isDozing) {
         if (mTextAnimator == null || mTextAnimator.isRunning()) {
             // Skip charge animation if dozing animation is already playing.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 5559a18..e92cae4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -24,8 +24,16 @@
 import android.app.smartspace.SmartspaceConfig;
 import android.app.smartspace.SmartspaceManager;
 import android.app.smartspace.SmartspaceSession;
+import android.app.smartspace.SmartspaceTarget;
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
 import android.view.View;
@@ -47,15 +55,18 @@
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.ViewController;
+import com.android.systemui.util.settings.SecureSettings;
 
 import java.util.Locale;
 import java.util.TimeZone;
@@ -90,10 +101,19 @@
 
     private SmartspaceSession mSmartspaceSession;
     private SmartspaceSession.OnTargetsAvailableListener mSmartspaceCallback;
-    private int mWallpaperTextColor;
     private ConfigurationController mConfigurationController;
     private ActivityStarter mActivityStarter;
     private FalsingManager mFalsingManager;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final KeyguardBypassController mBypassController;
+    private Handler mHandler;
+    private UserTracker mUserTracker;
+    private SecureSettings mSecureSettings;
+    private ContentObserver mSettingsObserver;
+    private boolean mShowSensitiveContentForCurrentUser;
+    private boolean mShowSensitiveContentForManagedUser;
+    private UserHandle mManagedUserHandle;
+    private UserTracker.Callback mUserTrackerCallback;
 
     /**
      * Listener for changes to the color palette.
@@ -147,7 +167,12 @@
             ConfigurationController configurationController,
             SystemUIFactory systemUIFactory,
             ActivityStarter activityStarter,
-            FalsingManager falsingManager) {
+            FalsingManager falsingManager,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            KeyguardBypassController bypassController,
+            @Main Handler handler,
+            UserTracker userTracker,
+            SecureSettings secureSettings) {
         super(keyguardClockSwitch);
         mStatusBarStateController = statusBarStateController;
         mColorExtractor = colorExtractor;
@@ -162,6 +187,11 @@
         mSystemUIFactory = systemUIFactory;
         mActivityStarter = activityStarter;
         mFalsingManager = falsingManager;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mBypassController = bypassController;
+        mHandler = handler;
+        mUserTracker = userTracker;
+        mSecureSettings = secureSettings;
     }
 
     /**
@@ -185,19 +215,23 @@
         mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
 
         mClockViewController =
-            new AnimatableClockController(
-                mView.findViewById(R.id.animatable_clock_view),
-                mStatusBarStateController,
-                mBroadcastDispatcher,
-                mBatteryController);
+                new AnimatableClockController(
+                        mView.findViewById(R.id.animatable_clock_view),
+                        mStatusBarStateController,
+                        mBroadcastDispatcher,
+                        mBatteryController,
+                        mKeyguardUpdateMonitor,
+                        mBypassController);
         mClockViewController.init();
 
         mLargeClockViewController =
-            new AnimatableClockController(
-                mView.findViewById(R.id.animatable_clock_view_large),
-                mStatusBarStateController,
-                mBroadcastDispatcher,
-                mBatteryController);
+                new AnimatableClockController(
+                        mView.findViewById(R.id.animatable_clock_view_large),
+                        mStatusBarStateController,
+                        mBroadcastDispatcher,
+                        mBatteryController,
+                        mKeyguardUpdateMonitor,
+                        mBypassController);
         mLargeClockViewController.init();
 
         mStatusBarStateController.addCallback(mStatusBarStateListener);
@@ -248,15 +282,74 @@
             mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class)
                     .createSmartspaceSession(
                             new SmartspaceConfig.Builder(getContext(), "lockscreen").build());
-            mSmartspaceCallback = targets -> smartspaceDataPlugin.onTargetsAvailable(targets);
+            mSmartspaceCallback = targets -> {
+                targets.removeIf(this::filterSmartspaceTarget);
+                smartspaceDataPlugin.onTargetsAvailable(targets);
+            };
             mSmartspaceSession.addOnTargetsAvailableListener(mUiExecutor, mSmartspaceCallback);
-            mSmartspaceSession.requestSmartspaceUpdate();
+            mSettingsObserver = new ContentObserver(mHandler) {
+                @Override
+                public void onChange(boolean selfChange, Uri uri) {
+                    reloadSmartspace();
+                }
+            };
+
+            mUserTrackerCallback = new UserTracker.Callback() {
+                public void onUserChanged(int newUser, Context userContext) {
+                    reloadSmartspace();
+                }
+            };
+            mUserTracker.addCallback(mUserTrackerCallback, mUiExecutor);
+
+            getContext().getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(
+                            Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+                    true, mSettingsObserver, UserHandle.USER_ALL);
+            reloadSmartspace();
         }
 
         float dozeAmount = mStatusBarStateController.getDozeAmount();
         mStatusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
     }
 
+    @VisibleForTesting
+    boolean filterSmartspaceTarget(SmartspaceTarget t) {
+        if (!t.isSensitive()) return false;
+
+        if (t.getUserHandle().equals(mUserTracker.getUserHandle())) {
+            return !mShowSensitiveContentForCurrentUser;
+        }
+        if (t.getUserHandle().equals(mManagedUserHandle)) {
+            return !mShowSensitiveContentForManagedUser;
+        }
+
+        return false;
+    }
+
+    private void reloadSmartspace() {
+        mManagedUserHandle = getWorkProfileUser();
+        final String setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
+
+        mShowSensitiveContentForCurrentUser =
+                mSecureSettings.getIntForUser(setting, 0, mUserTracker.getUserId()) == 1;
+        if (mManagedUserHandle != null) {
+            int id = mManagedUserHandle.getIdentifier();
+            mShowSensitiveContentForManagedUser =
+                    mSecureSettings.getIntForUser(setting, 0, id) == 1;
+        }
+
+        mSmartspaceSession.requestSmartspaceUpdate();
+    }
+
+    private UserHandle getWorkProfileUser() {
+        for (UserInfo userInfo : mUserTracker.getUserProfiles()) {
+            if (userInfo.isManagedProfile()) {
+                return userInfo.getUserHandle();
+            }
+        }
+        return null;
+    }
+
     private void updateWallpaperColor() {
         if (mSmartspaceView != null) {
             int color = Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColor);
@@ -279,6 +372,14 @@
         }
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
         mConfigurationController.removeCallback(mConfigurationListener);
+
+        if (mSettingsObserver != null) {
+            getContext().getContentResolver().unregisterContentObserver(mSettingsObserver);
+        }
+
+        if (mUserTrackerCallback != null) {
+            mUserTracker.removeCallback(mUserTrackerCallback);
+        }
     }
 
     /**
@@ -427,4 +528,9 @@
     ConfigurationController.ConfigurationListener getConfigurationListener() {
         return mConfigurationListener;
     }
+
+    @VisibleForTesting
+    ContentObserver getSettingsObserver() {
+        return mSettingsObserver;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 9b0ae6b..b5f2ab2 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -83,6 +83,7 @@
     private boolean mQsExpanded;
     private int mStatusBarState;
     private boolean mIsKeyguardShowing;
+    private boolean mUserUnlockedWithBiometric;
 
     private boolean mShowButton;
     private boolean mShowUnlockIcon;
@@ -155,7 +156,8 @@
             mView.setLocation(new PointF(props[0], props[1]), props[2]);
         }
 
-        mIsKeyguardShowing = mKeyguardViewController.isShowing();
+        updateKeyguardShowing();
+        mUserUnlockedWithBiometric = false;
         mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
         mIsDozing = mStatusBarStateController.isDozing();
         mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
@@ -216,7 +218,8 @@
         }
 
         // these three states are mutually exclusive:
-        mShowButton = mUdfpsEnrolled && !mCanDismissLockScreen && !mRunningFPS && isLockScreen();
+        mShowButton = mUdfpsEnrolled && !mCanDismissLockScreen && !mRunningFPS
+                && !mUserUnlockedWithBiometric && isLockScreen();
         mShowUnlockIcon = mFaceAuthEnrolled & mCanDismissLockScreen && isLockScreen();
         mShowLockIcon = !mUdfpsEnrolled && !mCanDismissLockScreen && isLockScreen()
             && mFaceAuthEnrolled;
@@ -289,6 +292,11 @@
         }
     }
 
+    private void updateKeyguardShowing() {
+        mIsKeyguardShowing = mKeyguardStateController.isShowing()
+                && !mKeyguardStateController.isKeyguardGoingAway();
+    }
+
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("  mShowBouncerButton: " + mShowButton);
@@ -300,6 +308,7 @@
         pw.println("  mIsKeyguardShowing: " + mIsKeyguardShowing);
         pw.println("  mIsDozing: " + mIsDozing);
         pw.println("  mIsBouncerShowing: " + mIsBouncerShowing);
+        pw.println("  mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
         pw.println("  mRunningFPS: " + mRunningFPS);
         pw.println("  mCanDismissLockScreen: " + mCanDismissLockScreen);
         pw.println("  mStatusBarState: " + StatusBarState.toShortString(mStatusBarState));
@@ -326,18 +335,20 @@
                 @Override
                 public void onKeyguardBouncerChanged(boolean bouncer) {
                     mIsBouncerShowing = bouncer;
-                    mIsKeyguardShowing = mKeyguardStateController.isShowing();
                     updateVisibility();
                 }
 
                 @Override
                 public void onBiometricRunningStateChanged(boolean running,
                         BiometricSourceType biometricSourceType) {
+                    mUserUnlockedWithBiometric =
+                            mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
+                                    KeyguardUpdateMonitor.getCurrentUser());
+
                     if (biometricSourceType == FINGERPRINT) {
                         mRunningFPS = running;
+                        updateVisibility();
                     }
-
-                    updateVisibility();
                 }
             };
 
@@ -346,15 +357,23 @@
         @Override
         public void onUnlockedChanged() {
             mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
+            updateKeyguardShowing();
             updateVisibility();
         }
+
         @Override
         public void onKeyguardShowingChanged() {
-            mIsKeyguardShowing = mKeyguardStateController.isShowing();
+            updateKeyguardShowing();
             mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
             mFaceAuthEnrolled = mKeyguardUpdateMonitor.isFaceEnrolled();
             updateVisibility();
         }
+
+        @Override
+        public void onKeyguardFadingAwayChanged() {
+            updateKeyguardShowing();
+            updateVisibility();
+        }
     };
 
     private final AccessibilityManager.TouchExplorationStateChangeListener
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index cbfdce5..351ae82 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -54,6 +54,7 @@
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarOverlayController;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.PluginDependencyProvider;
@@ -356,6 +357,7 @@
     @Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager;
     @Inject Lazy<SystemStatusAnimationScheduler> mSystemStatusAnimationSchedulerLazy;
     @Inject Lazy<PrivacyDotViewController> mPrivacyDotViewControllerLazy;
+    @Inject Lazy<EdgeBackGestureHandler> mEdgeBackGestureHandler;
 
     @Inject
     public Dependency() {
@@ -568,6 +570,7 @@
         mProviders.put(SystemStatusAnimationScheduler.class,
                 mSystemStatusAnimationSchedulerLazy::get);
         mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
+        mProviders.put(EdgeBackGestureHandler.class, mEdgeBackGestureHandler::get);
 
         Dependency.setInstance(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 2808450..58881d9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.classifier;
 
+import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
+import static com.android.systemui.classifier.Classifier.GENERIC;
 import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS;
 import static com.android.systemui.classifier.FalsingModule.BRIGHT_LINE_GESTURE_CLASSIFERS;
 
@@ -196,7 +198,7 @@
     @Override
     public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
         mPriorInteractionType = interactionType;
-        if (skipFalsing()) {
+        if (skipFalsing(interactionType)) {
             mPriorResults = getPassedResult(1);
             logDebug("Skipped falsing");
             return false;
@@ -229,7 +231,7 @@
 
     @Override
     public boolean isFalseTap(@Penalty int penalty) {
-        if (skipFalsing()) {
+        if (skipFalsing(GENERIC)) {
             mPriorResults = getPassedResult(1);
             logDebug("Skipped falsing");
             return false;
@@ -291,7 +293,7 @@
 
     @Override
     public boolean isFalseDoubleTap() {
-        if (skipFalsing()) {
+        if (skipFalsing(GENERIC)) {
             mPriorResults = getPassedResult(1);
             logDebug("Skipped falsing");
             return false;
@@ -306,8 +308,9 @@
         return result.isFalse();
     }
 
-    private boolean skipFalsing() {
-        return !mKeyguardStateController.isShowing()
+    private boolean skipFalsing(@Classifier.InteractionType  int interactionType) {
+        return interactionType == BACK_GESTURE
+                || !mKeyguardStateController.isShowing()
                 || mTestHarness
                 || mDataProvider.isJustUnlockedWithFace()
                 || mDockManager.isDocked();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 6f70672..ffdcff2 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -43,6 +43,7 @@
     public static final int UDFPS_AUTHENTICATION = 13;
     public static final int DISABLED_UDFPS_AFFORDANCE = 14;
     public static final int QS_SWIPE = 15;
+    public static final int BACK_GESTURE = 16;
 
     @IntDef({
             QUICK_SETTINGS,
@@ -61,7 +62,8 @@
             BRIGHTNESS_SLIDER,
             UDFPS_AUTHENTICATION,
             DISABLED_UDFPS_AFFORDANCE,
-            QS_SWIPE
+            QS_SWIPE,
+            BACK_GESTURE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InteractionType {}
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 2ea139e..b668e88 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -54,6 +54,14 @@
                 }
             }
         })
+
+        // First let's set the desired state that we want for this host
+        mediaHost.expansion = MediaHostState.COLLAPSED
+        mediaHost.showsOnlyActiveMedia = true
+        mediaHost.falsingProtectionNeeded = true
+
+        // Let's now initialize this view, which also creates the host view for us.
+        mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN)
     }
 
     var visibilityChangedListener: ((Boolean) -> Unit)? = null
@@ -71,13 +79,7 @@
      */
     fun attachSinglePaneContainer(mediaView: MediaHeaderView?) {
         singlePaneContainer = mediaView
-        // First let's set the desired state that we want for this host
-        mediaHost.expansion = MediaHostState.COLLAPSED
-        mediaHost.showsOnlyActiveMedia = true
-        mediaHost.falsingProtectionNeeded = true
 
-        // Let's now initialize this view, which also creates the host view for us.
-        mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN)
         // Required to show it for the first time, afterwards visibility is managed automatically
         mediaHost.visible = true
         mediaHost.addVisibilityChangeListener { visible ->
@@ -133,6 +135,10 @@
         }
         // might be called a few times for the same view, no need to add hostView again
         if (activeContainer?.childCount == 0) {
+            // Detach the hostView from its parent view if exists
+            mediaHost.hostView.parent ?.let {
+                (it as? ViewGroup)?.removeView(mediaHost.hostView)
+            }
             activeContainer.addView(mediaHost.hostView)
         }
         setVisibility(activeContainer, View.VISIBLE)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index c3c617c..d87142f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -1,6 +1,5 @@
 package com.android.systemui.media
 
-import android.animation.ArgbEvaluator
 import android.app.smartspace.SmartspaceTarget
 import android.content.Context
 import android.content.Intent
@@ -116,8 +115,7 @@
     private var needsReordering: Boolean = false
     private var keysNeedRemoval = mutableSetOf<String>()
     private var bgColor = getBackgroundColor()
-    private var fgColor = com.android.settingslib.Utils.getColorAttr(context,
-            com.android.internal.R.attr.textColorPrimary).defaultColor
+    private var fgColor = getForegroundColor()
     private var isRtl: Boolean = false
         set(value) {
             if (value != field) {
@@ -359,9 +357,7 @@
 
     private fun recreatePlayers() {
         bgColor = getBackgroundColor()
-
-        fgColor = com.android.settingslib.Utils.getColorAttr(context,
-                com.android.internal.R.attr.textColorPrimary).defaultColor
+        fgColor = getForegroundColor()
         pageIndicator.tintList = ColorStateList.valueOf(fgColor)
 
         MediaPlayerData.mediaData().forEach { (key, data) ->
@@ -371,12 +367,12 @@
     }
 
     private fun getBackgroundColor(): Int {
-        val themeAccent = com.android.settingslib.Utils.getColorAttr(context,
-                com.android.internal.R.attr.colorAccent).defaultColor
-        val themeBackground = com.android.settingslib.Utils.getColorAttr(context,
-                com.android.internal.R.attr.colorBackground).defaultColor
-        // Simulate transparency - cannot be actually transparent because of lockscreen
-        return ArgbEvaluator().evaluate(0.25f, themeBackground, themeAccent) as Int
+        return context.getColor(android.R.color.system_accent2_50)
+    }
+
+    private fun getForegroundColor(): Int {
+        return com.android.settingslib.Utils.getColorAttr(context,
+                com.android.internal.R.attr.textColorPrimary).defaultColor
     }
 
     private fun updatePageIndicator() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 473db9f..96ae2cd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -25,7 +25,6 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
-import android.graphics.Outline;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
@@ -33,10 +32,8 @@
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
-import android.os.SystemProperties;
 import android.util.Log;
 import android.view.View;
-import android.view.ViewOutlineProvider;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -46,7 +43,6 @@
 import androidx.annotation.UiThread;
 import androidx.constraintlayout.widget.ConstraintSet;
 
-import com.android.settingslib.Utils;
 import com.android.settingslib.widget.AdaptiveIcon;
 import com.android.systemui.R;
 import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -75,11 +71,6 @@
     private static final String EXTRAS_MEDIA_SOURCE_PACKAGE_NAME = "package_name";
     private static final int MEDIA_RECOMMENDATION_MAX_NUM = 4;
 
-    private final boolean mShowAppName = SystemProperties.getBoolean(
-            "persist.sysui.qs_media_show_app_name", false);
-    private final boolean mShowDeviceName = SystemProperties.getBoolean(
-            "persist.sysui.qs_media_show_device_name", false);
-
     private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS);
 
     // Button IDs for QS controls
@@ -106,10 +97,8 @@
     private KeyguardDismissUtil mKeyguardDismissUtil;
     private Lazy<MediaDataManager> mMediaDataManagerLazy;
     private int mBackgroundColor;
+    private int mDevicePadding;
     private int mAlbumArtSize;
-    private int mAlbumArtRadius;
-    // This will provide the corners for the album art.
-    private final ViewOutlineProvider mViewOutlineProvider;
     private final MediaOutputDialogFactory mMediaOutputDialogFactory;
 
     /**
@@ -133,13 +122,6 @@
         mKeyguardDismissUtil = keyguardDismissUtil;
         mMediaOutputDialogFactory = mediaOutputDialogFactory;
         loadDimens();
-
-        mViewOutlineProvider = new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                outline.setRoundRect(0, 0, mAlbumArtSize, mAlbumArtSize, mAlbumArtRadius);
-            }
-        };
     }
 
     public void onDestroy() {
@@ -151,9 +133,9 @@
     }
 
     private void loadDimens() {
-        mAlbumArtRadius = mContext.getResources().getDimensionPixelSize(
-                Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
         mAlbumArtSize = mContext.getResources().getDimensionPixelSize(R.dimen.qs_media_album_size);
+        mDevicePadding = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.qs_media_album_device_padding);
     }
 
     /**
@@ -211,10 +193,6 @@
         mPlayerViewHolder = vh;
         TransitionLayout player = vh.getPlayer();
 
-        ImageView albumView = vh.getAlbumView();
-        albumView.setOutlineProvider(mViewOutlineProvider);
-        albumView.setClipToOutline(true);
-
         mSeekBarObserver = new SeekBarObserver(vh);
         mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver);
         mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
@@ -297,10 +275,19 @@
         boolean hasArtwork = data.getArtwork() != null;
         if (hasArtwork) {
             Drawable artwork = scaleDrawable(data.getArtwork());
+            albumView.setPadding(0, 0, 0, 0);
             albumView.setImageDrawable(artwork);
+        } else {
+            Drawable deviceIcon;
+            if (data.getDevice() != null && data.getDevice().getIcon() != null) {
+                deviceIcon = data.getDevice().getIcon().getConstantState().newDrawable().mutate();
+            } else {
+                deviceIcon = getContext().getDrawable(R.drawable.ic_headphone);
+            }
+            deviceIcon.setTintList(ColorStateList.valueOf(mBackgroundColor));
+            albumView.setPadding(mDevicePadding, mDevicePadding, mDevicePadding, mDevicePadding);
+            albumView.setImageDrawable(deviceIcon);
         }
-        setVisibleAndAlpha(collapsedSet, R.id.album_art, hasArtwork);
-        setVisibleAndAlpha(expandedSet, R.id.album_art, hasArtwork);
 
         // App icon
         ImageView appIcon = mPlayerViewHolder.getAppIcon();
@@ -315,13 +302,6 @@
         TextView titleText = mPlayerViewHolder.getTitleText();
         titleText.setText(data.getSong());
 
-        // App title
-        TextView appName = mPlayerViewHolder.getAppName();
-        appName.setText(data.getApp());
-        appName.setVisibility(mShowAppName ? View.VISIBLE : View.GONE);
-        setVisibleAndAlpha(collapsedSet, R.id.app_name, mShowAppName);
-        setVisibleAndAlpha(expandedSet, R.id.app_name, mShowAppName);
-
         // Artist name
         TextView artistText = mPlayerViewHolder.getArtistText();
         artistText.setText(data.getArtist());
@@ -333,10 +313,6 @@
         mPlayerViewHolder.getSeamless().setOnClickListener(v -> {
             mMediaOutputDialogFactory.create(data.getPackageName(), true);
         });
-        TextView mDeviceName = mPlayerViewHolder.getSeamlessText();
-        mDeviceName.setVisibility(mShowDeviceName ? View.VISIBLE : View.GONE);
-        setVisibleAndAlpha(collapsedSet, R.id.media_seamless_text, mShowDeviceName);
-        setVisibleAndAlpha(expandedSet, R.id.media_seamless_text, mShowDeviceName);
 
         ImageView iconView = mPlayerViewHolder.getSeamlessIcon();
         TextView deviceName = mPlayerViewHolder.getSeamlessText();
@@ -406,8 +382,12 @@
 
         // Hide any unused buttons
         for (; i < ACTION_IDS.length; i++) {
-            setVisibleAndAlpha(expandedSet, ACTION_IDS[i], false /*visible */);
             setVisibleAndAlpha(collapsedSet, ACTION_IDS[i], false /*visible */);
+            setVisibleAndAlpha(expandedSet, ACTION_IDS[i], false /* visible */);
+        }
+        // If no expanded buttons, set the first view as INVISIBLE so z remains constant
+        if (actionIcons.size() == 0) {
+            expandedSet.setVisibility(ACTION_IDS[0], ConstraintSet.INVISIBLE);
         }
 
         // Seek Bar
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 6ef29d6..6f0c887 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -31,7 +31,7 @@
      */
     val app: String?,
     /**
-     * Icon shown on player, close to app name.
+     * App icon shown on player.
      */
     val appIcon: Icon?,
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 2bd7729..9163044 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.media
 
+import android.app.smartspace.SmartspaceAction
 import android.app.smartspace.SmartspaceTarget
 import android.os.SystemProperties
 import android.util.Log
@@ -121,6 +122,12 @@
         }
 
         // If no recent media, continue with smartspace update
+        if (isMediaRecommendationEmpty(data)) {
+            Log.d(TAG, "Empty media recommendations. Skip showing the card")
+            return
+        }
+
+        // Proceed only if the Smartspace recommendation is not empty.
         listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data) }
     }
 
@@ -214,4 +221,10 @@
      * Remove a listener that was registered with addListener
      */
     fun removeListener(listener: MediaDataManager.Listener) = _listeners.remove(listener)
+
+    /** Check if the Smartspace sends an empty update. */
+    private fun isMediaRecommendationEmpty(data: SmartspaceTarget): Boolean {
+        val mediaRecommendationList: List<SmartspaceAction> = data.getIconGrid()
+        return mediaRecommendationList == null || mediaRecommendationList.isEmpty()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index a070861..138c422 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -115,12 +115,15 @@
         // UI surface label for subscribing Smartspace updates.
         @JvmField
         val SMARTSPACE_UI_SURFACE_LABEL = "media_data_manager"
+
+        // Maximum number of actions allowed in compact view
+        @JvmField
+        val MAX_COMPACT_ACTIONS = 3
     }
 
     private val themeText = com.android.settingslib.Utils.getColorAttr(context,
             com.android.internal.R.attr.textColorPrimary).defaultColor
-    private val bgColor = com.android.settingslib.Utils.getColorAttr(context,
-            com.android.internal.R.attr.colorBackground).defaultColor
+    private val bgColor = context.getColor(android.R.color.system_accent2_50)
 
     // Internal listeners are part of the internal pipeline. External listeners (those registered
     // with [MediaDeviceManager.addListener]) receive events after they have propagated through
@@ -549,8 +552,12 @@
         // Control buttons
         val actionIcons: MutableList<MediaAction> = ArrayList()
         val actions = notif.actions
-        val actionsToShowCollapsed = notif.extras.getIntArray(
+        var actionsToShowCollapsed = notif.extras.getIntArray(
                 Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList() ?: mutableListOf<Int>()
+        if (actionsToShowCollapsed.size > MAX_COMPACT_ACTIONS) {
+            Log.e(TAG, "Too many compact actions for $key, limiting to first $MAX_COMPACT_ACTIONS")
+            actionsToShowCollapsed = actionsToShowCollapsed.subList(0, MAX_COMPACT_ACTIONS)
+        }
         // TODO: b/153736623 look into creating actions when this isn't a media style notification
 
         if (actions != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 2347481..28e4640 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -25,6 +25,8 @@
 
     private val tmpLocationOnScreen: IntArray = intArrayOf(0, 0)
 
+    private var inited: Boolean = false
+
     /**
      * Get the current bounds on the screen. This makes sure the state is fresh and up to date
      */
@@ -84,6 +86,11 @@
      *                 transitions.
      */
     fun init(@MediaLocation location: Int) {
+        if (inited) {
+            return
+        }
+        inited = true
+
         this.location = location
         hostView = mediaHierarchyManager.register(this)
         hostView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index 16327bd..08d8726 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -35,7 +35,6 @@
 
     // Player information
     val appIcon = itemView.requireViewById<ImageView>(R.id.icon)
-    val appName = itemView.requireViewById<TextView>(R.id.app_name)
     val albumView = itemView.requireViewById<ImageView>(R.id.album_art)
     val titleText = itemView.requireViewById<TextView>(R.id.header_title)
     val artistText = itemView.requireViewById<TextView>(R.id.header_artist)
@@ -120,6 +119,7 @@
                 R.id.header_title,
                 R.id.header_artist,
                 R.id.media_seamless,
+                R.id.media_seamless_fallback,
                 R.id.notification_media_progress_time,
                 R.id.media_progress_bar,
                 R.id.action0,
@@ -130,7 +130,6 @@
                 R.id.icon
         )
         val gutsIds = setOf(
-                R.id.media_text,
                 R.id.remove_text,
                 R.id.cancel,
                 R.id.dismiss,
diff --git a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
index 19c83bc..02150c4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
@@ -20,7 +20,6 @@
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
-import android.widget.TextView
 import androidx.annotation.IntegerRes
 import com.android.systemui.R
 import com.android.systemui.util.animation.TransitionLayout
@@ -56,7 +55,6 @@
     val cancel = itemView.requireViewById<View>(R.id.cancel)
     val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
     val dismissLabel = dismiss.getChildAt(0)
-    val recommendationText = itemView.requireViewById<TextView>(R.id.recommendation_text)
     val settings = itemView.requireViewById<View>(R.id.settings)
 
     init {
@@ -105,7 +103,6 @@
 
         // Res Ids for the components on the guts panel.
         val gutsIds = setOf(
-            R.id.recommendation_text,
             R.id.remove_text,
             R.id.cancel,
             R.id.dismiss,
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index d789501..f17ad6f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -78,6 +78,7 @@
     fun setVerticalPadding(padding: Int) {
         val leftPadding = holder.seekBar.paddingLeft
         val rightPadding = holder.seekBar.paddingRight
-        holder.seekBar.setPadding(leftPadding, padding, rightPadding, padding)
+        val bottomPadding = holder.seekBar.paddingBottom
+        holder.seekBar.setPadding(leftPadding, padding, rightPadding, bottomPadding)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 01c80f6..0ed4d86 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -83,7 +83,6 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsOnboarding;
-import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
@@ -113,7 +112,6 @@
     private final RegionSamplingHelper mRegionSamplingHelper;
     private final int mNavColorSampleMargin;
     private final SysUiState mSysUiFlagContainer;
-    private final PluginManager mPluginManager;
 
     View mCurrentView = null;
     private View mVertical;
@@ -316,7 +314,6 @@
         boolean isGesturalMode = isGesturalMode(mNavBarMode);
 
         mSysUiFlagContainer = Dependency.get(SysUiState.class);
-        mPluginManager = Dependency.get(PluginManager.class);
         // Set up the context group of buttons
         mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
         final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
@@ -366,8 +363,8 @@
 
         mNavColorSampleMargin = getResources()
                         .getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
-        mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService,
-                mSysUiFlagContainer, mPluginManager, this::updateStates);
+        mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.class);
+        mEdgeBackGestureHandler.setStateChangeCallback(this::updateStates);
         mRegionSamplingHelper = new RegionSamplingHelper(this,
                 new RegionSamplingHelper.SamplingCallback() {
                     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 806ea4f..fc615de 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.navigationbar.gestural;
 
+import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
+
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -27,8 +29,6 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
 import android.hardware.input.InputManager;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -41,6 +41,7 @@
 import android.view.Choreographer;
 import android.view.Display;
 import android.view.ISystemGestureExclusionListener;
+import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputMonitor;
@@ -50,18 +51,19 @@
 import android.view.Surface;
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
 import android.view.WindowMetrics;
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.NavigationEdgeBackPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.recents.OverviewProxyService;
@@ -85,9 +87,12 @@
 import java.util.Map;
 import java.util.concurrent.Executor;
 
+import javax.inject.Inject;
+
 /**
  * Utility class to handle edge swipes for back gesture
  */
+@SysUISingleton
 public class EdgeBackGestureHandler extends CurrentUserTracker
         implements PluginListener<NavigationEdgeBackPlugin>, ProtoTraceable<SystemUiTraceProto> {
 
@@ -164,9 +169,15 @@
     private final Context mContext;
     private final OverviewProxyService mOverviewProxyService;
     private final SysUiState mSysUiState;
-    private final Runnable mStateChangeCallback;
+    private Runnable mStateChangeCallback;
 
     private final PluginManager mPluginManager;
+    private final ProtoTracer mProtoTracer;
+    private final NavigationModeController mNavigationModeController;
+    private final ViewConfiguration mViewConfiguration;
+    private final WindowManager mWindowManager;
+    private final IWindowManager mWindowManagerService;
+    private final FalsingManager mFalsingManager;
     // Activities which should not trigger Back gesture.
     private final List<ComponentName> mGestureBlockingActivities = new ArrayList<>();
 
@@ -237,6 +248,9 @@
             new NavigationEdgeBackPlugin.BackCallback() {
                 @Override
                 public void triggerBack() {
+                    // Notify FalsingManager that an intentional gesture has occurred.
+                    // TODO(b/186519446): use a different method than isFalseTouch
+                    mFalsingManager.isFalseTouch(BACK_GESTURE);
                     boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
                     boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
                     if (DEBUG_MISSING_GESTURE) {
@@ -267,16 +281,26 @@
         }
     };
 
+    @Inject
     public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
-            SysUiState sysUiState, PluginManager pluginManager, Runnable stateChangeCallback) {
-        super(Dependency.get(BroadcastDispatcher.class));
+            SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor,
+            BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer,
+            NavigationModeController navigationModeController, ViewConfiguration viewConfiguration,
+            WindowManager windowManager, IWindowManager windowManagerService,
+            FalsingManager falsingManager) {
+        super(broadcastDispatcher);
         mContext = context;
         mDisplayId = context.getDisplayId();
-        mMainExecutor = context.getMainExecutor();
+        mMainExecutor = executor;
         mOverviewProxyService = overviewProxyService;
         mSysUiState = sysUiState;
         mPluginManager = pluginManager;
-        mStateChangeCallback = stateChangeCallback;
+        mProtoTracer = protoTracer;
+        mNavigationModeController = navigationModeController;
+        mViewConfiguration = viewConfiguration;
+        mWindowManager = windowManager;
+        mWindowManagerService = windowManagerService;
+        mFalsingManager = falsingManager;
         ComponentName recentsComponentName = ComponentName.unflattenFromString(
                 context.getString(com.android.internal.R.string.config_recentsComponentName));
         if (recentsComponentName != null) {
@@ -309,9 +333,12 @@
         updateCurrentUserResources();
     }
 
+    public void setStateChangeCallback(Runnable callback) {
+        mStateChangeCallback = callback;
+    }
+
     public void updateCurrentUserResources() {
-        Resources res = Dependency.get(NavigationModeController.class).getCurrentUserContext()
-                .getResources();
+        Resources res = mNavigationModeController.getCurrentUserContext().getResources();
         mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
         mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res);
         mIsBackGestureAllowed =
@@ -336,7 +363,7 @@
         // TODO(b/130352502) Tune this value and extract into a constant
         final float backGestureSlop = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
                         SystemUiDeviceConfigFlags.BACK_GESTURE_SLOP_MULTIPLIER, 0.75f);
-        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop() * backGestureSlop;
+        mTouchSlop = mViewConfiguration.getScaledTouchSlop() * backGestureSlop;
     }
 
     private void onNavigationSettingsChanged() {
@@ -358,7 +385,7 @@
      */
     public void onNavBarAttached() {
         mIsAttached = true;
-        Dependency.get(ProtoTracer.class).add(this);
+        mProtoTracer.add(this);
         mOverviewProxyService.addCallback(mQuickSwitchListener);
         mSysUiState.addCallback(mSysUiStateCallback);
         updateIsEnabled();
@@ -370,7 +397,7 @@
      */
     public void onNavBarDetached() {
         mIsAttached = false;
-        Dependency.get(ProtoTracer.class).remove(this);
+        mProtoTracer.remove(this);
         mOverviewProxyService.removeCallback(mQuickSwitchListener);
         mSysUiState.removeCallback(mSysUiStateCallback);
         updateIsEnabled();
@@ -424,9 +451,8 @@
             DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
 
             try {
-                WindowManagerGlobal.getWindowManagerService()
-                        .unregisterSystemGestureExclusionListener(
-                                mGestureExclusionListener, mDisplayId);
+                mWindowManagerService.unregisterSystemGestureExclusionListener(
+                        mGestureExclusionListener, mDisplayId);
             } catch (RemoteException | IllegalArgumentException e) {
                 Log.e(TAG, "Failed to unregister window manager callbacks", e);
             }
@@ -439,13 +465,11 @@
             }
             TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
-                    runnable -> (mContext.getMainThreadHandler()).post(runnable),
-                    mOnPropertiesChangedListener);
+                    mMainExecutor::execute, mOnPropertiesChangedListener);
 
             try {
-                WindowManagerGlobal.getWindowManagerService()
-                        .registerSystemGestureExclusionListener(
-                                mGestureExclusionListener, mDisplayId);
+                mWindowManagerService.registerSystemGestureExclusionListener(
+                        mGestureExclusionListener, mDisplayId);
             } catch (RemoteException | IllegalArgumentException e) {
                 Log.e(TAG, "Failed to register window manager callbacks", e);
             }
@@ -801,7 +825,7 @@
             }
         }
 
-        Dependency.get(ProtoTracer.class).scheduleFrameUpdate();
+        mProtoTracer.scheduleFrameUpdate();
     }
 
     private void updateDisabledForQuickstep(Configuration newConfig) {
@@ -822,8 +846,7 @@
     }
 
     private void updateDisplaySize() {
-        WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
-                .getMaximumWindowMetrics();
+        WindowMetrics metrics = mWindowManager.getMaximumWindowMetrics();
         Rect bounds = metrics.getBounds();
         mDisplaySize.set(bounds.width(), bounds.height());
         if (DEBUG_MISSING_GESTURE) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index f7fa5bf..c552e89 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -8,7 +8,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -63,7 +62,6 @@
     private int mPageToRestore = -1;
     private int mLayoutOrientation;
     private int mLayoutDirection;
-    private final Rect mClippingRect;
     private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
     private int mExcessHeight;
     private int mLastExcessHeight;
@@ -78,14 +76,16 @@
         setCurrentItem(0, false);
         mLayoutOrientation = getResources().getConfiguration().orientation;
         mLayoutDirection = getLayoutDirection();
-        mClippingRect = new Rect();
-
-        // Make sure there's a space between pages when scroling
-        setPageMargin(context.getResources().getDimensionPixelOffset(
-                    R.dimen.qs_tile_margin_horizontal));
     }
     private int mLastMaxHeight = -1;
 
+    @Override
+    public void setPageMargin(int marginPixels) {
+        if (marginPixels != getPageMargin()) {
+            super.setPageMargin(marginPixels);
+        }
+    }
+
     public void saveInstanceState(Bundle outState) {
         outState.putInt(CURRENT_PAGE, getCurrentItem());
     }
@@ -353,14 +353,6 @@
     }
 
     @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        // Clip to margins
-        mClippingRect.set(0, 0, (r - l), b - t);
-        setClipBounds(mClippingRect);
-    }
-
-    @Override
     public boolean setMinRows(int minRows) {
         mMinRows = minRows;
         boolean changed = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 294d765..488ada9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -80,7 +80,6 @@
     private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
     private TouchAnimator mFirstPageAnimator;
     private TouchAnimator mFirstPageDelayedAnimator;
-    private TouchAnimator mTranslationXAnimator;
     private TouchAnimator mTranslationYAnimator;
     private TouchAnimator mNonfirstPageAnimator;
     private TouchAnimator mNonfirstPageDelayedAnimator;
@@ -219,10 +218,29 @@
         mOnFirstPage = isFirst;
     }
 
+    private void translateContent(
+            View qqsView,
+            View qsView,
+            View commonParent,
+            int yOffset,
+            int[] temp,
+            TouchAnimator.Builder animatorBuilder
+    ) {
+        getRelativePosition(temp, qqsView, commonParent);
+        int qqsPos = temp[1];
+        getRelativePosition(temp, qsView, commonParent);
+        int qsPos = temp[1];
+
+        int diff = qsPos - qqsPos - yOffset;
+        animatorBuilder.addFloat(qqsView, "translationY", 0, diff);
+        animatorBuilder.addFloat(qsView, "translationY", -diff, 0);
+        mAllViews.add(qqsView);
+        mAllViews.add(qsView);
+    }
+
     private void updateAnimators() {
         mNeedsAnimatorUpdate = false;
         TouchAnimator.Builder firstPageBuilder = new Builder();
-        TouchAnimator.Builder translationXBuilder = new Builder();
         TouchAnimator.Builder translationYBuilder = new Builder();
 
         Collection<QSTile> tiles = mHost.getTiles();
@@ -241,7 +259,6 @@
         QSTileLayout tileLayout = mQsPanelController.getTileLayout();
         mAllViews.add((View) tileLayout);
         int height = mQs.getView() != null ? mQs.getView().getMeasuredHeight() : 0;
-        int width = mQs.getView() != null ? mQs.getView().getMeasuredWidth() : 0;
         int heightDiff = height - mQs.getHeader().getBottom()
                 + mQs.getHeader().getPaddingBottom();
         if (!mTranslateWhileExpanding) {
@@ -268,68 +285,62 @@
                     QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
                     if (quickTileView == null) continue;
 
-                    getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
-                    getRelativePosition(loc2, tileIcon, view);
-                    final int xDiff = loc2[0] - loc1[0];
-                    int yDiff = loc2[1] - loc1[1];
+                    getRelativePosition(loc1, quickTileView, view);
+                    getRelativePosition(loc2, tileView, view);
+                    int yOffset = loc2[1] - loc1[1];
 
-                    if (count < tileLayout.getNumVisibleTiles()) {
-                        getRelativePosition(loc1, quickTileView, view);
-                        getRelativePosition(loc2, tileView, view);
-                        int yOffset = loc2[1] - loc1[1];
-                        // Move the quick tile right from its location to the new one.
-                        View v = quickTileView.getIcon();
-                        translationXBuilder.addFloat(v, "translationX", 0, xDiff);
-                        translationYBuilder.addFloat(v, "translationY", 0, yDiff - yOffset);
-                        mAllViews.add(v);
+                    // Offset the translation animation on the views
+                    // (that goes from 0 to getOffsetTranslation)
+                    int offsetWithQSBHTranslation =
+                            yOffset - mQuickStatusBarHeader.getOffsetTranslation();
+                    translationYBuilder.addFloat(quickTileView, "translationY", 0,
+                            offsetWithQSBHTranslation);
+                    translationYBuilder.addFloat(tileView, "translationY",
+                            -offsetWithQSBHTranslation, 0);
 
-                        // Move the real tile from the quick tile position to its final
-                        // location.
-                        v = tileIcon;
-                        translationXBuilder.addFloat(v, "translationX", -xDiff, 0);
-                        translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0);
-
-                        // Offset the translation animation on the views
-                        // (that goes from 0 to getOffsetTranslation)
-                        int offsetWithQSBHTranslation =
-                                yOffset - mQuickStatusBarHeader.getOffsetTranslation();
-                        translationYBuilder.addFloat(quickTileView, "translationY", 0,
-                                offsetWithQSBHTranslation);
-                        translationYBuilder.addFloat(tileView, "translationY",
-                                -offsetWithQSBHTranslation, 0);
-
-                        if (mQQSTileHeightAnimator == null) {
-                            mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
-                                    quickTileView.getHeight(), tileView.getHeight());
-                            qqsTileHeight = quickTileView.getHeight();
-                        }
-
-                        mQQSTileHeightAnimator.addView(quickTileView);
-                        View qqsLabelContainer = quickTileView.getLabelContainer();
-                        View qsLabelContainer = tileView.getLabelContainer();
-
-                        getRelativePosition(loc1, qqsLabelContainer, view);
-                        getRelativePosition(loc2, qsLabelContainer, view);
-                        yDiff = loc2[1] - loc1[1] - yOffset;
-
-                        translationYBuilder.addFloat(qqsLabelContainer, "translationY", 0, yDiff);
-                        translationYBuilder.addFloat(qsLabelContainer, "translationY", -yDiff, 0);
-                        mAllViews.add(qqsLabelContainer);
-                        mAllViews.add(qsLabelContainer);
-                    } else { // These tiles disappear when expanding
-                        firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
-                        translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
-
-                        // xDiff is negative here and this makes it "more" negative
-                        final int translationX =
-                                mQsPanelController.isLayoutRtl() ? xDiff - width : xDiff + width;
-                        translationXBuilder.addFloat(quickTileView, "translationX", 0,
-                                translationX);
+                    if (mQQSTileHeightAnimator == null) {
+                        mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
+                                quickTileView.getHeight(), tileView.getHeight());
+                        qqsTileHeight = quickTileView.getHeight();
                     }
 
+                    mQQSTileHeightAnimator.addView(quickTileView);
+
+                    // Icons
+                    translateContent(
+                            quickTileView.getIcon(),
+                            tileView.getIcon(),
+                            view,
+                            yOffset,
+                            loc1,
+                            translationYBuilder
+                    );
+
+                    // Label containers
+                    translateContent(
+                            quickTileView.getLabelContainer(),
+                            tileView.getLabelContainer(),
+                            view,
+                            yOffset,
+                            loc1,
+                            translationYBuilder
+                    );
+
+                    // Secondary icon
+                    translateContent(
+                            quickTileView.getSecondaryIcon(),
+                            tileView.getSecondaryIcon(),
+                            view,
+                            yOffset,
+                            loc1,
+                            translationYBuilder
+                    );
+
+                    firstPageBuilder.addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 1);
+
                     mQuickQsViews.add(tileView);
-                    mAllViews.add(tileView.getIcon());
                     mAllViews.add(quickTileView);
+                    mAllViews.add(quickTileView.getSecondaryLabel());
                 } else if (mFullRows && isIconInAnimatedRow(count)) {
 
                     firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
@@ -391,17 +402,7 @@
             if (mQsPanelController.getDivider() != null) {
                 mAllViews.add(mQsPanelController.getDivider());
             }
-
-            float px = 0;
-            if (tiles.size() <= 3) {
-                px = 1;
-            } else if (tiles.size() <= 6) {
-                px = .4f;
-            }
-            mQSExpansionPathInterpolator.setControlX2(px);
-            translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator());
             translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
-            mTranslationXAnimator = translationXBuilder.build();
             mTranslationYAnimator = translationYBuilder.build();
             if (mQQSTileHeightAnimator != null) {
                 mQQSTileHeightAnimator.setInterpolator(
@@ -474,7 +475,6 @@
             mQuickQsPanel.setAlpha(1);
             mFirstPageAnimator.setPosition(position);
             mFirstPageDelayedAnimator.setPosition(position);
-            mTranslationXAnimator.setPosition(position);
             mTranslationYAnimator.setPosition(position);
             if (mBrightnessAnimator != null) {
                 mBrightnessAnimator.setPosition(position);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index e89803d..9abb430 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -154,11 +154,9 @@
         // bottom and footer are inside the screen.
         MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
 
-        // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to
-        // subtract its height. We do not care if the collapsed notifications fit in the screen.
-        int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
+        int availableScreenHeight = getDisplayHeight() - mNavBarInset;
+        int maxQs = availableScreenHeight - layoutParams.topMargin - layoutParams.bottomMargin
                 - getPaddingBottom();
-        maxQs -= mNavBarInset;
         int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin
                 + layoutParams.rightMargin;
         final int qsPanelWidthSpec = getChildMeasureSpec(widthMeasureSpec, padding,
@@ -169,7 +167,7 @@
         int height = layoutParams.topMargin + layoutParams.bottomMargin
                 + mQSPanelContainer.getMeasuredHeight() + getPaddingBottom();
         super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+                MeasureSpec.makeMeasureSpec(availableScreenHeight, MeasureSpec.EXACTLY));
         // QSCustomizer will always be the height of the screen, but do this after
         // other measuring to avoid changing the height of the QS.
         mQSCustomizer.measure(widthMeasureSpec,
@@ -316,6 +314,9 @@
             if (view == mQSPanelContainer) {
                 // QS panel lays out some of its content full width
                 qsPanelController.setContentMargins(mContentPadding, mContentPadding);
+                // Set it as double the side margin (to simulate end margin of current page +
+                // start margin of next page).
+                qsPanelController.setPageMargin(2 * mSideMargins);
             } else if (view == mHeader) {
                 // The header contains the QQS panel which needs to have special padding, to
                 // visually align them.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index d5cb777..814846c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -480,8 +480,12 @@
     private void updateQsBounds() {
         if (mLastQSExpansion == 1.0f) {
             // Fully expanded, let's set the layout bounds as clip bounds. This is necessary because
-            // it's a scrollview and otherwise wouldn't be clipped.
-            mQsBounds.set(0, 0, mQSPanelScrollView.getWidth(), mQSPanelScrollView.getHeight());
+            // it's a scrollview and otherwise wouldn't be clipped. However, we set the horizontal
+            // bounds so the pages go to the ends of QSContainerImpl
+            ViewGroup.MarginLayoutParams lp =
+                    (ViewGroup.MarginLayoutParams) mQSPanelScrollView.getLayoutParams();
+            mQsBounds.set(-lp.leftMargin, 0, mQSPanelScrollView.getWidth() + lp.rightMargin,
+                    mQSPanelScrollView.getHeight());
         }
         mQSPanelScrollView.setClipBounds(mQsBounds);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index efa1a13..4e16b74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -138,7 +138,7 @@
 
             mHorizontalTileLayout = createHorizontalTileLayout();
             LayoutParams lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
-            int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
+            int marginSize = (int) mContext.getResources().getDimension(R.dimen.qs_media_padding);
             lp.setMarginStart(0);
             lp.setMarginEnd(marginSize);
             lp.gravity = Gravity.CENTER_VERTICAL;
@@ -301,12 +301,6 @@
     protected void updatePadding() {
         final Resources res = mContext.getResources();
         int padding = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
-        if (mUsingHorizontalLayout) {
-            // When using the horizontal layout, our space is quite constrained. We therefore
-            // reduce some of the padding on the top, which makes the brightness bar overlapp,
-            // but since that has naturally quite a bit of built in padding, that's fine.
-            padding = (int) (padding * 0.6f);
-        }
         setPaddingRelative(getPaddingStart(),
                 padding,
                 getPaddingEnd(),
@@ -678,6 +672,16 @@
         switchSecurityFooter();
     }
 
+    protected void setPageMargin(int pageMargin) {
+        if (mRegularTileLayout instanceof PagedTileLayout) {
+            ((PagedTileLayout) mRegularTileLayout).setPageMargin(pageMargin);
+        }
+        if (mHorizontalTileLayout != mRegularTileLayout
+                && mHorizontalTileLayout instanceof PagedTileLayout) {
+            ((PagedTileLayout) mHorizontalTileLayout).setPageMargin(pageMargin);
+        }
+    }
+
     void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boolean force,
             UiEventLogger uiEventLogger) {
         if (horizontal != mUsingHorizontalLayout || force) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 93ccfc72..fff3d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -321,5 +321,9 @@
     public boolean isExpanded() {
         return mView.isExpanded();
     }
+
+    void setPageMargin(int pageMargin) {
+        mView.setPageMargin(pageMargin);
+    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java
index 2c17b87..962537a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java
@@ -16,11 +16,17 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.Button;
 import android.widget.RelativeLayout;
 
+/**
+ * Used for QS tile labels
+ */
 public class ButtonRelativeLayout extends RelativeLayout {
 
+    private View mIgnoredView;
+
     public ButtonRelativeLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -29,4 +35,27 @@
     public CharSequence getAccessibilityClassName() {
         return Button.class.getName();
     }
+
+    /**
+     * Set a view to be ignored for measure.
+     *
+     * The view will be measured and laid out, but its size will be subtracted from the total size
+     * of this view. It assumes that this view only contributes vertical height.
+     */
+    public void setIgnoredView(View view) {
+        if (mIgnoredView == null || mIgnoredView.getParent() == this) {
+            mIgnoredView = view;
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        if (mIgnoredView != null && mIgnoredView.getVisibility() != GONE) {
+            int height = mIgnoredView.getMeasuredHeight();
+            MarginLayoutParams lp = (MarginLayoutParams) mIgnoredView.getLayoutParams();
+            height = height - lp.bottomMargin - lp.topMargin;
+            setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() - height);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 3d5a709..50417e9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -22,7 +22,6 @@
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -44,7 +43,7 @@
     protected TextView mSecondLine;
     private ImageView mPadLock;
     protected int mState;
-    protected ViewGroup mLabelContainer;
+    protected ButtonRelativeLayout mLabelContainer;
     private View mExpandIndicator;
     private View mExpandSpace;
     protected ColorStateList mColorLabelActive;
@@ -92,7 +91,7 @@
     }
 
     protected void createLabel() {
-        mLabelContainer = (ViewGroup) LayoutInflater.from(getContext())
+        mLabelContainer = (ButtonRelativeLayout) LayoutInflater.from(getContext())
                 .inflate(R.layout.qs_tile_label, this, false);
         mLabelContainer.setClipChildren(false);
         mLabelContainer.setClipToPadding(false);
@@ -140,7 +139,7 @@
         }
         if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) {
             mSecondLine.setText(state.secondaryLabel);
-            mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) || mCollapsedView
+            mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel)
                     ? View.GONE : View.VISIBLE);
         }
         boolean dualTarget = mDualTargetAllowed && state.dualTarget;
@@ -193,4 +192,9 @@
     public View getLabelContainer() {
         return mLabelContainer;
     }
+
+    @Override
+    public View getSecondaryLabel() {
+        return mSecondLine;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index b953323..70d51ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -25,6 +25,7 @@
 import android.service.quicksettings.Tile.STATE_ACTIVE
 import android.view.Gravity
 import android.view.View
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.RelativeLayout
@@ -43,7 +44,7 @@
     private var paintColor = Color.WHITE
     private var paintAnimator: ValueAnimator? = null
     private var labelAnimator: ValueAnimator? = null
-    private var mSideView: ImageView = ImageView(mContext)
+    private var sideView: ImageView = ImageView(mContext)
     override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
 
     init {
@@ -59,13 +60,14 @@
         val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
         addView(mIcon, 0, LayoutParams(iconSize, iconSize))
 
-        mSideView.visibility = View.GONE
-        addView(
-                mSideView,
-                -1,
-                LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
-                    gravity = Gravity.CENTER_VERTICAL
-        })
+        sideView.visibility = View.GONE
+        val sideViewLayoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
+            gravity = Gravity.CENTER_VERTICAL
+            marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
+        }
+        addView(sideView, -1, sideViewLayoutParams)
+        sideView.adjustViewBounds = true
+        sideView.scaleType = ImageView.ScaleType.FIT_CENTER
 
         mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
         changeLabelColor(getLabelColor(mState)) // Matches the default state of the tile
@@ -89,16 +91,17 @@
         mLabelContainer.setPadding(0, 0, 0, 0)
         (mLabelContainer.layoutParams as MarginLayoutParams).apply {
             marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
+            marginEnd = 0
+            gravity = Gravity.CENTER_VERTICAL or Gravity.START
         }
         mLabel.gravity = Gravity.START
         mLabel.textDirection = TEXT_DIRECTION_LOCALE
         mSecondLine.gravity = Gravity.START
         mSecondLine.textDirection = TEXT_DIRECTION_LOCALE
 
-        (mLabelContainer.layoutParams as LayoutParams).gravity =
-            Gravity.CENTER_VERTICAL or Gravity.START
         if (mCollapsedView) {
-            mSecondLine.visibility = GONE
+            mSecondLine.alpha = 0f
+            mLabelContainer.setIgnoredView(mSecondLine)
         }
     }
 
@@ -183,9 +186,7 @@
 
     private fun setLabelsColor(color: ColorStateList) {
         mLabel.setTextColor(color)
-        if (!mCollapsedView) {
-            mSecondLine.setTextColor(color)
-        }
+        mSecondLine.setTextColor(color)
     }
 
     private fun clearBackgroundAnimator() {
@@ -198,19 +199,17 @@
 
     private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
         if (state.sideViewDrawable != null) {
-            (mSideView.layoutParams as MarginLayoutParams).apply {
-                marginStart =
-                        context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
-            }
-            mSideView.setImageDrawable(state.sideViewDrawable)
-            mSideView.visibility = View.VISIBLE
-            mSideView.adjustViewBounds = true
-            mSideView.scaleType = ImageView.ScaleType.FIT_CENTER
+            sideView.setImageDrawable(state.sideViewDrawable)
+            sideView.visibility = View.VISIBLE
         } else {
-            mSideView.setImageDrawable(null)
-            mSideView.visibility = GONE
+            sideView.setImageDrawable(null)
+            sideView.visibility = GONE
         }
     }
 
     override fun handleExpand(dualTarget: Boolean) {}
+
+    override fun getSecondaryIcon(): View {
+        return sideView
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index ff830bc..be40423 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -129,8 +129,15 @@
                 Intent intent = new Intent(mContext, WalletActivity.class)
                         .setAction(Intent.ACTION_VIEW)
                         .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
-                mActivityStarter.startActivity(intent, true /* dismissShade */,
-                        animationController);
+                if (mKeyguardStateController.isUnlocked()) {
+                    mActivityStarter.startActivity(intent, true /* dismissShade */,
+                            animationController);
+                } else {
+                    mHost.collapsePanels();
+                    // Do not use ActivityStarter here because the WalletActivity is required to be
+                    // started without prompting keyguard when the device is locked.
+                    mContext.startActivity(intent);
+                }
             } else {
                 if (mQuickAccessWalletClient.createWalletIntent() == null) {
                     Log.w(TAG, "Could not get intent of the wallet app.");
@@ -147,7 +154,7 @@
     protected void handleUpdateState(State state, Object arg) {
         state.label = mLabel;
         state.contentDescription = state.label;
-        state.icon = ResourceIcon.get(R.drawable.ic_qs_wallet);
+        state.icon = ResourceIcon.get(R.drawable.ic_wallet_lockscreen);
         boolean isDeviceLocked = !mKeyguardStateController.isUnlocked();
         if (mQuickAccessWalletClient.isWalletServiceAvailable()) {
             if (mHasCard) {
@@ -219,7 +226,12 @@
                 refreshState();
                 return;
             }
-            mCardViewDrawable = cards.get(0).getCardImage().loadDrawable(mContext);
+            int selectedIndex = response.getSelectedIndex();
+            if (selectedIndex >= cards.size()) {
+                Log.d(TAG, "Selected card index out of bounds.");
+                return;
+            }
+            mCardViewDrawable = cards.get(selectedIndex).getCardImage().loadDrawable(mContext);
             mHasCard = true;
             refreshState();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index 0a55fbe..3c6ab34 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -47,9 +47,9 @@
     private ValueAnimator mColorAnimation;
     private int mMainColorTo;
     private float mCornerRadius;
-    private Rect mBounds;
     private ConcaveInfo mConcaveInfo;
     private int mBottomEdgePosition;
+    private boolean mCornerRadiusEnabled;
 
     public ScrimDrawable() {
         mPaint = new Paint();
@@ -134,29 +134,50 @@
     }
 
     /**
-     * Enable drawable shape to have rounded corners with provided radius
+     * Corner radius used by either concave or convex corners.
      */
     public void setRoundedCorners(float radius) {
+        if (radius == mCornerRadius) {
+            return;
+        }
         mCornerRadius = radius;
+        if (mConcaveInfo != null) {
+            mConcaveInfo.setCornerRadius(radius);
+            updatePath();
+        }
+        invalidateSelf();
     }
 
     /**
-     * Make bottom edge concave with provided corner radius
+     * If we should draw a rounded rect instead of a rect.
      */
-    public void setBottomEdgeConcave(float radius) {
-        if (radius == 0) {
-            // Disable clipping completely when there's no radius.
-            mConcaveInfo = null;
+    public void setRoundedCornersEnabled(boolean enabled) {
+        if (mCornerRadiusEnabled == enabled) {
             return;
         }
-        // only rounding top corners for clip out path
-        float[] cornerRadii = new float[]{radius, radius, radius, radius, 0, 0, 0, 0};
-        mConcaveInfo = new ConcaveInfo(radius, cornerRadii);
+        mCornerRadiusEnabled = enabled;
+        invalidateSelf();
+    }
+
+    /**
+     * If we should draw a concave rounded rect instead of a rect.
+     */
+    public void setBottomEdgeConcave(boolean enabled) {
+        if (enabled && mConcaveInfo != null) {
+            return;
+        }
+        if (!enabled) {
+            mConcaveInfo = null;
+        } else {
+            mConcaveInfo = new ConcaveInfo();
+            mConcaveInfo.setCornerRadius(mCornerRadius);
+        }
+        invalidateSelf();
     }
 
     /**
      * Location of concave edge.
-     * @see #setBottomEdgeConcave(float)
+     * @see #setBottomEdgeConcave(boolean)
      */
     public void setBottomEdgePosition(int y) {
         if (mBottomEdgePosition == y) {
@@ -176,34 +197,35 @@
         mPaint.setAlpha(mAlpha);
         if (mConcaveInfo != null) {
             drawConcave(canvas);
-        } else {
+        } else if (mCornerRadiusEnabled && mCornerRadius > 0) {
             canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right,
                     getBounds().bottom + mCornerRadius,
                     /* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint);
+        } else {
+            canvas.drawRect(getBounds().left, getBounds().top, getBounds().right,
+                    getBounds().bottom, mPaint);
         }
     }
 
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        updatePath();
+    }
+
     private void drawConcave(Canvas canvas) {
-        // checking if width of clip out path needs to change
-        if (mBounds == null
-                || getBounds().right != mBounds.right
-                || getBounds().left != mBounds.left) {
-            mBounds = getBounds();
-            updatePath();
-        }
         canvas.clipOutPath(mConcaveInfo.mPath);
         canvas.drawRect(getBounds().left, getBounds().top, getBounds().right,
                 mBottomEdgePosition + mConcaveInfo.mPathOverlap, mPaint);
     }
 
     private void updatePath() {
-        mConcaveInfo.mPath.reset();
-        if (mBounds == null) {
-            mBounds = getBounds();
+        if (mConcaveInfo == null) {
+            return;
         }
+        mConcaveInfo.mPath.reset();
         float top = mBottomEdgePosition;
         float bottom = mBottomEdgePosition + mConcaveInfo.mPathOverlap;
-        mConcaveInfo.mPath.addRoundRect(mBounds.left, top, mBounds.right, bottom,
+        mConcaveInfo.mPath.addRoundRect(getBounds().left, top, getBounds().right, bottom,
                 mConcaveInfo.mCornerRadii, Path.Direction.CW);
     }
 
@@ -213,13 +235,20 @@
     }
 
     private static class ConcaveInfo {
-        private final float mPathOverlap;
+        private float mPathOverlap;
         private final float[] mCornerRadii;
         private final Path mPath = new Path();
 
-        ConcaveInfo(float pathOverlap, float[] cornerRadii) {
-            mPathOverlap = pathOverlap;
-            mCornerRadii = cornerRadii;
+        ConcaveInfo() {
+            mCornerRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
+        }
+
+        public void setCornerRadius(float radius) {
+            mPathOverlap = radius;
+            mCornerRadii[0] = radius;
+            mCornerRadii[1] = radius;
+            mCornerRadii[2] = radius;
+            mCornerRadii[3] = radius;
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 0d9ade6d..1a5c9ee 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -31,14 +31,12 @@
 import android.util.AttributeSet;
 import android.view.View;
 
-import androidx.annotation.DimenRes;
 import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
-import com.android.systemui.R;
 
 import java.util.concurrent.Executor;
 
@@ -50,9 +48,6 @@
  */
 public class ScrimView extends View {
 
-    @DimenRes
-    private static final int CORNER_RADIUS = R.dimen.notification_scrim_corner_radius;
-
     private final Object mColorLock = new Object();
 
     @GuardedBy("mColorLock")
@@ -301,13 +296,10 @@
      * Make bottom edge concave so overlap between layers is not visible for alphas between 0 and 1
      * @return height of concavity
      */
-    public float enableBottomEdgeConcave(boolean clipScrim) {
+    public void enableBottomEdgeConcave(boolean clipScrim) {
         if (mDrawable instanceof ScrimDrawable) {
-            float radius = clipScrim ? getResources().getDimensionPixelSize(CORNER_RADIUS) : 0;
-            ((ScrimDrawable) mDrawable).setBottomEdgeConcave(radius);
-            return radius;
+            ((ScrimDrawable) mDrawable).setBottomEdgeConcave(clipScrim);
         }
-        return 0;
     }
 
     /**
@@ -321,12 +313,11 @@
     }
 
     /**
-     * Enable view to have rounded corners with radius of {@link #CORNER_RADIUS}
+     * Enable view to have rounded corners.
      */
-    public void enableRoundedCorners() {
+    public void enableRoundedCorners(boolean enabled) {
         if (mDrawable instanceof ScrimDrawable) {
-            int radius = getResources().getDimensionPixelSize(CORNER_RADIUS);
-            ((ScrimDrawable) mDrawable).setRoundedCorners(radius);
+            ((ScrimDrawable) mDrawable).setRoundedCornersEnabled(enabled);
         }
     }
 
@@ -340,4 +331,15 @@
         mDrawableBounds.set((int) left, (int) top, (int) right, (int) bottom);
         mDrawable.setBounds(mDrawableBounds);
     }
+
+    /**
+     * Corner radius of both concave or convex corners.
+     * @see #enableRoundedCorners(boolean)
+     * @see #enableBottomEdgeConcave(boolean)
+     */
+    public void setCornerRadius(int radius) {
+        if (mDrawable instanceof ScrimDrawable) {
+            ((ScrimDrawable) mDrawable).setRoundedCorners(radius);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index a4bb095..7c0496b 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -16,8 +16,6 @@
 
 package com.android.systemui.sensorprivacy
 
-import android.app.KeyguardManager
-import android.app.KeyguardManager.KeyguardDismissCallback
 import android.content.DialogInterface
 import android.content.Intent.EXTRA_PACKAGE_NAME
 import android.content.pm.PackageManager
@@ -28,16 +26,15 @@
 import android.os.Bundle
 import android.os.Handler
 import android.text.Html
-import android.util.Log
 import android.view.View.GONE
 import android.view.View.VISIBLE
 import android.widget.ImageView
 import com.android.internal.app.AlertActivity
 import com.android.internal.widget.DialogTitle
-import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.R
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
-
+import com.android.systemui.statusbar.policy.KeyguardStateController
 import javax.inject.Inject
 
 /**
@@ -48,8 +45,8 @@
  */
 class SensorUseStartedActivity @Inject constructor(
     private val sensorPrivacyController: IndividualSensorPrivacyController,
-    private val keyguardManager: KeyguardManager,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor
+    private val keyguardStateController: KeyguardStateController,
+    private val keyguardDismissUtil: KeyguardDismissUtil
 ) : AlertActivity(), DialogInterface.OnClickListener {
 
     companion object {
@@ -180,17 +177,11 @@
     override fun onClick(dialog: DialogInterface?, which: Int) {
         when (which) {
             BUTTON_POSITIVE -> {
-                if (keyguardUpdateMonitor.getUserHasTrust(userId)) {
-                    keyguardManager
-                            .requestDismissKeyguard(this, object : KeyguardDismissCallback() {
-                        override fun onDismissError() {
-                            Log.e(LOG_TAG, "Cannot dismiss keyguard")
-                        }
-
-                        override fun onDismissSucceeded() {
-                            disableSensorPrivacy()
-                        }
-                    })
+                if (keyguardStateController.isMethodSecure && keyguardStateController.isShowing) {
+                    keyguardDismissUtil.executeWhenUnlocked({
+                        disableSensorPrivacy()
+                        false
+                    }, false)
                 } else {
                     disableSensorPrivacy()
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index c1eaaaf..6b68fc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -888,7 +888,14 @@
             if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
                 // The face timeout message is not very actionable, let's ask the user to
                 // manually retry.
-                showSwipeUpToUnlock();
+                if (!mStatusBarKeyguardViewManager.isBouncerShowing()
+                        && mKeyguardUpdateMonitor.isUdfpsEnrolled()) {
+                    // suggest trying fingerprint
+                    showTransientIndication(R.string.keyguard_try_fingerprint);
+                } else {
+                    // suggest swiping up to unlock (try face auth again or swipe up to bouncer)
+                    showSwipeUpToUnlock();
+                }
             } else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
             } else if (mKeyguardUpdateMonitor.isScreenOn()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 3bf1ff2..91415f27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -34,8 +34,14 @@
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.leak.RotationUtils
 import com.android.systemui.R
+import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import javax.inject.Inject
+import kotlin.math.min
+import kotlin.math.pow
+
+private const val MAX_DEBOUNCE_LEVEL = 3
+private const val BASE_DEBOUNCE_TIME = 2000
 
 /***
  * Controls the ripple effect that shows when wired charging begins.
@@ -47,9 +53,11 @@
     batteryController: BatteryController,
     configurationController: ConfigurationController,
     featureFlags: FeatureFlags,
-    private val context: Context
+    private val context: Context,
+    private val windowManager: WindowManager,
+    private val systemClock: SystemClock
 ) {
-    private var charging: Boolean? = null
+    private var pluggedIn: Boolean? = null
     private val rippleEnabled: Boolean = featureFlags.isChargingRippleEnabled &&
             !SystemProperties.getBoolean("persist.debug.suppress-charging-ripple", false)
     private var normalizedPortPosX: Float = context.resources.getFloat(
@@ -68,6 +76,8 @@
                 or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
         setTrustedOverlay()
     }
+    private var lastTriggerTime: Long? = null
+    private var debounceLevel = 0
 
     @VisibleForTesting
     var rippleView: ChargingRippleView = ChargingRippleView(context, attrs = null)
@@ -76,19 +86,18 @@
         val batteryStateChangeCallback = object : BatteryController.BatteryStateChangeCallback {
             override fun onBatteryLevelChanged(
                 level: Int,
-                pluggedIn: Boolean,
-                nowCharging: Boolean
+                nowPluggedIn: Boolean,
+                charging: Boolean
             ) {
                 // Suppresses the ripple when it's disabled, or when the state change comes
                 // from wireless charging.
-                if (!rippleEnabled || batteryController.isWirelessCharging) {
+                if (!rippleEnabled || batteryController.isPluggedInWireless) {
                     return
                 }
-                val wasCharging = charging
-                charging = nowCharging
-                // Only triggers when the keyguard is active and the device is just plugged in.
-                if ((wasCharging == null || !wasCharging) && nowCharging) {
-                    startRipple()
+                val wasPluggedIn = pluggedIn
+                pluggedIn = nowPluggedIn
+                if ((wasPluggedIn == null || !wasPluggedIn) && nowPluggedIn) {
+                    startRippleWithDebounce()
                 }
             }
         }
@@ -118,6 +127,22 @@
         updateRippleColor()
     }
 
+    // Lazily debounce ripple to avoid triggering ripple constantly (e.g. from flaky chargers).
+    internal fun startRippleWithDebounce() {
+        val now = systemClock.elapsedRealtime()
+        // Debounce wait time = 2 ^ debounce level
+        if (lastTriggerTime == null ||
+                (now - lastTriggerTime!!) > BASE_DEBOUNCE_TIME * (2.0.pow(debounceLevel))) {
+            // Not waiting for debounce. Start ripple.
+            startRipple()
+            debounceLevel = 0
+        } else {
+            // Still waiting for debounce. Ignore ripple and bump debounce level.
+            debounceLevel = min(MAX_DEBOUNCE_LEVEL, debounceLevel + 1)
+        }
+        lastTriggerTime = now
+    }
+
     fun startRipple() {
         if (!rippleEnabled || rippleView.rippleInProgress || rippleView.parent != null) {
             // Skip if ripple is still playing, or not playing but already added the parent
@@ -125,7 +150,6 @@
             // the animation ends.)
             return
         }
-        val mWM = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
         windowLayoutParams.packageName = context.opPackageName
         rippleView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
             override fun onViewDetachedFromWindow(view: View?) {}
@@ -133,12 +157,12 @@
             override fun onViewAttachedToWindow(view: View?) {
                 layoutRipple()
                 rippleView.startRipple(Runnable {
-                    mWM.removeView(rippleView)
+                    windowManager.removeView(rippleView)
                 })
                 rippleView.removeOnAttachStateChangeListener(this)
             }
         })
-        mWM.addView(rippleView, windowLayoutParams)
+        windowManager.addView(rippleView, windowLayoutParams)
     }
 
     private fun layoutRipple() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 30d9841..26c6fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -217,7 +217,7 @@
     }
 
     companion object {
-        const val BYPASS_PANEL_FADE_DURATION = 67
+        const val BYPASS_FADE_DURATION = 67
 
         private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
         private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
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 8574830..a86fbc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -523,12 +523,13 @@
     private final Rect mKeyguardStatusAreaClipBounds = new Rect();
     private int mOldLayoutDirection;
     private NotificationShelfController mNotificationShelfController;
+    private int mScrimCornerRadius;
+    private int mScreenCornerRadius;
 
     private final QuickAccessWalletClient mQuickAccessWalletClient;
     private final Executor mUiExecutor;
 
     private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
-    private int mScrimCornerRadius;
     private KeyguardMediaController mKeyguardMediaController;
 
     private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@@ -807,6 +808,10 @@
                 com.android.internal.R.dimen.status_bar_height);
         mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
                 R.dimen.heads_up_status_bar_padding);
+        mScrimCornerRadius = mResources.getDimensionPixelSize(
+                R.dimen.notification_scrim_corner_radius);
+        mScreenCornerRadius = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.rounded_corner_radius);
     }
 
     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
@@ -871,8 +876,6 @@
     public void updateResources() {
         mSplitShadeNotificationsTopPadding =
                 mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade);
-        mScrimCornerRadius =
-                mResources.getDimensionPixelSize(R.dimen.notification_scrim_corner_radius);
         int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
         int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
         mShouldUseSplitNotificationShade =
@@ -2057,7 +2060,9 @@
         int bottom = 0;
         int left = 0;
         int right = 0;
-        boolean visible = qsFraction > 0 || qsPanelBottomY > 0;
+        boolean visible = (qsFraction > 0 || qsPanelBottomY > 0)
+                && !mShouldUseSplitNotificationShade;
+        int radius = mScrimCornerRadius;
         if (visible || !mShouldUseSplitNotificationShade) {
             if (!mShouldUseSplitNotificationShade) {
                 float notificationTop = mAmbientState.getStackY() - mQsNotificationTopPadding;
@@ -2065,6 +2070,8 @@
                 bottom = getView().getBottom();
                 left = getView().getLeft();
                 right = getView().getRight();
+                radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
+                        Math.min(top / (float) mScrimCornerRadius, 1f));
             } else {
                 top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
                 bottom = mNotificationStackScrollLayoutController.getHeight();
@@ -2073,17 +2080,18 @@
             }
         }
 
+        // Fancy clipping for quick settings
+        if (mQs != null) {
+            mQs.setFancyClipping(top, bottom, radius, visible);
+        }
         if (!mShouldUseSplitNotificationShade) {
-            // Fancy clipping for quick settings
-            if (mQs != null) {
-                mQs.setFancyClipping(top, bottom, mScrimCornerRadius, visible);
-            }
             // The padding on this area is large enough that we can use a cheaper clipping strategy
             mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
             mKeyguardStatusViewController.setClipBounds(visible
                     ? mKeyguardStatusAreaClipBounds : null);
         }
         mScrimController.setNotificationsBounds(left, top, right, bottom);
+        mScrimController.setScrimCornerRadius(radius);
     }
 
     private int calculateQsBottomPosition(float qsExpansionFraction) {
@@ -2237,6 +2245,7 @@
                 break;
             case FLING_HIDE:
             default:
+                mQs.closeDetail();
                 target = 0;
         }
         if (target == mQsExpansionHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 0d96ead..6fb18d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -269,7 +269,7 @@
         updateThemeColors();
 
         behindScrim.enableBottomEdgeConcave(mClipsQsScrim);
-        mNotificationsScrim.enableRoundedCorners();
+        mNotificationsScrim.enableRoundedCorners(true);
 
         if (mScrimBehindChangeRunnable != null) {
             mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable, mMainExecutor);
@@ -294,6 +294,17 @@
         mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
     }
 
+    /**
+     * Sets corner radius of scrims.
+     */
+    public void setScrimCornerRadius(int radius) {
+        if (mScrimBehind == null || mNotificationsScrim == null) {
+            return;
+        }
+        mScrimBehind.setCornerRadius(radius);
+        mNotificationsScrim.setCornerRadius(radius);
+    }
+
     void setScrimVisibleListener(Consumer<Integer> listener) {
         mScrimVisibleListener = listener;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 14aeca1..f403cc94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -615,7 +615,7 @@
             boolean needsFading = needsBypassFading();
             if (needsFading) {
                 delay = 0;
-                fadeoutDuration = KeyguardBypassController.BYPASS_PANEL_FADE_DURATION;
+                fadeoutDuration = KeyguardBypassController.BYPASS_FADE_DURATION;
             } else if (wakeUnlockPulsing) {
                 delay = 0;
                 fadeoutDuration = 240;
@@ -979,7 +979,6 @@
             resetAlternateAuth(false);
             executeAfterKeyguardGoneAction();
         }
-
     }
 
     public void showBouncerMessage(String message, ColorStateList colorState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 6ae5e90..95a7316 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -43,6 +43,13 @@
     boolean isPluggedIn();
 
     /**
+     * Returns {@code true} if the device is currently plugged in via wireless charger.
+     */
+    default boolean isPluggedInWireless() {
+        return false;
+    }
+
+    /**
      * Returns {@code true} if the device is currently in power save mode.
      */
     boolean isPowerSave();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 288eb3d..9e2c478 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -72,6 +72,7 @@
 
     protected int mLevel;
     protected boolean mPluggedIn;
+    private boolean mPluggedInWireless;
     protected boolean mCharging;
     private boolean mStateUnknown = false;
     private boolean mCharged;
@@ -175,6 +176,8 @@
                     * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
                     / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
             mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+            mPluggedInWireless = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
+                    == BatteryManager.BATTERY_PLUGGED_WIRELESS;
 
             final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
                     BatteryManager.BATTERY_STATUS_UNKNOWN);
@@ -260,6 +263,11 @@
     }
 
     @Override
+    public boolean isPluggedInWireless() {
+        return mPluggedInWireless;
+    }
+
+    @Override
     public void getEstimatedTimeRemainingString(EstimateFetchCompletion completion) {
         // Need to fetch or refresh the estimate, but it may involve binder calls so offload the
         // work
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 08cdebd..d2bbcd50d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -34,6 +34,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -59,6 +60,7 @@
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -489,12 +491,16 @@
                     ? "Dumping..."
                     : mContext.getString(R.string.heap_dump_tile_name);
             if (pmi != null) {
+                final long views = Debug.countInstancesOfClass(View.class);
+                final long enrs = Debug.countInstancesOfClass(ExpandableNotificationRow.class);
+                Log.v(TAG, String.format("updating tile state; rss=%d", pmi.currentRss));
+                Log.v(TAG, String.format("views: %d; ExpandableNotificationRows: %d", views, enrs));
                 icon.setRss(pmi.currentRss);
                 state.secondaryLabel =
                         String.format(
-                                "rss: %s / %s",
+                                "rss=%s views=%d\nenr=%d",
                                 formatBytes(pmi.currentRss * 1024),
-                                formatBytes(gm.mHeapLimit * 1024));
+                                views, enrs);
             } else {
                 icon.setRss(0);
                 state.secondaryLabel = null;
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 81bb819..74077a2 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -236,6 +236,14 @@
 
         oneHanded.registerTransitionCallback(new OneHandedTransitionCallback() {
             @Override
+            public void onStartTransition(boolean isEntering) {
+                mSysUiMainExecutor.execute(() -> {
+                    mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
+                            true).commitUpdate(DEFAULT_DISPLAY);
+                });
+            }
+
+            @Override
             public void onStartFinished(Rect bounds) {
                 mSysUiMainExecutor.execute(() -> {
                     mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 39ebe68..d07a8da 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -19,14 +19,21 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.smartspace.SmartspaceTarget;
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.os.Handler;
+import android.os.UserHandle;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.util.AttributeSet;
@@ -47,12 +54,15 @@
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.settings.SecureSettings;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,6 +72,8 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.verification.VerificationMode;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 @SmallTest
@@ -114,10 +126,23 @@
     ActivityStarter mActivityStarter;
     @Mock
     FalsingManager mFalsingManager;
+    @Mock
+    KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock
+    KeyguardBypassController mBypassController;
+    @Mock
+    Handler mHandler;
+    @Mock
+    UserTracker mUserTracker;
+    @Mock
+    SecureSettings mSecureSettings;
 
     private KeyguardClockSwitchController mController;
     private View mStatusArea;
 
+    private static final int USER_ID = 5;
+    private static final int MANAGED_USER_ID = 15;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -152,7 +177,12 @@
                 mConfigurationController,
                 mSystemUIFactory,
                 mActivityStarter,
-                mFalsingManager
+                mFalsingManager,
+                mKeyguardUpdateMonitor,
+                mBypassController,
+                mHandler,
+                mUserTracker,
+                mSecureSettings
         );
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
@@ -253,6 +283,89 @@
         verify(mSmartspaceView, times(2)).setPrimaryTextColor(anyInt());
     }
 
+    @Test
+    public void doNotFilterRegularTarget() {
+        setupPrimaryAndManagedUser();
+        mController.init();
+
+        when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(0);
+        when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
+                .thenReturn(0);
+
+        mController.getSettingsObserver().onChange(true, null);
+
+        SmartspaceTarget t = mock(SmartspaceTarget.class);
+        when(t.isSensitive()).thenReturn(false);
+        when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+        assertEquals(false, mController.filterSmartspaceTarget(t));
+
+        reset(t);
+        when(t.isSensitive()).thenReturn(false);
+        when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
+        assertEquals(false, mController.filterSmartspaceTarget(t));
+    }
+
+    @Test
+    public void filterAllSensitiveTargetsAllUsers() {
+        setupPrimaryAndManagedUser();
+        mController.init();
+
+        when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(0);
+        when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
+                .thenReturn(0);
+
+        mController.getSettingsObserver().onChange(true, null);
+
+        SmartspaceTarget t = mock(SmartspaceTarget.class);
+        when(t.isSensitive()).thenReturn(true);
+        when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+        assertEquals(true, mController.filterSmartspaceTarget(t));
+
+        reset(t);
+        when(t.isSensitive()).thenReturn(true);
+        when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
+        assertEquals(true, mController.filterSmartspaceTarget(t));
+    }
+
+    @Test
+    public void filterSensitiveManagedUserTargets() {
+        setupPrimaryAndManagedUser();
+        mController.init();
+
+        when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(1);
+        when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
+                .thenReturn(0);
+
+        mController.getSettingsObserver().onChange(true, null);
+
+        SmartspaceTarget t = mock(SmartspaceTarget.class);
+        when(t.isSensitive()).thenReturn(true);
+        when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+        assertEquals(false, mController.filterSmartspaceTarget(t));
+
+        reset(t);
+        when(t.isSensitive()).thenReturn(true);
+        when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
+        assertEquals(true, mController.filterSmartspaceTarget(t));
+    }
+
+    private void setupPrimaryAndManagedUser() {
+        UserInfo userInfo = mock(UserInfo.class);
+        when(userInfo.isManagedProfile()).thenReturn(true);
+        when(userInfo.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
+        when(mUserTracker.getUserProfiles()).thenReturn(List.of(userInfo));
+
+        when(mUserTracker.getUserId()).thenReturn(USER_ID);
+        when(mUserTracker.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+    }
+
+    private void setupPrimaryAndNoManagedUser() {
+        when(mUserTracker.getUserProfiles()).thenReturn(Collections.emptyList());
+
+        when(mUserTracker.getUserId()).thenReturn(USER_ID);
+        when(mUserTracker.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+    }
+
     private void verifyAttachment(VerificationMode times) {
         verify(mClockManager, times).addOnClockChangedListener(
                 any(ClockManager.ClockChangedListener.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 73b0a6b..94252d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -95,7 +95,6 @@
     @Mock private lateinit var collapsedSet: ConstraintSet
     @Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
     private lateinit var appIcon: ImageView
-    private lateinit var appName: TextView
     private lateinit var albumView: ImageView
     private lateinit var titleText: TextView
     private lateinit var artistText: TextView
@@ -138,8 +137,6 @@
         whenever(holder.player).thenReturn(view)
         appIcon = ImageView(context)
         whenever(holder.appIcon).thenReturn(appIcon)
-        appName = TextView(context)
-        whenever(holder.appName).thenReturn(appName)
         albumView = ImageView(context)
         whenever(holder.albumView).thenReturn(albumView)
         titleText = TextView(context)
@@ -220,7 +217,6 @@
         val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
                 emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
         player.bindPlayer(state, PACKAGE)
-        assertThat(appName.getText()).isEqualTo(APP)
         assertThat(titleText.getText()).isEqualTo(TITLE)
         assertThat(artistText.getText()).isEqualTo(ARTIST)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index a9d256b..ac24cde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.media
 
+import android.app.smartspace.SmartspaceAction
 import android.app.smartspace.SmartspaceTarget
 import android.graphics.Color
 import androidx.test.filters.SmallTest
@@ -72,6 +73,8 @@
     private lateinit var executor: Executor
     @Mock
     private lateinit var smartspaceData: SmartspaceTarget
+    @Mock
+    private lateinit var smartspaceMediaRecommendationItem: SmartspaceAction
 
     private lateinit var mediaDataFilter: MediaDataFilter
     private lateinit var dataMain: MediaData
@@ -97,6 +100,7 @@
             emptyList(), emptyList(), PACKAGE, null, null, device, true, null)
 
         `when`(smartspaceData.smartspaceTargetId).thenReturn(SMARTSPACE_KEY)
+        `when`(smartspaceData.iconGrid).thenReturn(listOf(smartspaceMediaRecommendationItem))
     }
 
     private fun setUser(id: Int) {
@@ -222,7 +226,7 @@
     }
 
     @Test
-    fun testOnSmartspaceMediaDataLoaded_noMedia_usesSmartspace() {
+    fun testOnSmartspaceMediaDataLoaded_noMedia_nonEmptyRecommendation_usesSmartspace() {
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
         verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
@@ -230,7 +234,18 @@
     }
 
     @Test
-    fun testOnSmartspaceMediaDataLoaded_noRecentMedia_usesSmartspace() {
+    fun testOnSmartspaceMediaDataLoaded_noMedia_emptyRecommendation_showsNothing() {
+        `when`(smartspaceData.iconGrid).thenReturn(listOf())
+
+        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+        verify(listener, never())
+            .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+        assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+    }
+
+    @Test
+    fun testOnSmartspaceMediaDataLoaded_noRecentMedia_nonEmptyRecommendation_usesSmartspace() {
         val dataOld = dataMain.copy(active = false, lastActive = 0L)
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -240,6 +255,19 @@
     }
 
     @Test
+    fun testOnSmartspaceMediaDataLoaded_noRecentMedia_emptyRecommendation_showsNothing() {
+        `when`(smartspaceData.iconGrid).thenReturn(listOf())
+
+        val dataOld = dataMain.copy(active = false, lastActive = 0L)
+        mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
+        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+        verify(listener, never())
+            .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+        assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+    }
+
+    @Test
     fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_usesMedia() {
         // WHEN we have media that was recently played, but not currently active
         val dataCurrent = dataMain.copy(active = false, lastActive = System.currentTimeMillis())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 678f89a..daa8b4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -425,4 +425,30 @@
         assertThat(mediaDataCaptor.value.resumption).isTrue()
         assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTimeMillis)
     }
+
+    @Test
+    fun testTooManyCompactActions_isTruncated() {
+        // GIVEN a notification where too many compact actions were specified
+        val notif = SbnBuilder().run {
+            setPkg(PACKAGE_NAME)
+            modifyNotification(context).also {
+                it.setSmallIcon(android.R.drawable.ic_media_pause)
+                it.setStyle(MediaStyle().apply {
+                    setMediaSession(session.sessionToken)
+                    setShowActionsInCompactView(0, 1, 2, 3, 4)
+                })
+            }
+            build()
+        }
+
+        // WHEN the notification is loaded
+        mediaDataManager.onNotificationAdded(KEY, notif)
+        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+
+        // THEN only the first MAX_COMPACT_ACTIONS are actually set
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
+        assertThat(mediaDataCaptor.value.actionsToShowInCompact.size).isEqualTo(
+                MediaDataManager.MAX_COMPACT_ACTIONS)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
index 37b7cbe..0eeb955 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
@@ -36,6 +36,7 @@
 import com.android.systemui.SysuiTestableContext;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.navigationbar.NavigationBarView;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -67,6 +68,7 @@
         mDependency.injectMockDependency(OverviewProxyService.class);
         mDependency.injectMockDependency(KeyguardStateController.class);
         mDependency.injectMockDependency(NavigationBarController.class);
+        mDependency.injectMockDependency(EdgeBackGestureHandler.class);
         mNavBar = new NavigationBarView(context, null);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index f0c48bd..4ec45b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -72,6 +72,7 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
@@ -132,6 +133,7 @@
         mDependency.injectMockDependency(StatusBarStateController.class);
         mDependency.injectMockDependency(NavigationBarController.class);
         mOverviewProxyService = mDependency.injectMockDependency(OverviewProxyService.class);
+        mDependency.injectMockDependency(EdgeBackGestureHandler.class);
         TestableLooper.get(this).runWithLooper(() -> {
             mNavigationBar = createNavBar(mContext);
             mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
index 7e0920c..e4d32f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
@@ -32,9 +32,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
-import com.android.systemui.navigationbar.NavigationBarTransitions;
-import com.android.systemui.navigationbar.NavigationBarView;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.CommandQueue;
@@ -60,6 +58,7 @@
         mDependency.injectMockDependency(StatusBarStateController.class);
         mDependency.injectMockDependency(KeyguardStateController.class);
         mDependency.injectMockDependency(NavigationBarController.class);
+        mDependency.injectMockDependency(EdgeBackGestureHandler.class);
         doReturn(mContext)
                 .when(mDependency.injectMockDependency(NavigationModeController.class))
                 .getCurrentUserContext();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 2f28b13..5cdad05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -207,7 +207,25 @@
     }
 
     @Test
-    public void testHandleClick_hasCards_startWalletActivity() {
+    public void testHandleClick_hasCards_deviceLocked_startWalletActivity() {
+        when(mKeyguardStateController.isUnlocked()).thenReturn(false);
+        setUpWalletCard(/* hasCard= */ true);
+
+        mTile.handleClick(null /* view */);
+        mTestableLooper.processAllMessages();
+
+        verify(mSpiedContext).startActivity(mIntentCaptor.capture());
+
+        Intent nextStartedIntent = mIntentCaptor.getValue();
+        String walletClassName = "com.android.systemui.wallet.ui.WalletActivity";
+
+        assertNotNull(nextStartedIntent);
+        assertThat(nextStartedIntent.getComponent().getClassName()).isEqualTo(walletClassName);
+    }
+
+    @Test
+    public void testHandleClick_hasCards_deviceUnlocked_startWalletActivity() {
+        when(mKeyguardStateController.isUnlocked()).thenReturn(true);
         setUpWalletCard(/* hasCard= */ true);
 
         mTile.handleClick(null /* view */);
@@ -226,7 +244,7 @@
     @Test
     public void testHandleUpdateState_updateLabelAndIcon() {
         QSTile.State state = new QSTile.State();
-        QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_wallet);
+        QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_wallet_lockscreen);
 
         mTile.handleUpdateState(state, null);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
index 5e783a5..03744b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.charging
 
-import android.content.Context
 import android.testing.AndroidTestingRunner
 import android.view.View
 import android.view.WindowManager
@@ -26,7 +25,7 @@
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,6 +36,7 @@
 import org.mockito.Mockito.any
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.reset
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -50,6 +50,7 @@
     @Mock private lateinit var configurationController: ConfigurationController
     @Mock private lateinit var rippleView: ChargingRippleView
     @Mock private lateinit var windowManager: WindowManager
+    private val systemClock = FakeSystemClock()
 
     @Before
     fun setUp() {
@@ -57,9 +58,8 @@
         `when`(featureFlags.isChargingRippleEnabled).thenReturn(true)
         controller = WiredChargingRippleController(
                 commandRegistry, batteryController, configurationController,
-                featureFlags, context)
+                featureFlags, context, windowManager, systemClock)
         controller.rippleView = rippleView // Replace the real ripple view with a mock instance
-        context.addMockSystemService(Context.WINDOW_SERVICE, windowManager)
     }
 
     @Test
@@ -71,8 +71,8 @@
         // Verify ripple added to window manager.
         captor.value.onBatteryLevelChanged(
                 0 /* unusedBatteryLevel */,
-                false /* plugged in */,
-                true /* charging */)
+                true /* plugged in */,
+                false /* charging */)
         val attachListenerCaptor =
                 ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
         verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture())
@@ -103,4 +103,37 @@
         captor.value.onUiModeChanged()
         verify(rippleView).setColor(ArgumentMatchers.anyInt())
     }
+
+    @Test
+    fun testDebounceRipple() {
+        var time: Long = 0
+        systemClock.setElapsedRealtime(time)
+
+        controller.startRippleWithDebounce()
+        verify(rippleView).addOnAttachStateChangeListener(ArgumentMatchers.any())
+
+        reset(rippleView)
+        // Wait a short while and trigger.
+        time += 100
+        systemClock.setElapsedRealtime(time)
+        controller.startRippleWithDebounce()
+
+        // Verify the ripple is debounced.
+        verify(rippleView, never()).addOnAttachStateChangeListener(ArgumentMatchers.any())
+
+        // Trigger many times.
+        for (i in 0..100) {
+            time += 100
+            systemClock.setElapsedRealtime(time)
+            controller.startRippleWithDebounce()
+        }
+        // Verify all attempts are debounced.
+        verify(rippleView, never()).addOnAttachStateChangeListener(ArgumentMatchers.any())
+
+        // Wait a long while and trigger.
+        systemClock.setElapsedRealtime(time + 500000)
+        controller.startRippleWithDebounce()
+        // Verify that ripple is triggered.
+        verify(rippleView).addOnAttachStateChangeListener(ArgumentMatchers.any())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index ab7cbf7..bfb98de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -4,6 +4,7 @@
 
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Matchers.anyLong;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -309,6 +310,7 @@
         // Put RSSI in the middle of the range.
         rssi += amountPerLevel / 2;
         when(mVcnTransportInfo.getWifiInfo()).thenReturn(mWifiInfo);
+        when(mVcnTransportInfo.makeCopy(anyLong())).thenReturn(mVcnTransportInfo);
         when(mWifiInfo.getRssi()).thenReturn(rssi);
         when(mWifiInfo.isCarrierMerged()).thenReturn(true);
         when(mWifiInfo.getSubscriptionId()).thenReturn(1);
@@ -318,6 +320,7 @@
 
     protected void setWifiStateForVcn(boolean connected, String ssid) {
         when(mVcnTransportInfo.getWifiInfo()).thenReturn(mWifiInfo);
+        when(mVcnTransportInfo.makeCopy(anyLong())).thenReturn(mVcnTransportInfo);
         when(mWifiInfo.getSSID()).thenReturn(ssid);
         when(mWifiInfo.isCarrierMerged()).thenReturn(true);
         when(mWifiInfo.getSubscriptionId()).thenReturn(1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
index d8271e8..db6164d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
@@ -71,6 +71,10 @@
         mCurrentTimeMillis = millis;
     }
 
+    public void setElapsedRealtime(long millis) {
+        mElapsedRealtime = millis;
+    }
+
     /**
      * Advances the time tracked by the fake clock and notifies any listeners that the time has
      * changed (for example, an attached {@link FakeExecutor} may fire its pending runnables).
diff --git a/services/Android.bp b/services/Android.bp
index 20b89de..2281a15 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -180,68 +180,12 @@
         " --hide-package com.google.android.startop.iorap" +
         " --hide DeprecationMismatch" +
         " --hide HiddenTypedefConstant",
-    visibility: ["//visibility:private"],
+    visibility: ["//frameworks/base:__subpackages__"],
     filter_packages: ["com.android."],
 }
 
 droidstubs {
-    name: "services-stubs.sources",
-    srcs: [":services-all-sources"],
-    defaults: ["services-stubs-default"],
-    check_api: {
-        current: {
-            api_file: "api/current.txt",
-            removed_api_file: "api/removed.txt",
-        },
-        last_released: {
-            api_file: ":android.api.system-server.latest",
-            removed_api_file: ":removed.api.system-server.latest",
-            baseline_file: ":android-incompatibilities.api.system-server.latest",
-        },
-        api_lint: {
-            enabled: true,
-            new_since: ":android.api.system-server.latest",
-            baseline_file: "api/lint-baseline.txt",
-        },
-    },
-    dists: [
-        {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
-            dir: "apistubs/android/system-server/api",
-            dest: "android.txt",
-            tag: ".api.txt",
-        },
-        {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
-            dir: "apistubs/android/system-server/api",
-            dest: "removed.txt",
-            tag: ".removed-api.txt",
-        },
-    ],
-}
-
-java_library {
-    name: "android_system_server_stubs_current",
-    defaults: ["android_stubs_dists_default"],
-    srcs: [":services-stubs.sources"],
-    installable: false,
-    static_libs: ["android_module_lib_stubs_current"],
-    sdk_version: "none",
-    system_modules: "none",
-    java_version: "1.8",
-    dist: {
-        dir: "apistubs/android/system-server",
-    },
-}
-
-droidstubs {
-    name: "services-non-updatable-stubs.sources",
+    name: "services-non-updatable-stubs",
     srcs: [":services-non-updatable-sources"],
     defaults: ["services-stubs-default"],
     check_api: {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index fd355d8..dc2628f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -16,6 +16,8 @@
 
 package com.android.server.accessibility;
 
+import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER;
+
 import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.NonNull;
@@ -24,7 +26,9 @@
 import android.appwidget.AppWidgetManagerInternal;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.InstallSourceInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
@@ -33,11 +37,13 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
 
 import libcore.util.EmptyArray;
 
@@ -666,13 +672,66 @@
 
     /**
      * Identifies whether the accessibility service is true and designed for accessibility. An
-     * accessibility service is considered as accessibility category if
-     * {@link AccessibilityServiceInfo#isAccessibilityTool} is true.
+     * accessibility service is considered as accessibility category if meets all conditions below:
+     * <ul>
+     *     <li> {@link AccessibilityServiceInfo#isAccessibilityTool} is true</li>
+     *     <li> is installed from the trusted install source</li>
+     * </ul>
      *
      * @param serviceInfo The accessibility service's serviceInfo.
      * @return Returns true if it is a true accessibility service.
      */
     public boolean isA11yCategoryService(AccessibilityServiceInfo serviceInfo) {
-        return serviceInfo.isAccessibilityTool();
+        if (!serviceInfo.isAccessibilityTool()) {
+            return false;
+        }
+        if (!serviceInfo.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) {
+            return hasTrustedSystemInstallSource(
+                    serviceInfo.getResolveInfo().serviceInfo.packageName);
+        }
+        return true;
+    }
+
+    /** Returns true if the {@code installedPackage} is installed from the trusted install source.
+     */
+    private boolean hasTrustedSystemInstallSource(String installedPackage) {
+        try {
+            InstallSourceInfo installSourceInfo = mPackageManager.getInstallSourceInfo(
+                    installedPackage);
+            if (installSourceInfo == null) {
+                return false;
+            }
+            final String installSourcePackageName = installSourceInfo.getInitiatingPackageName();
+            if (installSourcePackageName == null || !mPackageManager.getPackageInfo(
+                    installSourcePackageName,
+                    0).applicationInfo.isSystemApp()) {
+                return false;
+            }
+            return isTrustedInstallSource(installSourcePackageName);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.w(LOG_TAG, "can't find the package's install source:" + installedPackage);
+        }
+        return false;
+    }
+
+    /** Returns true if the {@code installerPackage} is a trusted install source. */
+    private boolean isTrustedInstallSource(String installerPackage) {
+        final String[] allowedInstallingSources = mContext.getResources().getStringArray(
+                com.android.internal.R.array
+                        .config_accessibility_allowed_install_source);
+
+        if (allowedInstallingSources.length == 0) {
+            //Filters unwanted default installers if no allowed install sources.
+            String defaultInstaller = ArrayUtils.firstOrNull(LocalServices.getService(
+                    PackageManagerInternal.class).getKnownPackageNames(PACKAGE_INSTALLER,
+                    mCurrentUserId));
+            return !TextUtils.equals(defaultInstaller, installerPackage);
+        }
+        for (int i = 0; i < allowedInstallingSources.length; i++) {
+            if (TextUtils.equals(allowedInstallingSources[i], installerPackage)) {
+                return true;
+            }
+        }
+        return false;
     }
 }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillUriGrantsManager.java b/services/autofill/java/com/android/server/autofill/AutofillUriGrantsManager.java
index 801be5e..51e023d 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillUriGrantsManager.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillUriGrantsManager.java
@@ -20,6 +20,8 @@
 
 import static com.android.server.autofill.Helper.sVerbose;
 
+import static java.lang.Integer.toHexString;
+
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.IUriGrantsManager;
@@ -33,32 +35,16 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Pair;
 import android.util.Slog;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
-import com.android.server.uri.UriGrantsManagerInternal;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
+import com.android.server.wm.ActivityTaskManagerInternal;
 
 /**
- * Grants and revokes URI permissions for content-based autofill suggestions.
+ * Grants URI permissions for content-based autofill suggestions.
  *
- * <p>Note that the system cannot just hand out grants directly; it must always do so on behalf of
- * an owner (see {@link com.android.server.uri.UriGrantsManagerService}). For autofill, the owner
- * is the autofill service provider that creates a given autofill suggestion containing a content
- * URI. Therefore, this manager class must be instantiated with the service uid of the provider for
- * which it will manage URI grants.
- *
- * <p>To dump the state of this class, use {@code adb shell dumpsys autofill}.
+ * <p>URI permissions granted by this class are tied to the activity being filled. When the
+ * activity finishes, its URI grants are automatically revoked.
  *
  * <p>To dump all active URI permissions, use {@code adb shell dumpsys activity permissions}.
  */
@@ -69,26 +55,10 @@
     @UserIdInt
     private final int mSourceUserId;
     @NonNull
-    private final IBinder mPermissionOwner;
-    @NonNull
-    private final UriGrantsManagerInternal mUgmInternal;
+    private final ActivityTaskManagerInternal mActivityTaskMgrInternal;
     @NonNull
     private final IUriGrantsManager mUgm;
 
-    // We use a local lock here for simplicity, since the synchronized code does not depend on
-    // any other resources (the "hold and wait" condition required for deadlock is not present).
-    // If this changes in the future, instead of using a local lock this should be updated to
-    // use the shared lock from AutofillManagerServiceImpl.
-    @NonNull
-    private final Object mLock;
-
-    // Tracks the URIs that have been granted to each package. For each URI, the map stores the
-    // activities that triggered the grant. This allows revoking permissions only once all
-    // activities that triggered the grant are finished.
-    @NonNull
-    @GuardedBy("mLock")
-    private final ArrayMap<String, List<Pair<Uri, String>>> mActiveGrantsByPackage;
-
     /**
      * Creates a new instance of the manager.
      *
@@ -99,159 +69,60 @@
     AutofillUriGrantsManager(int serviceUid) {
         mSourceUid = serviceUid;
         mSourceUserId = UserHandle.getUserId(mSourceUid);
-        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
-        mPermissionOwner = mUgmInternal.newUriPermissionOwner("autofill-" + serviceUid);
+        mActivityTaskMgrInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
         mUgm = UriGrantsManager.getService();
-        mLock = new Object();
-        mActiveGrantsByPackage = new ArrayMap<>(0);
     }
 
     public void grantUriPermissions(@NonNull ComponentName targetActivity,
-            @UserIdInt int targetUserId, @NonNull ClipData clip) {
-        String targetPkg = targetActivity.getPackageName();
+            @NonNull IBinder targetActivityToken, @UserIdInt int targetUserId,
+            @NonNull ClipData clip) {
+        final String targetPkg = targetActivity.getPackageName();
+        final IBinder permissionOwner =
+                mActivityTaskMgrInternal.getUriPermissionOwnerForActivity(targetActivityToken);
+        if (permissionOwner == null) {
+            Slog.w(TAG, "Can't grant URI permissions, because the target activity token is invalid:"
+                    + " clip=" + clip
+                    + ", targetActivity=" + targetActivity + ", targetUserId=" + targetUserId
+                    + ", targetActivityToken=" + toHexString(targetActivityToken.hashCode()));
+            return;
+        }
         for (int i = 0; i < clip.getItemCount(); i++) {
             ClipData.Item item = clip.getItemAt(i);
             Uri uri = item.getUri();
             if (uri == null || !SCHEME_CONTENT.equals(uri.getScheme())) {
                 continue;
             }
-            if (grantUriPermissions(targetPkg, targetUserId, uri)) {
-                addToActiveGrants(uri, targetActivity);
-            }
+            grantUriPermissions(uri, targetPkg, targetUserId, permissionOwner);
         }
     }
 
-    public void revokeUriPermissions(@NonNull ComponentName targetActivity,
-            @UserIdInt int targetUserId) {
-        String targetPkg = targetActivity.getPackageName();
-        Set<Uri> urisWhoseGrantsShouldBeRevoked = removeFromActiveGrants(targetActivity);
-        for (Uri uri : urisWhoseGrantsShouldBeRevoked) {
-            revokeUriPermissions(targetPkg, targetUserId, uri);
-        }
-    }
-
-    private boolean grantUriPermissions(@NonNull String targetPkg, @UserIdInt int targetUserId,
-            @NonNull Uri uri) {
+    private void grantUriPermissions(@NonNull Uri uri, @NonNull String targetPkg,
+            @UserIdInt int targetUserId, @NonNull IBinder permissionOwner) {
         final int sourceUserId = ContentProvider.getUserIdFromUri(uri, mSourceUserId);
         if (sVerbose) {
             Slog.v(TAG, "Granting URI permissions: uri=" + uri
                     + ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId
-                    + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId);
+                    + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId
+                    + ", permissionOwner=" + toHexString(permissionOwner.hashCode()));
         }
         final Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(uri);
         final long ident = Binder.clearCallingIdentity();
         try {
             mUgm.grantUriPermissionFromOwner(
-                    mPermissionOwner,
+                    permissionOwner,
                     mSourceUid,
                     targetPkg,
                     uriWithoutUserId,
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     sourceUserId,
                     targetUserId);
-            return true;
         } catch (RemoteException e) {
             Slog.e(TAG, "Granting URI permissions failed: uri=" + uri
                     + ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId
-                    + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId, e);
-            return false;
+                    + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId
+                    + ", permissionOwner=" + toHexString(permissionOwner.hashCode()), e);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
-
-    private void revokeUriPermissions(@NonNull String targetPkg, @UserIdInt int targetUserId,
-            @NonNull Uri uri) {
-        final int sourceUserId = ContentProvider.getUserIdFromUri(uri, mSourceUserId);
-        if (sVerbose) {
-            Slog.v(TAG, "Revoking URI permissions: uri=" + uri
-                    + ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId
-                    + ", target=" + targetPkg + ", targetUserId=" + targetUserId);
-        }
-        final Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(uri);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mUgmInternal.revokeUriPermissionFromOwner(
-                    mPermissionOwner,
-                    uriWithoutUserId,
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION,
-                    sourceUserId,
-                    targetPkg,
-                    targetUserId);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    private void addToActiveGrants(@NonNull Uri uri, @NonNull ComponentName targetActivity) {
-        synchronized (mLock) {
-            String packageName = targetActivity.getPackageName();
-            List<Pair<Uri, String>> uris = mActiveGrantsByPackage.computeIfAbsent(packageName,
-                    k -> new ArrayList<>(1));
-            uris.add(Pair.create(uri, targetActivity.getClassName()));
-        }
-    }
-
-    private Set<Uri> removeFromActiveGrants(@NonNull ComponentName targetActivity) {
-        synchronized (mLock) {
-            String targetPackageName = targetActivity.getPackageName();
-            List<Pair<Uri, String>> uris = mActiveGrantsByPackage.get(targetPackageName);
-            if (uris == null || uris.isEmpty()) {
-                return Collections.emptySet();
-            }
-
-            // Collect all URIs whose grant was triggered by the target activity.
-            String targetActivityClassName = targetActivity.getClassName();
-            Set<Uri> urisWhoseGrantsShouldBeRevoked = new ArraySet<>(1);
-            for (Iterator<Pair<Uri, String>> iter = uris.iterator(); iter.hasNext(); ) {
-                Pair<Uri, String> uriAndActivity = iter.next();
-                if (uriAndActivity.second.equals(targetActivityClassName)) {
-                    urisWhoseGrantsShouldBeRevoked.add(uriAndActivity.first);
-                    iter.remove();
-                }
-            }
-
-            // A URI grant may have been triggered by more than one activity for the same package.
-            // We should not revoke a grant if it was triggered by multiple activities and one or
-            // more of those activities is still alive. Therefore we do a second pass and prune
-            // the set of URIs to be revoked if an additional activity that triggered its grant
-            // is still present.
-            for (Pair<Uri, String> uriAndActivity : uris) {
-                urisWhoseGrantsShouldBeRevoked.remove(uriAndActivity.first);
-            }
-
-            // If there are no remaining URIs granted to the package, drop the entry from the map.
-            if (uris.isEmpty()) {
-                mActiveGrantsByPackage.remove(targetPackageName);
-            }
-            return urisWhoseGrantsShouldBeRevoked;
-        }
-    }
-
-    /**
-     * Dump the active URI grants.
-     */
-    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
-        synchronized (mLock) {
-            if (mActiveGrantsByPackage.isEmpty()) {
-                pw.print(prefix); pw.println("URI grants: none");
-                return;
-            }
-            pw.print(prefix); pw.println("URI grants:");
-            final String prefix2 = prefix + "  ";
-            final String prefix3 = prefix2 + "  ";
-            for (int i = mActiveGrantsByPackage.size() - 1; i >= 0; i--) {
-                String packageName = mActiveGrantsByPackage.keyAt(i);
-                pw.print(prefix2); pw.println(packageName);
-                List<Pair<Uri, String>> uris = mActiveGrantsByPackage.valueAt(i);
-                if (uris == null || uris.isEmpty())  {
-                    continue;
-                }
-                for (Pair<Uri, String> uriAndActivity : uris) {
-                    pw.print(prefix3);
-                    pw.println(uriAndActivity.first + ": " + uriAndActivity.second);
-                }
-            }
-        }
-    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index db5bc4d..8525e36 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -57,7 +57,6 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.server.autofill.ui.InlineFillUi;
 
-import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CancellationException;
@@ -152,8 +151,8 @@
      * Called by {@link Session} to request augmented autofill.
      */
     public void onRequestAutofillLocked(int sessionId, @NonNull IAutoFillManagerClient client,
-            int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
-            @Nullable AutofillValue focusedValue,
+            int taskId, @NonNull ComponentName activityComponent, @NonNull IBinder activityToken,
+            @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue,
             @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
             @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback,
             @NonNull Runnable onErrorCallback,
@@ -181,7 +180,8 @@
                                             inlineSuggestionsRequest, inlineSuggestionsData,
                                             clientState, focusedId, focusedValue,
                                             inlineSuggestionsCallback, client, onErrorCallback,
-                                            remoteRenderService, userId, activityComponent);
+                                            remoteRenderService, userId,
+                                            activityComponent, activityToken);
                                     if (!showingFillWindow) {
                                         requestAutofill.complete(null);
                                     }
@@ -253,7 +253,7 @@
             @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
             @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
             int userId,
-            @NonNull ComponentName targetActivity) {
+            @NonNull ComponentName targetActivity, @NonNull IBinder targetActivityToken) {
         if (inlineSuggestionsData == null || inlineSuggestionsData.isEmpty()
                 || inlineSuggestionsCallback == null || request == null
                 || remoteRenderService == null) {
@@ -307,8 +307,8 @@
                                     final ArrayList<AutofillId> fieldIds = dataset.getFieldIds();
                                     final ClipData content = dataset.getFieldContent();
                                     if (content != null) {
-                                        mUriGrantsManager.grantUriPermissions(
-                                                targetActivity, userId, content);
+                                        mUriGrantsManager.grantUriPermissions(targetActivity,
+                                                targetActivityToken, userId, content);
                                         final AutofillId fieldId = fieldIds.get(0);
                                         if (sDebug) {
                                             Slog.d(TAG, "Calling client autofillContent(): "
@@ -368,12 +368,6 @@
                 + ComponentName.flattenToShortString(mComponentName) + "]";
     }
 
-    @Override
-    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
-        super.dump(prefix, pw);
-        mUriGrantsManager.dump(prefix, pw);
-    }
-
     /**
      * Called by {@link Session} when it's time to destroy all augmented autofill requests.
      */
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f1dcdff..042631d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1689,7 +1689,7 @@
         if (content != null) {
             final AutofillUriGrantsManager autofillUgm =
                     remoteAugmentedAutofillService.getAutofillUriGrantsManager();
-            autofillUgm.grantUriPermissions(mComponentName, userId, content);
+            autofillUgm.grantUriPermissions(mComponentName, mActivityToken, userId, content);
         }
 
         // Fill the value into the field.
@@ -3537,7 +3537,8 @@
                     synchronized (mLock) {
                         logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(),
                                 focusedId, isWhitelisted, inlineSuggestionsRequest != null);
-                        remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName,
+                        remoteService.onRequestAutofillLocked(id, mClient,
+                                taskId, mComponentName, mActivityToken,
                                 AutofillId.withoutSession(focusedId), currentValue,
                                 inlineSuggestionsRequest, inlineSuggestionsResponseCallback,
                                 /*onErrorCallback=*/ () -> {
@@ -4167,13 +4168,6 @@
         if (remoteRenderService != null) {
             remoteRenderService.destroySuggestionViews(userId, id);
         }
-        final RemoteAugmentedAutofillService remoteAugmentedAutofillService =
-                mService.getRemoteAugmentedAutofillServiceIfCreatedLocked();
-        if (remoteAugmentedAutofillService != null) {
-            final AutofillUriGrantsManager autofillUgm =
-                    remoteAugmentedAutofillService.getAutofillUriGrantsManager();
-            autofillUgm.revokeUriPermissions(mComponentName, userId);
-        }
 
         mDestroyed = true;
 
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 78610a2..0bd331b 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -19,6 +19,15 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_COLLECT_LATENCY_DATA_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_DETAILED_TRACKING_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_ENABLED_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_IGNORE_BATTERY_STATUS_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_MAX_CALL_STATS_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_SAMPLING_INTERVAL_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_TRACK_DIRECT_CALLING_UID_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY;
+
 import android.app.ActivityThread;
 import android.content.Context;
 import android.content.pm.PackageInfo;
@@ -43,7 +52,6 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BinderCallsStats;
 import com.android.internal.os.BinderInternal;
-import com.android.internal.os.BinderLatencyObserver;
 import com.android.internal.os.CachedDeviceState;
 import com.android.internal.util.DumpUtils;
 
@@ -125,28 +133,6 @@
 
     /** Listens for flag changes. */
     private static class SettingsObserver extends ContentObserver {
-        // Settings for BinderCallsStats.
-        private static final String SETTINGS_ENABLED_KEY = "enabled";
-        private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
-        private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
-        private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
-        private static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state";
-        private static final String SETTINGS_TRACK_DIRECT_CALLING_UID_KEY = "track_calling_uid";
-        private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count";
-        private static final String SETTINGS_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status";
-        // Settings for BinderLatencyObserver.
-        private static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_Latency_data";
-        private static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY =
-                "latency_observer_sampling_interval";
-        private static final String SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY =
-                "latency_observer_push_interval_minutes";
-        private static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY =
-                "latency_histogram_bucket_count";
-        private static final String SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY =
-                "latency_histogram_first_bucket_size";
-        private static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY =
-                "latency_histogram_bucket_scale_factor";
-
         private boolean mEnabled;
         private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
         private final Context mContext;
@@ -206,23 +192,9 @@
                     mParser.getBoolean(SETTINGS_COLLECT_LATENCY_DATA_KEY,
                     BinderCallsStats.DEFAULT_COLLECT_LATENCY_DATA));
             // Binder latency observer settings.
-            BinderLatencyObserver binderLatencyObserver = mBinderCallsStats.getLatencyObserver();
-            binderLatencyObserver.setSamplingInterval(mParser.getInt(
-                    SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY,
-                    BinderLatencyObserver.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
-            binderLatencyObserver.setHistogramBucketsParams(
-                    mParser.getInt(
-                        SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY,
-                        BinderLatencyObserver.BUCKET_COUNT_DEFAULT),
-                    mParser.getInt(
-                        SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY,
-                        BinderLatencyObserver.FIRST_BUCKET_SIZE_DEFAULT),
-                    mParser.getFloat(
-                        SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY,
-                        BinderLatencyObserver.BUCKET_SCALE_FACTOR_DEFAULT));
-            binderLatencyObserver.setPushInterval(mParser.getInt(
-                    SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY,
-                    BinderLatencyObserver.STATSD_PUSH_INTERVAL_MINUTES_DEFAULT));
+            BinderCallsStats.SettingsObserver.configureLatencyObserver(
+                    mParser,
+                    mBinderCallsStats.getLatencyObserver());
 
             final boolean enabled =
                     mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 6fa8ecd4..f2762fc 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -3382,7 +3382,7 @@
             pw.println("      Sets the inactive state of an app.");
             pw.println("  get-inactive [--user <USER_ID>] <PACKAGE>");
             pw.println("      Returns the inactive state of an app.");
-            pw.println("  set-standby-bucket [--user <USER_ID>] <PACKAGE> active|working_set|frequent|rare");
+            pw.println("  set-standby-bucket [--user <USER_ID>] <PACKAGE> active|working_set|frequent|rare|restricted");
             pw.println("      Puts an app in the standby bucket.");
             pw.println("  get-standby-bucket [--user <USER_ID>] <PACKAGE>");
             pw.println("      Returns the standby bucket of an app.");
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 859cc44..406e866 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -663,7 +663,7 @@
 
                 final long gnssChargeUC = measuredEnergyDeltas.gnssChargeUC;
                 if (gnssChargeUC != MeasuredEnergySnapshot.UNAVAILABLE) {
-                    mStats.updateGnssMeasuredEnergyStatsLocked(displayChargeUC, elapsedRealtime);
+                    mStats.updateGnssMeasuredEnergyStatsLocked(gnssChargeUC, elapsedRealtime);
                 }
             }
             // Inform mStats about each applicable custom energy bucket.
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index efb0f4a..52388ff 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -33,6 +33,11 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.IActivityManager;
+import android.app.StatsManager;
+import android.app.StatsManager.StatsPullAtomCallback;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
 import android.apphibernation.IAppHibernationService;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -43,6 +48,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.RemoteException;
@@ -59,10 +65,12 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.StatsEvent;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -148,8 +156,8 @@
         intentFilter.addAction(ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
         userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
-
         LocalServices.addService(AppHibernationManagerInternal.class, mLocalService);
+        mInjector.getUsageStatsManagerInternal().registerListener(mUsageEventListener);
     }
 
     @Override
@@ -174,6 +182,12 @@
                     NAMESPACE_APP_HIBERNATION,
                     ActivityThread.currentApplication().getMainExecutor(),
                     this::onDeviceConfigChanged);
+            getContext().getSystemService(StatsManager.class)
+                    .setPullAtomCallback(
+                            FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS,
+                            /* metadata */ null, // use default PullAtomMetadata values
+                            mBackgroundExecutor,
+                            new StatsPullAtomCallbackImpl());
         }
     }
 
@@ -269,6 +283,16 @@
             } else {
                 unhibernatePackageForUser(packageName, userId, pkgState);
             }
+            final UserLevelState stateSnapshot = new UserLevelState(pkgState);
+            final int userIdSnapshot = userId;
+            mBackgroundExecutor.execute(() -> {
+                FrameworkStatsLog.write(
+                        FrameworkStatsLog.USER_LEVEL_HIBERNATION_STATE_CHANGED,
+                        stateSnapshot.packageName,
+                        userIdSnapshot,
+                        stateSnapshot.hibernated,
+                        stateSnapshot.lastUnhibernatedMs);
+            });
             List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
             mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
         }
@@ -785,6 +809,20 @@
         }
     };
 
+    private final UsageEventListener mUsageEventListener = (userId, event) -> {
+        if (!isAppHibernationEnabled()) {
+            return;
+        }
+        final int eventType = event.mEventType;
+        if (eventType == UsageEvents.Event.USER_INTERACTION
+                || eventType == UsageEvents.Event.ACTIVITY_RESUMED
+                || eventType == UsageEvents.Event.APP_COMPONENT_USED) {
+            final String pkgName = event.mPackage;
+            setHibernatingForUser(pkgName, userId, false);
+            setHibernatingGlobally(pkgName, false);
+        }
+    };
+
     /**
      * Whether app hibernation is enabled on this device.
      *
@@ -817,6 +855,8 @@
 
         Executor getBackgroundExecutor();
 
+        UsageStatsManagerInternal getUsageStatsManagerInternal();
+
         HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();
 
         HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
@@ -867,6 +907,11 @@
         }
 
         @Override
+        public UsageStatsManagerInternal getUsageStatsManagerInternal() {
+            return LocalServices.getService(UsageStatsManagerInternal.class);
+        }
+
+        @Override
         public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
             File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME);
             return new HibernationStateDiskStore<>(
@@ -886,4 +931,29 @@
                     com.android.internal.R.bool.config_hibernationDeletesOatArtifactsEnabled);
         }
     }
+
+    private final class StatsPullAtomCallbackImpl implements StatsPullAtomCallback {
+        @Override
+        public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
+            if (atomTag != FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS) {
+                return StatsManager.PULL_SKIP;
+            }
+            if (isAppHibernationEnabled()) {
+                List<UserInfo> userInfos = mUserManager.getAliveUsers();
+                final int numUsers = userInfos.size();
+                for (int i = 0; i < numUsers; ++i) {
+                    final int userId = userInfos.get(i).id;
+                    if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
+                        data.add(
+                                FrameworkStatsLog.buildStatsEvent(
+                                        atomTag,
+                                        getHibernatingPackagesForUser(userId).size(),
+                                        userId)
+                        );
+                    }
+                }
+            }
+            return StatsManager.PULL_SUCCESS;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java
index b75b19d..68c363c 100644
--- a/services/core/java/com/android/server/apphibernation/UserLevelState.java
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java
@@ -31,6 +31,14 @@
     @CurrentTimeMillisLong
     public long lastUnhibernatedMs;
 
+    UserLevelState() {}
+
+    UserLevelState(UserLevelState state) {
+        packageName = state.packageName;
+        hibernated = state.hibernated;
+        lastUnhibernatedMs = state.lastUnhibernatedMs;
+    }
+
     @Override
     public String toString() {
         return "UserLevelState{"
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index f2e422d..63b41b7 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -92,8 +92,8 @@
             "discrete_history_quantization_millis";
     private static final String PROPERTY_DISCRETE_FLAGS = "discrete_history_op_flags";
     private static final String PROPERTY_DISCRETE_OPS_LIST = "discrete_history_ops_cslist";
-    private static final String DEFAULT_DISCRETE_OPS = OP_CAMERA + "," + OP_RECORD_AUDIO + ","
-            + OP_FINE_LOCATION + "," + OP_COARSE_LOCATION;
+    private static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
+            + "," + OP_CAMERA + "," + OP_RECORD_AUDIO;
     private static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofHours(24).toMillis();
     private static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis();
     private static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION =
@@ -104,7 +104,6 @@
     private static int[] sDiscreteOps;
     private static int sDiscreteFlags;
 
-
     private static final String TAG_HISTORY = "h";
     private static final String ATTR_VERSION = "v";
     private static final int CURRENT_VERSION = 1;
@@ -144,6 +143,8 @@
     @GuardedBy("mOnDiskLock")
     private DiscreteOps mCachedOps = null;
 
+    private boolean mDebugMode = false;
+
     DiscreteRegistry(Object inMemoryLock) {
         mInMemoryLock = inMemoryLock;
     }
@@ -159,40 +160,35 @@
                 AsyncTask.THREAD_POOL_EXECUTOR, (DeviceConfig.Properties p) -> {
                     setDiscreteHistoryParameters(p);
                 });
-        sDiscreteHistoryCutoff = DeviceConfig.getLong(DeviceConfig.NAMESPACE_PRIVACY,
-                PROPERTY_DISCRETE_HISTORY_CUTOFF, DEFAULT_DISCRETE_HISTORY_CUTOFF);
-        sDiscreteHistoryQuantization = DeviceConfig.getLong(DeviceConfig.NAMESPACE_PRIVACY,
-                PROPERTY_DISCRETE_HISTORY_QUANTIZATION, DEFAULT_DISCRETE_HISTORY_QUANTIZATION);
-        sDiscreteFlags = DeviceConfig.getInt(DeviceConfig.NAMESPACE_PRIVACY,
-                PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE);
-        sDiscreteOps = parseOpsList(DeviceConfig.getString(DeviceConfig.NAMESPACE_PRIVACY,
-                PROPERTY_DISCRETE_OPS_LIST, DEFAULT_DISCRETE_OPS));
+        setDiscreteHistoryParameters(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_PRIVACY));
     }
 
     private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
         if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_CUTOFF)) {
             sDiscreteHistoryCutoff = p.getLong(PROPERTY_DISCRETE_HISTORY_CUTOFF,
                     DEFAULT_DISCRETE_HISTORY_CUTOFF);
-            if (!Build.IS_DEBUGGABLE) {
+            if (!Build.IS_DEBUGGABLE && !mDebugMode) {
                 sDiscreteHistoryCutoff = min(MAXIMUM_DISCRETE_HISTORY_CUTOFF,
                         sDiscreteHistoryCutoff);
             }
+        } else {
+            sDiscreteHistoryCutoff = DEFAULT_DISCRETE_HISTORY_CUTOFF;
         }
         if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_QUANTIZATION)) {
             sDiscreteHistoryQuantization = p.getLong(PROPERTY_DISCRETE_HISTORY_QUANTIZATION,
                     DEFAULT_DISCRETE_HISTORY_QUANTIZATION);
-            if (!Build.IS_DEBUGGABLE) {
+            if (!Build.IS_DEBUGGABLE && !mDebugMode) {
                 sDiscreteHistoryQuantization = max(DEFAULT_DISCRETE_HISTORY_QUANTIZATION,
                         sDiscreteHistoryQuantization);
             }
+        } else {
+            sDiscreteHistoryQuantization = DEFAULT_DISCRETE_HISTORY_QUANTIZATION;
         }
-        if (p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS)) {
-            sDiscreteFlags = p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE);
-        }
-        if (p.getKeyset().contains(PROPERTY_DISCRETE_OPS_LIST)) {
-            sDiscreteOps = parseOpsList(p.getString(PROPERTY_DISCRETE_OPS_LIST,
-                    DEFAULT_DISCRETE_OPS));
-        }
+        sDiscreteFlags = p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS) ? sDiscreteFlags =
+                p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE) : OP_FLAGS_DISCRETE;
+        sDiscreteOps = p.getKeyset().contains(PROPERTY_DISCRETE_OPS_LIST) ? parseOpsList(
+                p.getString(PROPERTY_DISCRETE_OPS_LIST, DEFAULT_DISCRETE_OPS)) : parseOpsList(
+                DEFAULT_DISCRETE_OPS);
     }
 
     void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag,
@@ -232,6 +228,8 @@
             @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
             @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
         DiscreteOps discreteOps = getAllDiscreteOps();
+        beginTimeMillis = max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff,
+                ChronoUnit.MILLIS).toEpochMilli());
         discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
                 opNamesFilter, attributionTagFilter, flagsFilter);
         discreteOps.applyToHistoricalOps(result);
@@ -282,6 +280,18 @@
         }
     }
 
+    void offsetHistory(long offset) {
+        synchronized (mOnDiskLock) {
+            DiscreteOps discreteOps;
+            synchronized (mInMemoryLock) {
+                discreteOps = getAllDiscreteOps();
+                clearHistory();
+            }
+            discreteOps.offsetHistory(offset);
+            persistDiscreteOpsLocked(discreteOps);
+        }
+    }
+
     void dump(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter,
             @Nullable String attributionTagFilter,
             @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
@@ -363,6 +373,13 @@
             }
         }
 
+        private void offsetHistory(long offset) {
+            int nUids = mUids.size();
+            for (int i = 0; i < nUids; i++) {
+                mUids.valueAt(i).offsetHistory(offset);
+            }
+        }
+
         private void clearHistory(int uid, String packageName) {
             if (mUids.containsKey(uid)) {
                 mUids.get(uid).clearPackage(packageName);
@@ -549,6 +566,13 @@
             }
         }
 
+        private void offsetHistory(long offset) {
+            int nPackages = mPackages.size();
+            for (int i = 0; i < nPackages; i++) {
+                mPackages.valueAt(i).offsetHistory(offset);
+            }
+        }
+
         private void clearPackage(String packageName) {
             mPackages.remove(packageName);
         }
@@ -656,6 +680,13 @@
             }
         }
 
+        private void offsetHistory(long offset) {
+            int nOps = mPackageOps.size();
+            for (int i = 0; i < nOps; i++) {
+                mPackageOps.valueAt(i).offsetHistory(offset);
+            }
+        }
+
         private DiscreteOp getOrCreateDiscreteOp(int op) {
             DiscreteOp result = mPackageOps.get(op);
             if (result == null) {
@@ -749,12 +780,29 @@
             }
         }
 
+        private void offsetHistory(long offset) {
+            int nTags = mAttributedOps.size();
+            for (int i = 0; i < nTags; i++) {
+                List<DiscreteOpEvent> list = mAttributedOps.valueAt(i);
+
+                int n = list.size();
+                for (int j = 0; j < n; j++) {
+                    DiscreteOpEvent event = list.get(j);
+                    list.set(j, new DiscreteOpEvent(event.mNoteTime - offset, event.mNoteDuration,
+                            event.mUidState, event.mOpFlag));
+                }
+            }
+        }
+
         void addDiscreteAccess(@Nullable String attributionTag,
                 @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                 long accessTime, long accessDuration) {
             List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
                     attributionTag);
             accessTime = accessTime / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+            accessDuration = accessDuration == -1 ? -1
+                    : (accessDuration + sDiscreteHistoryQuantization - 1)
+                            / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
 
             int nAttributedOps = attributedOps.size();
             int i = nAttributedOps;
@@ -764,8 +812,7 @@
                     break;
                 }
                 if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
-                    if (accessDuration != previousOp.mNoteDuration
-                            && accessDuration > sDiscreteHistoryQuantization) {
+                    if (accessDuration != previousOp.mNoteDuration) {
                         break;
                     } else {
                         return;
@@ -983,5 +1030,9 @@
         }
         return true;
     }
+
+    void setDebugMode(boolean debugMode) {
+        this.mDebugMode = debugMode;
+    }
 }
 
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index ffd2458..72e582e 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -553,6 +553,11 @@
                     if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) {
                         clearHistoryOnDiskDLocked();
                     }
+                    if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE) {
+                        mDiscreteRegistry.setDebugMode(true);
+                    } else {
+                        mDiscreteRegistry.setDebugMode(false);
+                    }
                 }
                 if (mBaseSnapshotInterval != baseSnapshotInterval) {
                     mBaseSnapshotInterval = baseSnapshotInterval;
@@ -591,6 +596,7 @@
                 mPersistence.persistHistoricalOpsDLocked(history);
             }
         }
+        mDiscreteRegistry.offsetHistory(offsetMillis);
     }
 
     void addHistoricalOps(HistoricalOps ops) {
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 94d47aa..779558e 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
@@ -632,7 +632,7 @@
             for (String instance : instances) {
                 final String fqName = IFace.DESCRIPTOR + "/" + instance;
                 final IFace face = IFace.Stub.asInterface(
-                        ServiceManager.waitForDeclaredService(fqName));
+                        Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
                 if (face == null) {
                     Slog.e(TAG, "Unable to get declared service: " + fqName);
                     continue;
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 23be50e..84d239e 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
@@ -33,6 +33,7 @@
 import android.hardware.face.Face;
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -173,7 +174,9 @@
         Slog.d(getTag(), "Daemon was null, reconnecting");
 
         mDaemon = IFace.Stub.asInterface(
-                ServiceManager.waitForDeclaredService(IFace.DESCRIPTOR + "/" + mHalInstanceName));
+                Binder.allowBlocking(
+                        ServiceManager.waitForDeclaredService(
+                                IFace.DESCRIPTOR + "/" + mHalInstanceName)));
         if (mDaemon == null) {
             Slog.e(getTag(), "Unable to get daemon");
             return null;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 871b4f67..418b969 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -6089,10 +6089,8 @@
 
         @BinderThread
         @Override
-        public void updateStatusIcon(String packageName, @DrawableRes int iconId,
-                IVoidResultCallback resultCallback) {
-            CallbackUtils.onResult(resultCallback,
-                    () -> mImms.updateStatusIcon(mToken, packageName, iconId));
+        public void updateStatusIconAsync(String packageName, @DrawableRes int iconId) {
+            mImms.updateStatusIcon(mToken, packageName, iconId);
         }
 
         @BinderThread
@@ -6119,16 +6117,14 @@
 
         @BinderThread
         @Override
-        public void notifyUserAction(IVoidResultCallback resultCallback) {
-            CallbackUtils.onResult(resultCallback, () -> mImms.notifyUserAction(mToken));
+        public void notifyUserActionAsync() {
+            mImms.notifyUserAction(mToken);
         }
 
         @BinderThread
         @Override
-        public void applyImeVisibility(IBinder windowToken, boolean setVisible,
-                IVoidResultCallback resultCallback) {
-            CallbackUtils.onResult(resultCallback,
-                    () -> mImms.applyImeVisibility(mToken, windowToken, setVisible));
+        public void applyImeVisibilityAsync(IBinder windowToken, boolean setVisible) {
+            mImms.applyImeVisibility(mToken, windowToken, setVisible);
         }
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ad5be07..5b03989 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1311,7 +1311,7 @@
 
     private void unlockKeystore(byte[] password, int userHandle) {
         if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle);
-        Authorization.onLockScreenEvent(false, userHandle, password);
+        Authorization.onLockScreenEvent(false, userHandle, password, null);
     }
 
     @VisibleForTesting /** Note: this method is overridden in unit tests */
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 90694d0..3f2b8ff 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -31,6 +31,7 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -224,6 +225,12 @@
         }
 
         public boolean serverBasedResumeOnReboot() {
+            // Always use the server based RoR if the HAL isn't installed on device.
+            if (!mContext.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_REBOOT_ESCROW)) {
+                return true;
+            }
+
             return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
                     "server_based_ror_enabled", false);
         }
@@ -374,6 +381,7 @@
         try {
             escrowKey = getAndClearRebootEscrowKey(kk);
         } catch (IOException e) {
+            Slog.i(TAG, "Failed to load escrow key, scheduling retry.", e);
             scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users,
                     rebootEscrowUsers);
             return;
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index e7c0a50..431b009 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -382,8 +382,8 @@
 
         // Migrate data usage over a VPN to the TUN network.
         for (UnderlyingNetworkInfo info : vpnArray) {
-            delta.migrateTun(info.getOwnerUid(), info.getIface(),
-                    info.getUnderlyingIfaces());
+            delta.migrateTun(info.getOwnerUid(), info.getInterface(),
+                    info.getUnderlyingInterfaces());
             // Filter out debug entries as that may lead to over counting.
             delta.filterDebugEntries();
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8d6c145..9a3fe82 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4883,6 +4883,11 @@
                 return super.filterAppAccess(packageName, callingUid, userId);
             }
         }
+        public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
+            synchronized (mLock) {
+                super.dump(type, fd, pw, dumpState);
+            }
+        }
     }
 
 
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 678f046..7f18c4b 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -168,7 +168,7 @@
     static final boolean DEBUG = false; // STOPSHIP if true
     static final boolean DEBUG_LOAD = false; // STOPSHIP if true
     static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
-    static final boolean DEBUG_REBOOT = true;
+    static final boolean DEBUG_REBOOT = false; // STOPSHIP if true
 
     @VisibleForTesting
     static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
@@ -2241,6 +2241,8 @@
 
                 packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
 
+                reportShortcutUsedInternal(packageName, shortcut.getId(), userId);
+
                 verifyStates();
 
                 ret.complete(null);
@@ -2851,12 +2853,7 @@
                     }
                 }
 
-                final long token = injectClearCallingIdentity();
-                try {
-                    mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId);
-                } finally {
-                    injectRestoreCallingIdentity(token);
-                }
+                reportShortcutUsedInternal(packageName, shortcutId, userId);
                 ret.complete(true);
             } catch (Exception e) {
                 ret.completeExceptionally(e);
@@ -2865,6 +2862,15 @@
         return ret;
     }
 
+    private void reportShortcutUsedInternal(String packageName, String shortcutId, int userId) {
+        final long token = injectClearCallingIdentity();
+        try {
+            mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId);
+        } finally {
+            injectRestoreCallingIdentity(token);
+        }
+    }
+
     @Override
     public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
         final long token = injectClearCallingIdentity();
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 622b758..3a097a7 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -280,14 +280,15 @@
     private static void updateAllowListedTagsForPackageLocked(int uid, String packageName,
             Set<String> allowListedTags, ConcurrentHashMap<Integer, ArrayMap<String,
             ArraySet<String>>> datastore) {
+        final int appId = UserHandle.getAppId(uid);
         // We make a copy of the per UID state to limit our mutation to one
         // operation in the underlying concurrent data structure.
-        ArrayMap<String, ArraySet<String>> uidTags = datastore.get(uid);
-        if (uidTags != null) {
-            uidTags = new ArrayMap<>(uidTags);
+        ArrayMap<String, ArraySet<String>> appIdTags = datastore.get(appId);
+        if (appIdTags != null) {
+            appIdTags = new ArrayMap<>(appIdTags);
         }
 
-        ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
+        ArraySet<String> packageTags = (appIdTags != null) ? appIdTags.get(packageName) : null;
         if (packageTags != null) {
             packageTags = new ArraySet<>(packageTags);
         }
@@ -299,17 +300,17 @@
             } else {
                 packageTags = new ArraySet<>(allowListedTags);
             }
-            if (uidTags == null) {
-                uidTags = new ArrayMap<>();
+            if (appIdTags == null) {
+                appIdTags = new ArrayMap<>();
             }
-            uidTags.put(packageName, packageTags);
-            datastore.put(uid, uidTags);
-        } else if (uidTags != null) {
-            uidTags.remove(packageName);
-            if (!uidTags.isEmpty()) {
-                datastore.put(uid, uidTags);
+            appIdTags.put(packageName, packageTags);
+            datastore.put(appId, appIdTags);
+        } else if (appIdTags != null) {
+            appIdTags.remove(packageName);
+            if (!appIdTags.isEmpty()) {
+                datastore.put(appId, appIdTags);
             } else {
-                datastore.remove(uid);
+                datastore.remove(appId);
             }
         }
     }
@@ -318,9 +319,10 @@
             @NonNull String attributionTag, @NonNull Map<Integer, ArrayMap<String,
             ArraySet<String>>> mappedOps) {
         // Only a single lookup from the underlying concurrent data structure
-        final ArrayMap<String, ArraySet<String>> uidTags = mappedOps.get(uid);
-        if (uidTags != null) {
-            final ArraySet<String> packageTags = uidTags.get(packageName);
+        final int appId = UserHandle.getAppId(uid);
+        final ArrayMap<String, ArraySet<String>> appIdTags = mappedOps.get(appId);
+        if (appIdTags != null) {
+            final ArraySet<String> packageTags = appIdTags.get(packageName);
             if (packageTags != null && packageTags.contains(attributionTag)) {
                 return true;
             }
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 6f6bdac..edd5f5f 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -83,6 +83,7 @@
     private static final String TAG = "DeviceStateProviderImpl";
 
     private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true;
+    private static final BooleanSupplier FALSE_BOOLEAN_SUPPLIER = () -> false;
 
     @VisibleForTesting
     static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE,
@@ -152,7 +153,7 @@
     private final DeviceState[] mOrderedStates;
     // Map of state identifier to a boolean supplier that returns true when all required conditions
     // are met for the device to be in the state.
-    private final SparseArray<BooleanSupplier> mStateConditions;
+    private final SparseArray<BooleanSupplier> mStateConditions = new SparseArray<>();
 
     @Nullable
     @GuardedBy("mLock")
@@ -177,6 +178,11 @@
         Arrays.sort(orderedStates, Comparator.comparingInt(DeviceState::getIdentifier));
         mOrderedStates = orderedStates;
 
+        setStateConditions(deviceStates, stateConditions);
+    }
+
+    private void setStateConditions(@NonNull List<DeviceState> deviceStates,
+            @NonNull List<Conditions> stateConditions) {
         // Whether or not this instance should register to receive lid switch notifications from
         // InputManagerInternal. If there are no device state conditions that are based on the lid
         // switch there is no need to register for a callback.
@@ -185,7 +191,6 @@
         // The set of Sensor(s) that this instance should register to receive SensorEvent(s) from.
         final ArraySet<Sensor> sensorsToListenTo = new ArraySet<>();
 
-        mStateConditions = new SparseArray<>();
         for (int i = 0; i < stateConditions.size(); i++) {
             final int state = deviceStates.get(i).getIdentifier();
             final Conditions conditions = stateConditions.get(i);
@@ -194,12 +199,20 @@
                 continue;
             }
 
+            // Whether or not all the required hardware components could be found that match the
+            // requirements from the config.
+            boolean allRequiredComponentsFound = true;
+            // Whether or not this condition requires the lid switch.
+            boolean lidSwitchRequired = false;
+            // Set of sensors required for this condition.
+            ArraySet<Sensor> sensorsRequired = new ArraySet<>();
+
             List<BooleanSupplier> suppliers = new ArrayList<>();
 
             LidSwitchCondition lidSwitchCondition = conditions.getLidSwitch();
             if (lidSwitchCondition != null) {
                 suppliers.add(new LidSwitchBooleanSupplier(lidSwitchCondition.getOpen()));
-                shouldListenToLidSwitch = true;
+                lidSwitchRequired = true;
             }
 
             List<SensorCondition> sensorConditions = conditions.getSensor();
@@ -210,22 +223,33 @@
 
                 final Sensor foundSensor = findSensor(expectedSensorType, expectedSensorName);
                 if (foundSensor == null) {
-                    throw new IllegalStateException("Failed to find Sensor with type: "
-                            + expectedSensorType + " and name: " + expectedSensorName);
+                    Slog.e(TAG, "Failed to find Sensor with type: " + expectedSensorType
+                            + " and name: " + expectedSensorName);
+                    allRequiredComponentsFound = false;
+                    break;
                 }
 
                 suppliers.add(new SensorBooleanSupplier(foundSensor, sensorCondition.getValue()));
-                sensorsToListenTo.add(foundSensor);
+                sensorsRequired.add(foundSensor);
             }
 
-            if (suppliers.size() > 1) {
-                mStateConditions.put(state, new AndBooleanSupplier(suppliers));
-            } else if (suppliers.size() > 0) {
-                // No need to wrap with an AND supplier if there is only 1.
-                mStateConditions.put(state, suppliers.get(0));
+            if (allRequiredComponentsFound) {
+                shouldListenToLidSwitch |= lidSwitchRequired;
+                sensorsToListenTo.addAll(sensorsRequired);
+
+                if (suppliers.size() > 1) {
+                    mStateConditions.put(state, new AndBooleanSupplier(suppliers));
+                } else if (suppliers.size() > 0) {
+                    // No need to wrap with an AND supplier if there is only 1.
+                    mStateConditions.put(state, suppliers.get(0));
+                } else {
+                    // There are no conditions for this state. Default to always true.
+                    mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
+                }
             } else {
-                // There are no conditions for this state. Default to always true.
-                mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
+                // Failed to setup this condition. This can happen if a sensor is missing. Default
+                // this state to always false.
+                mStateConditions.put(state, FALSE_BOOLEAN_SUPPLIER);
             }
         }
 
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index f014b07..4b71742 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -41,6 +41,7 @@
 import android.content.res.XmlResourceParser;
 import android.database.ContentObserver;
 import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricSourceType;
 import android.net.Uri;
 import android.os.Binder;
@@ -188,8 +189,6 @@
     private boolean mTrustAgentsCanRun = false;
     private int mCurrentUser = UserHandle.USER_SYSTEM;
 
-    private Authorization mAuthorizationService;
-
     public TrustManagerService(Context context) {
         super(context);
         mContext = context;
@@ -199,7 +198,6 @@
         mStrongAuthTracker = new StrongAuthTracker(context);
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mSettingsObserver = new SettingsObserver(mHandler);
-        mAuthorizationService = new Authorization();
     }
 
     @Override
@@ -701,13 +699,14 @@
         }
         if (changed) {
             dispatchDeviceLocked(userId, locked);
-
-            Authorization.onLockScreenEvent(locked, userId, null);
+            Authorization.onLockScreenEvent(locked, userId, null,
+                    getBiometricSids(userId));
             // Also update the user's profiles who have unified challenge, since they
             // share the same unlocked state (see {@link #isDeviceLocked(int)})
             for (int profileHandle : mUserManager.getEnabledProfileIds(userId)) {
                 if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(profileHandle)) {
-                    mAuthorizationService.onLockScreenEvent(locked, profileHandle, null);
+                    Authorization.onLockScreenEvent(locked, profileHandle, null,
+                            getBiometricSids(profileHandle));
                 }
             }
         }
@@ -1047,6 +1046,14 @@
         }
     }
 
+    private long[] getBiometricSids(int userId) {
+        BiometricManager biometricManager = mContext.getSystemService(BiometricManager.class);
+        if (biometricManager == null) {
+            return null;
+        }
+        return biometricManager.getAuthenticatorIds(userId);
+    }
+
     // User lifecycle
 
     @Override
@@ -1258,7 +1265,8 @@
                         mDeviceLockedForUser.put(userId, locked);
                     }
 
-                    Authorization.onLockScreenEvent(locked, userId, null);
+                    Authorization.onLockScreenEvent(locked, userId, null,
+                            getBiometricSids(userId));
 
                     if (locked) {
                         try {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 8f60b09..65b947c 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1558,8 +1558,22 @@
                                 teardownAsynchronously();
                             } /* networkUnwantedCallback */,
                             (status) -> {
-                                if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
-                                    clearFailedAttemptCounterAndSafeModeAlarm();
+                                switch (status) {
+                                    case NetworkAgent.VALIDATION_STATUS_VALID:
+                                        clearFailedAttemptCounterAndSafeModeAlarm();
+                                        break;
+                                    case NetworkAgent.VALIDATION_STATUS_NOT_VALID:
+                                        // Will only set a new alarm if no safe mode alarm is
+                                        // currently scheduled.
+                                        setSafeModeAlarm();
+                                        break;
+                                    default:
+                                        Slog.wtf(
+                                                TAG,
+                                                "Unknown validation status "
+                                                        + status
+                                                        + "; ignoring");
+                                        break;
                                 }
                             } /* validationStatusCallback */);
 
@@ -1837,7 +1851,7 @@
 
         private long getNextRetryIntervalsMs() {
             final int retryDelayIndex = mFailedAttempts - 1;
-            final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs();
+            final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMillis();
 
             // Repeatedly use last item in retry timeout list.
             if (retryDelayIndex >= retryIntervalsMs.length) {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index efee0a1..eb9ab36 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -827,9 +827,9 @@
 
                 if (rootTask.inFreeformWindowingMode()) {
                     rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-                } else if (!mService.mSupportsNonResizableMultiWindow && r.inSizeCompatMode()) {
-                    throw new IllegalStateException("Size-compat windows are currently not"
-                            + "freeform-enabled");
+                } else if (!r.supportsFreeform()) {
+                    throw new IllegalStateException(
+                            "This activity is currently not freeform-enabled");
                 } else if (rootTask.getParent().inFreeformWindowingMode()) {
                     // If the window is on a freeform display, set it to undefined. It will be
                     // resolved to freeform and it can adjust windowing mode when the display mode
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3e8bc5d..6957aa0 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2465,8 +2465,7 @@
         if (windowingMode == WINDOWING_MODE_PINNED && info.supportsPictureInPicture()) {
             return false;
         }
-        if (WindowConfiguration.inMultiWindowMode(windowingMode)
-                && mAtmService.mSupportsNonResizableMultiWindow
+        if (WindowConfiguration.inMultiWindowMode(windowingMode) && supportsMultiWindow()
                 && !mAtmService.mForceResizableActivities) {
             // The non resizable app will be letterboxed instead of being forced resizable.
             return false;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index aa993bf..9178a8d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -561,6 +561,14 @@
     public abstract ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry();
 
     /**
+     * Returns the URI permission owner associated with the given activity (see
+     * {@link ActivityRecord#getUriPermissionsLocked()}). If the passed-in activity token is
+     * invalid, returns null.
+     */
+    @Nullable
+    public abstract IBinder getUriPermissionOwnerForActivity(@NonNull IBinder activityToken);
+
+    /**
      * Gets bitmap snapshot of the provided task id.
      *
      * <p>Warning! this may restore the snapshot from disk so can block, don't call in a latency
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0570f6c..6198573 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3340,11 +3340,6 @@
     }
 
     @Override
-    public boolean supportsNonResizableMultiWindow() {
-        return mSupportsNonResizableMultiWindow;
-    }
-
-    @Override
     public boolean updateConfiguration(Configuration values) {
         mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
 
@@ -6256,6 +6251,16 @@
             }
         }
 
+        @Nullable
+        @Override
+        public IBinder getUriPermissionOwnerForActivity(@NonNull IBinder activityToken) {
+            ActivityTaskManagerService.enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
+            synchronized (mGlobalLock) {
+                ActivityRecord r = ActivityRecord.isInRootTaskLocked(activityToken);
+                return (r == null) ? null : r.getUriPermissionsLocked().getExternalToken();
+            }
+        }
+
         @Override
         public TaskSnapshot getTaskSnapshotBlocking(
                 int taskId, boolean isLowResolution) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 43326df..d5a7619 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1691,7 +1691,11 @@
 
     static boolean isTaskTransitOld(@TransitionOldType int transit) {
         return isTaskOpenTransitOld(transit)
-                || transit == TRANSIT_OLD_TASK_CLOSE
+                || isTaskCloseTransitOld(transit);
+    }
+
+    static boolean isTaskCloseTransitOld(@TransitionOldType int transit) {
+        return transit == TRANSIT_OLD_TASK_CLOSE
                 || transit == TRANSIT_OLD_TASK_TO_BACK;
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e28ab26..a108478 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1538,6 +1538,11 @@
             // to cover the activity configuration change.
             return false;
         }
+        if (r.mStartingData != null && r.mStartingData.hasImeSurface()) {
+            // Currently it is unknown that when will IME window be ready. Reject the case to
+            // avoid flickering by showing IME in inconsistent orientation.
+            return false;
+        }
         if (checkOpening) {
             if (!mAppTransition.isTransitionSet() || !mOpeningApps.contains(r)) {
                 // Apply normal rotation animation in case of the activity set different requested
@@ -2108,9 +2113,7 @@
             }
 
             // Check if input device can dispatch events to current display.
-            // If display type is virtual, will follow the default display.
-            if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(),
-                    displayInfo.type == Display.TYPE_VIRTUAL ? DEFAULT_DISPLAY : mDisplayId)) {
+            if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(), mDisplayId)) {
                 continue;
             }
 
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index af0c3e3..94d81fb 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -129,10 +129,10 @@
     private boolean mParsedXml;
 
     /**
-     * Specified throttle time in milliseconds. Don't allow an app to generate a display hash more
-     * than once per throttleTime
+     * Specified duration between requests to generate a display hash in milliseconds. Requests
+     * faster than this delay will be throttled.
      */
-    private int mThrottleDurationMillis = 0;
+    private int mDurationBetweenRequestMillis = 0;
 
     /**
      * The last time an app requested to generate a display hash in System time.
@@ -203,8 +203,8 @@
             return true;
         }
 
-        int throttleDurationMs = getThrottleDurationMillis();
-        if (currentTime - mLastRequestTimeMs < throttleDurationMs) {
+        int mDurationBetweenRequestsMs = getDurationBetweenRequestMillis();
+        if (currentTime - mLastRequestTimeMs < mDurationBetweenRequestsMs) {
             return false;
         }
 
@@ -233,7 +233,7 @@
                     (float) size.getHeight() / boundsInWindow.height());
         }
 
-        args.setGrayscale(displayHashParams.isUseGrayscale());
+        args.setGrayscale(displayHashParams.isGrayscaleBuffer());
 
         SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
                 SurfaceControl.captureLayers(args.build());
@@ -356,11 +356,11 @@
         }
     }
 
-    private int getThrottleDurationMillis() {
+    private int getDurationBetweenRequestMillis() {
         if (!parseXmlProperties()) {
             return 0;
         }
-        return mThrottleDurationMillis;
+        return mDurationBetweenRequestMillis;
     }
 
     private boolean parseXmlProperties() {
@@ -406,8 +406,8 @@
             }
 
             TypedArray sa = res.obtainAttributes(attrs, R.styleable.DisplayHashingService);
-            mThrottleDurationMillis = sa.getInt(
-                    R.styleable.DisplayHashingService_throttleDurationMillis, 0);
+            mDurationBetweenRequestMillis = sa.getInt(
+                    R.styleable.DisplayHashingService_durationBetweenRequestsMillis, 0);
             sa.recycle();
             mParsedXml = true;
             return true;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 84616c0..aa7e6c9 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -116,10 +116,8 @@
     /** Notifies that the input device configuration has changed. */
     @Override
     public void notifyConfigurationChanged() {
-        // TODO(multi-display): Notify proper displays that are associated with this input device.
-
         synchronized (mService.mGlobalLock) {
-            mService.getDefaultDisplayContentLocked().sendNewConfiguration();
+            mService.mRoot.forAllDisplays(DisplayContent::sendNewConfiguration);
         }
 
         synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java
index cd20c82..1cda8d5 100644
--- a/services/core/java/com/android/server/wm/RootDisplayArea.java
+++ b/services/core/java/com/android/server/wm/RootDisplayArea.java
@@ -37,7 +37,7 @@
  * of the whole logical display, or a {@link DisplayAreaGroup} as the root of a partition of the
  * logical display.
  */
-class RootDisplayArea extends DisplayArea<DisplayArea> {
+class RootDisplayArea extends DisplayArea.Dimmable {
 
     /** {@link Feature} that are supported in this {@link DisplayArea} hierarchy. */
     List<DisplayAreaPolicyBuilder.Feature> mFeatures;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d9c5fa4..dc07988 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2657,9 +2657,12 @@
     }
 
     void addStartingWindowsForVisibleActivities() {
+        final ArrayList<Task> addedTasks = new ArrayList<>();
         forAllActivities((r) -> {
-            if (r.mVisibleRequested) {
+            final Task task = r.getTask();
+            if (r.mVisibleRequested && r.mStartingData == null && !addedTasks.contains(task)) {
                 r.showStartingWindow(true /*taskSwitch*/);
+                addedTasks.add(task);
             }
         });
     }
diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java
index 2124ed6..66ae0eb 100644
--- a/services/core/java/com/android/server/wm/SnapshotStartingData.java
+++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java
@@ -39,4 +39,9 @@
         return mService.mStartingSurfaceController.createTaskSnapshotSurface(activity,
                 mSnapshot);
     }
+
+    @Override
+    boolean hasImeSurface() {
+        return mSnapshot.hasImeSurface();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index a5bd797..59de43a 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -40,4 +40,9 @@
      *         {@link StartingSurface#remove}
      */
     abstract StartingSurface createStartingSurface(ActivityRecord activity);
+
+    /** @see android.window.TaskSnapshot#hasImeSurface() */
+    boolean hasImeSurface() {
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index a9b06ca..c3815c1 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -118,7 +118,9 @@
                 return null;
             }
             if (topFullscreenActivity.getWindowConfiguration().getRotation()
-                    != taskSnapshot.getRotation()) {
+                    != taskSnapshot.getRotation()
+                    // Use normal rotation to avoid flickering of IME window in old orientation.
+                    && !taskSnapshot.hasImeSurface()) {
                 // The snapshot should have been checked by ActivityRecord#isSnapshotCompatible
                 // that the activity will be updated to the same rotation as the snapshot. Since
                 // the transition is not started yet, fixed rotation transform needs to be applied
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2a0041a..c6478ee 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2836,14 +2836,13 @@
             getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
         }
 
-        // Do not allow non-resizable tasks to be in a multi-window mode, unless it is in pinned
-        // windowing mode or supports non-resizable tasks in multi-window mode.
-        if (!isResizeable()) {
+        // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
+        // pinned windowing mode.
+        if (!supportsMultiWindow()) {
             final int candidateWindowingMode =
                     windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
             if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
-                    && candidateWindowingMode != WINDOWING_MODE_PINNED
-                    && !mTaskSupervisor.mService.mSupportsNonResizableMultiWindow) {
+                    && candidateWindowingMode != WINDOWING_MODE_PINNED) {
                 getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
                         WINDOWING_MODE_FULLSCREEN);
             }
@@ -4079,6 +4078,7 @@
         info.lastActiveTime = lastActiveTime;
         info.taskDescription = new ActivityManager.TaskDescription(getTaskDescription());
         info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
+        info.supportsMultiWindow = supportsMultiWindow();
         info.configuration.setTo(getConfiguration());
         // Update to the task's current activity type and windowing mode which may differ from the
         // window configuration
@@ -7176,8 +7176,11 @@
         if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task="
                 + tr.mTaskId);
 
-        mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
-        mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_TO_BACK, tr);
+        // Skip the transition for pinned task.
+        if (!inPinnedWindowingMode()) {
+            mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
+            mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_TO_BACK, tr);
+        }
         moveToBack("moveTaskToBackLocked", tr);
 
         if (inPinnedWindowingMode()) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index cda8c4b..87f685e 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1274,7 +1274,15 @@
 
         for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
             if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
-                return mLaunchRootTasks.get(i).task;
+                final Task launchRootTask = mLaunchRootTasks.get(i).task;
+                // Return the focusable root task for improving the UX with staged split screen.
+                final Task adjacentRootTask = launchRootTask != null
+                        ? launchRootTask.mAdjacentTask : null;
+                if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) {
+                    return adjacentRootTask;
+                } else {
+                    return launchRootTask;
+                }
             }
         }
         return null;
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 29677b2..0bc7999 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -612,7 +612,7 @@
 
     private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
             TaskDisplayArea displayArea) {
-        if (!mSupervisor.mService.mSupportsNonResizableMultiWindow || activity.isResizeable()) {
+        if (!activity.supportsFreeform() || activity.isResizeable()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bea733b..b1c7e19 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -37,6 +37,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
+import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -2696,7 +2697,14 @@
             @Nullable ArrayList<WindowContainer> sources) {
         final Task task = asTask();
         if (task != null && !enter && !task.isHomeOrRecentsRootTask()) {
-            mDisplayContent.showImeScreenshot();
+            final InsetsControlTarget imeTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
+            final boolean isImeLayeringTarget = imeTarget != null && imeTarget.getWindow() != null
+                    && imeTarget.getWindow().getTask() == task;
+            // Attach and show the IME screenshot when the task is the IME target and performing
+            // task closing transition to the next task.
+            if (isImeLayeringTarget && AppTransition.isTaskCloseTransitOld(transit)) {
+                mDisplayContent.showImeScreenshot();
+            }
         }
         final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
                 transit, enter, isVoiceInteraction);
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index d076434..51bc99a 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -27,3 +27,4 @@
 per-file com_android_server_security_* = file:/core/java/android/security/OWNERS
 per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS
 per-file com_android_server_vibrator_* = file:/services/core/java/com/android/server/vibrator/OWNERS
+per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
\ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d9fa471..24f73c3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -883,6 +883,12 @@
                 synchronized (getLockObject()) {
                     // Check whether the user is affiliated, *before* removing its data.
                     boolean isRemovedUserAffiliated = isUserAffiliatedWithDeviceLocked(userHandle);
+                    if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
+                        // Disable network and security logging
+                        mInjector.securityLogSetLoggingEnabledProperty(false);
+                        mSecurityLogMonitor.stop();
+                        setNetworkLoggingActiveInternal(false);
+                    }
                     removeUserData(userHandle);
                     if (!isRemovedUserAffiliated) {
                         // We discard the logs when unaffiliated users are deleted (so that the
@@ -8552,11 +8558,12 @@
         synchronized (getLockObject()) {
             enforceCanSetProfileOwnerLocked(
                     caller, who, userHandle, hasIncompatibleAccountsOrNonAdb);
-            Preconditions.checkArgument(isPackageInstalledForUser(who.getPackageName(), userHandle),
-                    "Component " + who + " not installed for userId:" + userHandle);
             final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
-            Preconditions.checkArgument(admin != null && !getUserData(
-                    userHandle).mRemovingAdmins.contains(who), "Not active admin: " + who);
+            Preconditions.checkArgument(
+                    isPackageInstalledForUser(who.getPackageName(), userHandle)
+                            && admin != null
+                            && !getUserData(userHandle).mRemovingAdmins.contains(who),
+                    "Not active admin: " + who);
 
             final int parentUserId = getProfileParentId(userHandle);
             // When trying to set a profile owner on a new user, it may be that this user is
@@ -8896,17 +8903,17 @@
     }
 
     @Override
-    public ComponentName getProfileOwnerAsUser(int userHandle) {
+    public ComponentName getProfileOwnerAsUser(int userId) {
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+        Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
 
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userHandle));
+        CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId));
 
         synchronized (getLockObject()) {
-            return mOwners.getProfileOwnerComponent(userHandle);
+            return mOwners.getProfileOwnerComponent(userId);
         }
     }
 
@@ -9350,19 +9357,20 @@
     public List<UserHandle> listForegroundAffiliatedUsers() {
         checkIsDeviceOwner(getCallerIdentity());
 
-        int userId = mInjector.binderWithCleanCallingIdentity(() -> getCurrentForegroundUserId());
+        return mInjector.binderWithCleanCallingIdentity(() -> {
+            int userId = getCurrentForegroundUserId();
+            boolean isAffiliated;
+            synchronized (getLockObject()) {
+                isAffiliated = isUserAffiliatedWithDeviceLocked(userId);
+            }
 
-        boolean isAffiliated;
-        synchronized (getLockObject()) {
-            isAffiliated = isUserAffiliatedWithDeviceLocked(userId);
-        }
+            if (!isAffiliated) return Collections.emptyList();
 
-        if (!isAffiliated) return Collections.emptyList();
+            List<UserHandle> users = new ArrayList<>(1);
+            users.add(UserHandle.of(userId));
 
-        List<UserHandle> users = new ArrayList<>(1);
-        users.add(UserHandle.of(userId));
-
-        return users;
+            return users;
+        });
     }
 
     protected int getProfileParentId(int userHandle) {
@@ -11931,17 +11939,25 @@
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(isDeviceOwner(caller));
 
+        UserHandle userHandle = caller.getUserHandle();
+        if (mIsAutomotive) {
+            Slogf.v(LOG_TAG, "setLocationEnabled(%s, %b): ignoring for user %s on automotive build",
+                    who.flattenToShortString(), locationEnabled, userHandle);
+            return;
+        }
+
         mInjector.binderWithCleanCallingIdentity(() -> {
             boolean wasLocationEnabled = mInjector.getLocationManager().isLocationEnabledForUser(
-                    caller.getUserHandle());
-            mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled,
-                    caller.getUserHandle());
+                    userHandle);
+            Slogf.v(LOG_TAG, "calling locationManager.setLocationEnabledForUser(%b, %s)",
+                    locationEnabled, userHandle);
+            mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled, userHandle);
 
             // make a best effort to only show the notification if the admin is actually enabling
             // location. this is subject to race conditions with settings changes, but those are
             // unlikely to realistically interfere
             if (locationEnabled && !wasLocationEnabled) {
-                showLocationSettingsEnabledNotification(caller.getUserHandle());
+                showLocationSettingsEnabledNotification(userHandle);
             }
         });
 
@@ -13311,12 +13327,10 @@
         final CallerIdentity caller = getCallerIdentity();
         final long ident = mInjector.binderClearCallingIdentity();
         try {
-            final int uidForPackage = mInjector.getPackageManager().getPackageUidAsUser(
-                    packageName, caller.getUserId());
-            Preconditions.checkArgument(caller.getUid() == uidForPackage,
+            final List<String> callerUidPackageNames = Arrays.asList(
+                    mInjector.getPackageManager().getPackagesForUid(caller.getUid()));
+            Preconditions.checkArgument(callerUidPackageNames.contains(packageName),
                     "Caller uid doesn't match the one for the provided package.");
-        } catch (NameNotFoundException e) {
-            throw new IllegalArgumentException("Invalid package provided " + packageName, e);
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
         }
@@ -14116,7 +14130,7 @@
         }
     }
 
-    private boolean isUserAffiliatedWithDeviceLocked(int userId) {
+    private boolean isUserAffiliatedWithDeviceLocked(@UserIdInt int userId) {
         if (!mOwners.hasDeviceOwner()) {
             return false;
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 285ecfb..a2db6aac 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -15,8 +15,12 @@
  */
 package com.android.server.devicepolicy;
 
+import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
 import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.os.UserHandle;
 
 import com.android.server.devicepolicy.Owners.OwnerDto;
 
@@ -32,8 +36,23 @@
     private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
     private static final String CMD_LIST_OWNERS = "list-owners";
     private static final String CMD_LIST_POLICY_EXEMPT_APPS = "list-policy-exempt-apps";
+    private static final String CMD_SET_ACTIVE_ADMIN = "set-active-admin";
+    private static final String CMD_SET_DEVICE_OWNER = "set-device-owner";
+    private static final String CMD_SET_PROFILE_OWNER = "set-profile-owner";
+    private static final String CMD_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
+    private static final String CMD_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
+    private static final String CMD_FORCE_NETWORK_LOGS = "force-network-logs";
+    private static final String CMD_FORCE_SECURITY_LOGS = "force-security-logs";
+    private static final String CMD_MARK_PO_ON_ORG_OWNED_DEVICE =
+            "mark-profile-owner-on-organization-owned-device";
+
+    private static final String USER_OPTION = "--user";
+    private static final String NAME_OPTION = "--name";
 
     private final DevicePolicyManagerService mService;
+    private int mUserId = UserHandle.USER_SYSTEM;
+    private String mName = "";
+    private ComponentName mComponent;
 
     DevicePolicyManagerServiceShellCommand(DevicePolicyManagerService service) {
         mService = Objects.requireNonNull(service);
@@ -41,7 +60,7 @@
 
     @Override
     public void onHelp() {
-        try (PrintWriter pw = getOutPrintWriter();) {
+        try (PrintWriter pw = getOutPrintWriter()) {
             pw.printf("DevicePolicyManager Service (device_policy) commands:\n\n");
             showHelp(pw);
         }
@@ -52,7 +71,7 @@
         if (cmd == null) {
             return handleDefaultCommands(cmd);
         }
-        try (PrintWriter pw = getOutPrintWriter();) {
+        try (PrintWriter pw = getOutPrintWriter()) {
             switch (cmd) {
                 case CMD_IS_SAFE_OPERATION:
                     return runIsSafeOperation(pw);
@@ -64,6 +83,22 @@
                     return runListOwners(pw);
                 case CMD_LIST_POLICY_EXEMPT_APPS:
                     return runListPolicyExemptApps(pw);
+                case CMD_SET_ACTIVE_ADMIN:
+                    return runSetActiveAdmin(pw);
+                case CMD_SET_DEVICE_OWNER:
+                    return runSetDeviceOwner(pw);
+                case CMD_SET_PROFILE_OWNER:
+                    return runSetProfileOwner(pw);
+                case CMD_REMOVE_ACTIVE_ADMIN:
+                    return runRemoveActiveAdmin(pw);
+                case CMD_CLEAR_FREEZE_PERIOD_RECORD:
+                    return runClearFreezePeriodRecord(pw);
+                case CMD_FORCE_NETWORK_LOGS:
+                    return runForceNetworkLogs(pw);
+                case CMD_FORCE_SECURITY_LOGS:
+                    return runForceSecurityLogs(pw);
+                case CMD_MARK_PO_ON_ORG_OWNED_DEVICE:
+                    return runMarkProfileOwnerOnOrganizationOwnedDevice(pw);
                 default:
                     return onInvalidCommand(pw, cmd);
             }
@@ -75,7 +110,7 @@
             return 0;
         }
 
-        pw.println("Usage: ");
+        pw.printf("Usage: \n");
         showHelp(pw);
         return -1;
     }
@@ -94,6 +129,37 @@
         pw.printf("    Lists the device / profile owners per user \n\n");
         pw.printf("  %s\n", CMD_LIST_POLICY_EXEMPT_APPS);
         pw.printf("    Lists the apps that are exempt from policies\n\n");
+        pw.printf("  %s [ %s <USER_ID> | current ] <COMPONENT>\n",
+                CMD_SET_ACTIVE_ADMIN, USER_OPTION);
+        pw.printf("    Sets the given component as active admin for an existing user.\n\n");
+        pw.printf("  %s [ %s <USER_ID> | current *EXPERIMENTAL* ] [ %s <NAME> ] "
+                + "<COMPONENT>\n", CMD_SET_DEVICE_OWNER, USER_OPTION, NAME_OPTION);
+        pw.printf("    Sets the given component as active admin, and its package as device owner."
+                + "\n\n");
+        pw.printf("  %s [ %s <USER_ID> | current ] [ %s <NAME> ] <COMPONENT>\n",
+                CMD_SET_PROFILE_OWNER, USER_OPTION, NAME_OPTION);
+        pw.printf("    Sets the given component as active admin and profile owner for an existing "
+                + "user.\n\n");
+        pw.printf("  %s [ %s <USER_ID> | current ] [ %s <NAME> ] <COMPONENT>\n",
+                CMD_REMOVE_ACTIVE_ADMIN, USER_OPTION, NAME_OPTION);
+        pw.printf("    Disables an active admin, the admin must have declared android:testOnly in "
+                + "the application in its manifest. This will also remove device and profile "
+                + "owners.\n\n");
+        pw.printf("  %s\n", CMD_CLEAR_FREEZE_PERIOD_RECORD);
+        pw.printf("    Clears framework-maintained record of past freeze periods that the device "
+                + "went through. For use during feature development to prevent triggering "
+                + "restriction on setting freeze periods.\n\n");
+        pw.printf("  %s\n", CMD_FORCE_NETWORK_LOGS);
+        pw.printf("    Makes all network logs available to the DPC and triggers "
+                + "DeviceAdminReceiver.onNetworkLogsAvailable() if needed.\n\n");
+        pw.printf("  %s\n", CMD_FORCE_SECURITY_LOGS);
+        pw.printf("    Makes all security logs available to the DPC and triggers "
+                + "DeviceAdminReceiver.onSecurityLogsAvailable() if needed.\n\n");
+        pw.printf("  %s [ %s <USER_ID> | current ] <COMPONENT>\n",
+                CMD_MARK_PO_ON_ORG_OWNED_DEVICE, USER_OPTION);
+        pw.printf("    Marks the profile owner of the given user as managing an organization-owned"
+                + "device. That will give it access to device identifiers (such as serial number, "
+                + "IMEI and MEID), as well as other privileges.\n\n");
     }
 
     private int runIsSafeOperation(PrintWriter pw) {
@@ -161,7 +227,6 @@
         return 0;
     }
 
-
     private int runListPolicyExemptApps(PrintWriter pw) {
         List<String> apps = mService.listPolicyExemptApps();
         int size = printAndGetSize(pw, apps, "policy exempt app");
@@ -174,4 +239,131 @@
         }
         return 0;
     }
+
+    private int runSetActiveAdmin(PrintWriter pw) {
+        parseArgs(/* canHaveName= */ false);
+        mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
+
+        pw.printf("Success: Active admin set to component %s\n", mComponent.flattenToShortString());
+        return 0;
+    }
+
+    private int runSetDeviceOwner(PrintWriter pw) {
+        parseArgs(/* canHaveName= */ true);
+        mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
+
+        try {
+            if (!mService.setDeviceOwner(mComponent, mName, mUserId)) {
+                throw new RuntimeException(
+                        "Can't set package " + mComponent + " as device owner.");
+            }
+        } catch (Exception e) {
+            // Need to remove the admin that we just added.
+            mService.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
+            throw e;
+        }
+
+        mService.setUserProvisioningState(
+                DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
+
+        pw.printf("Success: Device owner set to package %s\n", mComponent.flattenToShortString());
+        pw.printf("Active admin set to component %s\n", mComponent.flattenToShortString());
+        return 0;
+    }
+
+    private int runRemoveActiveAdmin(PrintWriter pw) {
+        parseArgs(/* canHaveName= */ false);
+        mService.forceRemoveActiveAdmin(mComponent, mUserId);
+        pw.printf("Success: Admin removed %s\n", mComponent);
+        return 0;
+    }
+
+    private int runSetProfileOwner(PrintWriter pw) {
+        parseArgs(/* canHaveName= */ true);
+        mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
+
+        try {
+            if (!mService.setProfileOwner(mComponent, mName, mUserId)) {
+                throw new RuntimeException("Can't set component "
+                        + mComponent.flattenToShortString() + " as profile owner for user "
+                        + mUserId);
+            }
+        } catch (Exception e) {
+            // Need to remove the admin that we just added.
+            mService.removeActiveAdmin(mComponent, mUserId);
+            throw e;
+        }
+
+        mService.setUserProvisioningState(
+                DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
+
+        pw.printf("Success: Active admin and profile owner set to %s for user %d\n",
+                mComponent.flattenToShortString(), mUserId);
+        return 0;
+    }
+
+    private int runClearFreezePeriodRecord(PrintWriter pw) {
+        mService.clearSystemUpdatePolicyFreezePeriodRecord();
+        pw.printf("Success\n");
+        return 0;
+    }
+
+    private int runForceNetworkLogs(PrintWriter pw) {
+        while (true) {
+            long toWait = mService.forceNetworkLogs();
+            if (toWait == 0) {
+                break;
+            }
+            pw.printf("We have to wait for %d milliseconds...\n", toWait);
+            SystemClock.sleep(toWait);
+        }
+        pw.printf("Success\n");
+        return 0;
+    }
+
+    private int runForceSecurityLogs(PrintWriter pw) {
+        while (true) {
+            long toWait = mService.forceSecurityLogs();
+            if (toWait == 0) {
+                break;
+            }
+            pw.printf("We have to wait for %d milliseconds...\n", toWait);
+            SystemClock.sleep(toWait);
+        }
+        pw.printf("Success\n");
+        return 0;
+    }
+
+    private int runMarkProfileOwnerOnOrganizationOwnedDevice(PrintWriter pw) {
+        parseArgs(/* canHaveName= */ false);
+        mService.markProfileOwnerOnOrganizationOwnedDevice(mComponent, mUserId);
+        pw.printf("Success\n");
+        return 0;
+    }
+
+    private void parseArgs(boolean canHaveName) {
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            if (USER_OPTION.equals(opt)) {
+                String arg = getNextArgRequired();
+                mUserId = UserHandle.parseUserArg(arg);
+                if (mUserId == UserHandle.USER_CURRENT) {
+                    mUserId = ActivityManager.getCurrentUser();
+                }
+            } else if (canHaveName && NAME_OPTION.equals(opt)) {
+                mName = getNextArgRequired();
+            } else {
+                throw new IllegalArgumentException("Unknown option: " + opt);
+            }
+        }
+        mComponent = parseComponentName(getNextArgRequired());
+    }
+
+    private ComponentName parseComponentName(String component) {
+        ComponentName cn = ComponentName.unflattenFromString(component);
+        if (cn == null) {
+            throw new IllegalArgumentException("Invalid component " + component);
+        }
+        return cn;
+    }
 }
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 1208ecc..9706d7f 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -23,11 +23,13 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ResolveInfo;
 import android.os.Handler;
 import android.os.IBinder.DeathRecipient;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.UpdateEngine;
 import android.os.UpdateEngineCallback;
 import android.os.UserHandle;
@@ -42,6 +44,9 @@
 import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 
@@ -75,7 +80,7 @@
      */
     public static boolean enabled() {
         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, "enabled",
-            false);
+            false) || SystemProperties.getBoolean("persist.profcollectd.enabled_override", false);
     }
 
     @Override
@@ -297,24 +302,20 @@
             return;
         }
 
-        final boolean uploadReport =
-                DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
-                                        "upload_report", false);
-
         new Thread(() -> {
             try {
                 String reportUuid = mIProfcollect.report();
 
-                if (!uploadReport) {
+                final int profileId = getBBProfileId();
+                String reportDir = "/data/user/" + profileId
+                        + "/com.google.android.apps.internal.betterbug/cache/";
+                String reportPath = reportDir + reportUuid + ".zip";
+
+                if (!Files.exists(Paths.get(reportDir))) {
+                    Log.i(LOG_TAG, "Destination directory does not exist, abort upload.");
                     return;
                 }
 
-                final int profileId = getBBProfileId();
-                mIProfcollect.copy_report_to_bb(profileId, reportUuid);
-                String reportPath =
-                        "/data/user/" + profileId
-                        + "/com.google.android.apps.internal.betterbug/cache/"
-                        + reportUuid + ".zip";
                 Intent uploadIntent =
                         new Intent("com.google.android.apps.betterbug.intent.action.UPLOAD_PROFILE")
                         .setPackage("com.google.android.apps.internal.betterbug")
@@ -323,9 +324,15 @@
                         .putExtra("EXTRA_PROFILE_PATH", reportPath)
                         .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                 Context context = getContext();
-                if (context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0) != null) {
-                    context.sendBroadcast(uploadIntent);
+
+                List<ResolveInfo> receivers =
+                        context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0);
+                if (receivers == null || receivers.isEmpty()) {
+                    Log.i(LOG_TAG, "No one to receive upload intent, abort upload.");
+                    return;
                 }
+                mIProfcollect.copy_report_to_bb(profileId, reportUuid);
+                context.sendBroadcast(uploadIntent);
                 mIProfcollect.delete_report(reportUuid);
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, e.getMessage());
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 3bc1c8a..64dad7f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -80,6 +80,7 @@
 import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX;
 import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
 import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_UNDEFINED;
 import static com.android.server.alarm.AlarmManagerService.TIME_CHANGED_MASK;
 import static com.android.server.alarm.AlarmManagerService.WORKING_INDEX;
 import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
@@ -771,10 +772,10 @@
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, pi6);
         assertEquals(mNowElapsedTest + 6, mTestTimer.getElapsed());
 
-        mService.removeLocked(pi6, null);
+        mService.removeLocked(pi6, null, REMOVE_REASON_UNDEFINED);
         assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed());
 
-        mService.removeLocked(pi8, null);
+        mService.removeLocked(pi8, null, REMOVE_REASON_UNDEFINED);
         assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
     }
 
@@ -1252,7 +1253,8 @@
             setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10, getNewMockPendingIntent());
         }
         assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
-        mService.removeLocked(TEST_CALLING_UID);
+        mService.removeLocked(TEST_CALLING_UID,
+                REMOVE_REASON_UNDEFINED);
         assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
     }
 
@@ -1292,7 +1294,7 @@
         }
         assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
         for (int i = 0; i < numAlarms; i++) {
-            mService.removeLocked(pis[i], null);
+            mService.removeLocked(pis[i], null, REMOVE_REASON_UNDEFINED);
             assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
         }
     }
@@ -1422,7 +1424,7 @@
         mTestTimer.expire();
         assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size());
         for (int i = 0; i < numAlarms; i++) {
-            mService.removeLocked(pis[i], null);
+            mService.removeLocked(pis[i], null, REMOVE_REASON_UNDEFINED);
             assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
         }
     }
@@ -1492,13 +1494,13 @@
         assertEquals(trigger6, mTestTimer.getElapsed());
         assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());
 
-        mService.removeLocked(wakeFromIdle6, null);
+        mService.removeLocked(wakeFromIdle6, null, REMOVE_REASON_UNDEFINED);
 
         assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle10, null));
         assertEquals(trigger10, mTestTimer.getElapsed());
         assertEquals(trigger10, mService.mNextWakeFromIdle.getWhenElapsed());
 
-        mService.removeLocked(wakeFromIdle10, null);
+        mService.removeLocked(wakeFromIdle10, null, REMOVE_REASON_UNDEFINED);
         assertNull(mService.mNextWakeFromIdle);
     }
 
@@ -1524,13 +1526,13 @@
         setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 12, wakeFromIdle12);
         assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());
 
-        mService.removeLocked(wakeFromIdle5, null);
+        mService.removeLocked(wakeFromIdle5, null, REMOVE_REASON_UNDEFINED);
         assertEquals(mNowElapsedTest + 8, mService.mPendingIdleUntil.getWhenElapsed());
 
-        mService.removeLocked(wakeFromIdle8, null);
+        mService.removeLocked(wakeFromIdle8, null, REMOVE_REASON_UNDEFINED);
         assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed());
 
-        mService.removeLocked(idleUntilPi, null);
+        mService.removeLocked(idleUntilPi, null, REMOVE_REASON_UNDEFINED);
         assertNull(mService.mPendingIdleUntil);
 
         setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 15, idleUntilPi);
@@ -1641,7 +1643,7 @@
         verify(pis[0], never()).send(eq(mMockContext), eq(0), any(Intent.class), any(),
                 any(Handler.class), isNull(), any());
 
-        mService.removeLocked(idleUntil, null);
+        mService.removeLocked(idleUntil, null, REMOVE_REASON_UNDEFINED);
         mTestTimer.expire();
         // Now, the first 5 alarms (upto i = 4) should expire.
         for (int i = 0; i < 5; i++) {
@@ -1688,14 +1690,16 @@
                 getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
 
         // Refresh the state
-        mService.removeLocked(TEST_CALLING_UID);
+        mService.removeLocked(TEST_CALLING_UID,
+                REMOVE_REASON_UNDEFINED);
         mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
 
         testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
                 trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
 
         // Refresh the state
-        mService.removeLocked(TEST_CALLING_UID);
+        mService.removeLocked(TEST_CALLING_UID,
+                REMOVE_REASON_UNDEFINED);
         mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
 
         testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index 45f43e8..ee00cb2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -43,9 +43,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.SigningInfo;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -81,8 +84,10 @@
     private static final int APP_PID = 2000;
     private static final int SYSTEM_PID = 558;
     private static final int TEST_USER_ID = UserHandle.USER_SYSTEM;
+    private static final String TEST_PACKAGE_NAME = "com.android.server.accessibility";
     private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
-            "com.android.server.accessibility", "AccessibilitySecurityPolicyTest");
+            TEST_PACKAGE_NAME, "AccessibilitySecurityPolicyTest");
+    private static final String ALLOWED_INSTALL_PACKAGE_NAME = "com.allowed.install.package";
 
     private static final int[] ALWAYS_DISPATCH_EVENTS = {
             AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -142,19 +147,40 @@
     @Mock
     private AccessibilityServiceInfo mMockA11yServiceInfo;
     @Mock
+    private ResolveInfo mMockResolveInfo;
+    @Mock
+    private ServiceInfo mMockServiceInfo;
+    @Mock
+    private ApplicationInfo mMockApplicationInfo;
+    @Mock
+    private ApplicationInfo mMockSourceApplicationInfo;
+    @Mock
+    private PackageInfo mMockSourcePackageInfo;
+    @Mock
     private PolicyWarningUIController mPolicyWarningUIController;
 
     @Before
-    public void setUp() {
+    public void setUp() throws PackageManager.NameNotFoundException {
         MockitoAnnotations.initMocks(this);
         mContext.setMockPackageManager(mMockPackageManager);
         mContext.addMockSystemService(Context.USER_SERVICE, mMockUserManager);
         mContext.addMockSystemService(Context.APP_OPS_SERVICE, mMockAppOpsManager);
         mContext.getOrCreateTestableResources().addOverride(
                 R.dimen.accessibility_focus_highlight_stroke_width, 1);
+        mContext.getOrCreateTestableResources().addOverride(R.array
+                        .config_accessibility_allowed_install_source,
+                new String[]{ALLOWED_INSTALL_PACKAGE_NAME});
 
+        when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
         when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
         when(mMockA11yServiceConnection.getServiceInfo()).thenReturn(mMockA11yServiceInfo);
+        when(mMockPackageManager.getPackageInfo(ALLOWED_INSTALL_PACKAGE_NAME, 0)).thenReturn(
+                mMockSourcePackageInfo);
+
+        mMockResolveInfo.serviceInfo = mMockServiceInfo;
+        mMockServiceInfo.applicationInfo = mMockApplicationInfo;
+        mMockServiceInfo.packageName = TEST_PACKAGE_NAME;
+        mMockSourcePackageInfo.applicationInfo = mMockSourceApplicationInfo;
 
         mA11ySecurityPolicy = new AccessibilitySecurityPolicy(
                 mPolicyWarningUIController, mContext, mMockA11yUserManager);
@@ -595,28 +621,7 @@
     }
 
     @Test
-    public void onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction() {
-        final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
-        boundServices.add(mMockA11yServiceConnection);
-        when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
-
-        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
-
-        verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
-    }
-
-    @Test
-    public void onBoundServicesChanged_unbindA11yCategoryService_noUIControllerAction() {
-        onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction();
-
-        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
-
-        verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
-                any());
-    }
-
-    @Test
-    public void onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction() {
+    public void onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction() {
         final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
         boundServices.add(mMockA11yServiceConnection);
         when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(false);
@@ -628,8 +633,8 @@
     }
 
     @Test
-    public void onBoundServicesChanged_unbindNonA11yCategoryService_activateUIControllerAction() {
-        onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+    public void onBoundServicesChanged_unbindNonA11yToolService_activateUIControllerAction() {
+        onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
 
         mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
 
@@ -638,8 +643,65 @@
     }
 
     @Test
+    public void onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction() {
+        final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+        boundServices.add(mMockA11yServiceConnection);
+        when(mMockApplicationInfo.isSystemApp()).thenReturn(true);
+        when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+
+        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+        verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+    }
+
+    @Test
+    public void onBoundServicesChanged_unbindSystemA11yToolService_noUIControllerAction() {
+        onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction();
+
+        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
+
+        verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
+                any());
+    }
+
+    @Test
+    public void onBoundServicesChanged_bindAllowedSourceA11yToolService_noUIControllerAction()
+            throws PackageManager.NameNotFoundException {
+        final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+        boundServices.add(mMockA11yServiceConnection);
+        when(mMockApplicationInfo.isSystemApp()).thenReturn(false);
+        final InstallSourceInfo installSourceInfo = new InstallSourceInfo(
+                ALLOWED_INSTALL_PACKAGE_NAME, new SigningInfo(), null,
+                ALLOWED_INSTALL_PACKAGE_NAME);
+        when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
+                installSourceInfo);
+        when(mMockSourceApplicationInfo.isSystemApp()).thenReturn(true);
+        when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+
+        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+        verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+    }
+
+    @Test
+    public void onBoundServicesChanged_bindUnknownSourceA11yToolService_activateUIControllerAction()
+            throws PackageManager.NameNotFoundException {
+        final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+        boundServices.add(mMockA11yServiceConnection);
+        when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+        final InstallSourceInfo installSourceInfo = new InstallSourceInfo(null, null, null, null);
+        when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
+                installSourceInfo);
+
+        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+        verify(mPolicyWarningUIController).onNonA11yCategoryServiceBound(eq(TEST_USER_ID),
+                eq(TEST_COMPONENT_NAME));
+    }
+
+    @Test
     public void onSwitchUser_differentUser_activateUIControllerAction() {
-        onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+        onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
 
         mA11ySecurityPolicy.onSwitchUserLocked(2, new HashSet<>());
 
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 4a6c9be..5c7a580 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.apphibernation;
 
+import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
+import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED;
+import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 
 import static org.junit.Assert.assertEquals;
@@ -34,6 +37,9 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.IActivityManager;
+import android.app.usage.UsageEvents.Event;
+import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -81,6 +87,8 @@
 
     private AppHibernationService mAppHibernationService;
     private BroadcastReceiver mBroadcastReceiver;
+    private UsageEventListener mUsageEventListener;
+
     @Mock
     private Context mContext;
     @Mock
@@ -93,8 +101,14 @@
     private UserManager mUserManager;
     @Mock
     private HibernationStateDiskStore<UserLevelState> mUserLevelDiskStore;
+    @Mock
+    private UsageStatsManagerInternal mUsageStatsManagerInternal;
+    @Mock
+    private HibernationStateDiskStore<UserLevelState> mHibernationStateDiskStore;
     @Captor
     private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
+    @Captor
+    private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;
 
     @Before
     public void setUp() throws RemoteException {
@@ -108,6 +122,8 @@
 
         verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
         mBroadcastReceiver = mReceiverCaptor.getValue();
+        verify(mUsageStatsManagerInternal).registerListener(mUsageEventListenerCaptor.capture());
+        mUsageEventListener = mUsageEventListenerCaptor.getValue();
 
         doReturn(mUserInfos).when(mUserManager).getUsers();
 
@@ -284,6 +300,89 @@
         assertEquals(capturedIntents.get(1).getAction(), Intent.ACTION_BOOT_COMPLETED);
     }
 
+    @Test
+    public void testHibernatingPackageIsUnhibernatedForUserWhenUserInteracted() {
+        // GIVEN a package that is currently hibernated for a user
+        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
+
+        // WHEN the package is interacted with by user
+        generateUsageEvent(USER_INTERACTION);
+
+        // THEN the package is not hibernating anymore
+        assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
+    }
+
+    @Test
+    public void testHibernatingPackageIsUnhibernatedForUserWhenActivityResumed() {
+        // GIVEN a package that is currently hibernated for a user
+        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
+
+        // WHEN the package has activity resumed
+        generateUsageEvent(ACTIVITY_RESUMED);
+
+        // THEN the package is not hibernating anymore
+        assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
+    }
+
+    @Test
+    public void testHibernatingPackageIsUnhibernatedForUserWhenComponentUsed() {
+        // GIVEN a package that is currently hibernated for a user
+        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
+
+        // WHEN a package component is used
+        generateUsageEvent(APP_COMPONENT_USED);
+
+        // THEN the package is not hibernating anymore
+        assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
+    }
+
+    @Test
+    public void testHibernatingPackageIsUnhibernatedGloballyWhenUserInteracted() {
+        // GIVEN a package that is currently hibernated globally
+        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+
+        // WHEN the user interacts with the package
+        generateUsageEvent(USER_INTERACTION);
+
+        // THEN the package is not hibernating globally anymore
+        assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
+    }
+
+    @Test
+    public void testHibernatingPackageIsUnhibernatedGloballyWhenActivityResumed() {
+        // GIVEN a package that is currently hibernated globally
+        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+
+        // WHEN activity in package resumed
+        generateUsageEvent(ACTIVITY_RESUMED);
+
+        // THEN the package is not hibernating globally anymore
+        assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
+    }
+
+    @Test
+    public void testHibernatingPackageIsUnhibernatedGloballyWhenComponentUsed() {
+        // GIVEN a package that is currently hibernated globally
+        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+
+        // WHEN a package component is used
+        generateUsageEvent(APP_COMPONENT_USED);
+
+        // THEN the package is not hibernating globally anymore
+        assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
+    }
+
+    /**
+     * Mock a usage event occurring.
+     *
+     * @param usageEventId id of a usage event
+     */
+    private void generateUsageEvent(int usageEventId) {
+        Event event = new Event(usageEventId, 0 /* timestamp */);
+        event.mPackage = PACKAGE_NAME_1;
+        mUsageEventListener.onUsageEvent(USER_ID_1, event);
+    }
+
     /**
      * Add a mock user with one package.
      */
@@ -360,6 +459,11 @@
         }
 
         @Override
+        public UsageStatsManagerInternal getUsageStatsManagerInternal() {
+            return mUsageStatsManagerInternal;
+        }
+
+        @Override
         public Executor getBackgroundExecutor() {
             // Just execute immediately in tests.
             return r -> r.run();
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 b552fd5..79e5865 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -70,7 +70,8 @@
                         mTemporaryFolder.newFolder(),
                         mContext,
                         mContext.getUserId(),
-                        mContext.getPackageName());
+                        mContext.getPackageName(),
+                        /*logger=*/ null);
         mGlobalQuerierUid =
                 mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
     }
@@ -117,9 +118,8 @@
         // No query filters specified, global query can retrieve all documents.
         SearchSpec searchSpec =
                 new SearchSpec.Builder().setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY).build();
-        SearchResultPage searchResultPage =
-                mAppSearchImpl.globalQuery(
-                        "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
+        SearchResultPage searchResultPage = mAppSearchImpl.globalQuery(
+                "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid, /*logger=*/ null);
         assertThat(searchResultPage.getResults()).hasSize(2);
 
         // Document2 will be first since it got indexed later and has a "better", aka more recent
@@ -174,9 +174,8 @@
                         .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
                         .addFilterPackageNames("package1")
                         .build();
-        SearchResultPage searchResultPage =
-                mAppSearchImpl.globalQuery(
-                        "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
+        SearchResultPage searchResultPage = mAppSearchImpl.globalQuery(
+                "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid, /*logger=*/ null);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
 
@@ -186,9 +185,8 @@
                         .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
                         .addFilterPackageNames("package2")
                         .build();
-        searchResultPage =
-                mAppSearchImpl.globalQuery(
-                        "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
+        searchResultPage = mAppSearchImpl.globalQuery(
+                "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid, /*logger=*/ null);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
     }
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
index 11ae76b..28955d6 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
@@ -67,7 +67,8 @@
                         mTemporaryFolder.newFolder(),
                         mContext,
                         mContext.getUserId(),
-                        /*globalQuerierPackage=*/ mContext.getPackageName());
+                        /*globalQuerierPackage=*/ mContext.getPackageName(),
+                        /*logger=*/ null);
         mGlobalQuerierUid =
                 mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
 
@@ -163,7 +164,8 @@
                         mTemporaryFolder.newFolder(),
                         mContext,
                         mContext.getUserId(),
-                        /*globalQuerierPackage=*/ mContext.getPackageName());
+                        /*globalQuerierPackage=*/ mContext.getPackageName(),
+                        /*logger=*/ null);
         VisibilityStore visibilityStore = appSearchImpl.getVisibilityStoreLocked();
 
         // Use some arbitrary callerUid. If we can't find the global querier's uid though,
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 9a7cf80..b367203 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
@@ -82,7 +82,8 @@
                         mTemporaryFolder.newFolder(),
                         context,
                         VisibilityStore.NO_OP_USER_ID,
-                        /*globalQuerierPackage=*/ context.getPackageName());
+                        /*globalQuerierPackage=*/ context.getPackageName(),
+                        /*logger=*/ null);
     }
 
     // TODO(b/175430168) add test to verify reset is working properly.
@@ -604,7 +605,7 @@
         SearchSpec searchSpec =
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
         SearchResultPage searchResultPage =
-                mAppSearchImpl.query("package", "EmptyDatabase", "", searchSpec);
+                mAppSearchImpl.query("package", "EmptyDatabase", "", searchSpec, /*logger=*/ null);
         assertThat(searchResultPage.getResults()).isEmpty();
     }
 
@@ -647,7 +648,7 @@
         SearchSpec searchSpec =
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
         SearchResultPage searchResultPage =
-                mAppSearchImpl.query("package2", "database2", "", searchSpec);
+                mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=*/ null);
         assertThat(searchResultPage.getResults()).isEmpty();
 
         // Insert package2 document
@@ -655,7 +656,9 @@
         mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null);
 
         // No query filters specified. package2 should only get its own documents back.
-        searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
+        searchResultPage =
+                mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=
+         */ null);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
     }
@@ -703,7 +706,7 @@
                         .addFilterPackageNames("package1")
                         .build();
         SearchResultPage searchResultPage =
-                mAppSearchImpl.query("package2", "database2", "", searchSpec);
+                mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=*/ null);
         assertThat(searchResultPage.getResults()).isEmpty();
 
         // Insert package2 document
@@ -716,7 +719,9 @@
                         .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
                         .addFilterPackageNames("package2")
                         .build();
-        searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
+        searchResultPage =
+                mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=
+         */ null);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
     }
@@ -727,7 +732,11 @@
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
         SearchResultPage searchResultPage =
                 mAppSearchImpl.globalQuery(
-                        "", searchSpec, /*callerPackageName=*/ "", /*callerUid=*/ 0);
+                        "",
+                        searchSpec,
+                        /*callerPackageName=*/ "",
+                        /*callerUid=*/ 0,
+                        /*logger=*/ null);
         assertThat(searchResultPage.getResults()).isEmpty();
     }
 
@@ -1033,7 +1042,12 @@
         SearchSpec searchSpec =
                 new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
         SearchResultPage searchResultPage =
-                mAppSearchImpl.query("package", "database", /*queryExpression=*/ "", searchSpec);
+                mAppSearchImpl.query(
+                        "package",
+                        "database",
+                        /*queryExpression=*/ "",
+                        searchSpec,
+                        /*logger=*/ null);
         assertThat(searchResultPage.getResults()).hasSize(1);
         assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
 
@@ -1042,7 +1056,12 @@
 
         // Verify the document is cleared.
         searchResultPage =
-                mAppSearchImpl.query("package2", "database2", /*queryExpression=*/ "", searchSpec);
+                mAppSearchImpl.query(
+                        "package2",
+                        "database2",
+                        /*queryExpression=*/ "",
+                        searchSpec,
+                        /*logger=*/ null);
         assertThat(searchResultPage.getResults()).isEmpty();
 
         // Verify the schema is cleared.
@@ -1244,7 +1263,8 @@
                                 new SearchSpec.Builder()
                                         .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                                         .setRankingStrategy(SearchSpec.RANKING_STRATEGY_USAGE_COUNT)
-                                        .build())
+                                        .build(),
+                                /*logger=*/ null)
                         .getResults();
         assertThat(page).hasSize(2);
         assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1");
@@ -1262,7 +1282,8 @@
                                         .setRankingStrategy(
                                                 SearchSpec
                                                         .RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP)
-                                        .build())
+                                        .build(),
+                                /*logger=*/ null)
                         .getResults();
         assertThat(page).hasSize(2);
         assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id2");
@@ -1279,7 +1300,8 @@
                                         .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                                         .setRankingStrategy(
                                                 SearchSpec.RANKING_STRATEGY_SYSTEM_USAGE_COUNT)
-                                        .build())
+                                        .build(),
+                                /*logger=*/ null)
                         .getResults();
         assertThat(page).hasSize(2);
         assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id2");
@@ -1297,7 +1319,8 @@
                                         .setRankingStrategy(
                                                 SearchSpec
                                                         .RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP)
-                                        .build())
+                                        .build(),
+                                /*logger=*/ null)
                         .getResults();
         assertThat(page).hasSize(2);
         assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1");
@@ -1499,7 +1522,9 @@
                         mTemporaryFolder.newFolder(),
                         context,
                         VisibilityStore.NO_OP_USER_ID,
-                        /*globalQuerierPackage=*/ "");
+                        /*globalQuerierPackage=*/ "",
+                        /*logger
+                        =*/ null);
 
         // Initial check that we could do something at first.
         List<AppSearchSchema> schemas =
@@ -1561,7 +1586,8 @@
                             "query",
                             new SearchSpec.Builder()
                                     .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
-                                    .build());
+                                    .build(),
+                            /*logger=*/ null);
                 });
 
         expectThrows(
@@ -1573,7 +1599,8 @@
                                     .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
                                     .build(),
                             "package",
-                            /*callerUid=*/ 1);
+                            /*callerUid=*/ 1,
+                            /*logger=*/ null);
                 });
 
         expectThrows(
@@ -1647,7 +1674,8 @@
                         appsearchDir,
                         context,
                         VisibilityStore.NO_OP_USER_ID,
-                        /*globalQuerierPackage=*/ "");
+                        /*globalQuerierPackage=*/ "",
+                        /*logger=*/ null);
 
         List<AppSearchSchema> schemas =
                 Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1677,7 +1705,8 @@
                         appsearchDir,
                         context,
                         VisibilityStore.NO_OP_USER_ID,
-                        /*globalQuerierPackage=*/ "");
+                        /*globalQuerierPackage=*/ "",
+                        /*logger=*/ null);
         getResult =
                 appSearchImpl2.getDocument(
                         "package", "database", "namespace1", "id1", Collections.emptyMap());
@@ -1694,7 +1723,8 @@
                         appsearchDir,
                         context,
                         VisibilityStore.NO_OP_USER_ID,
-                        /*globalQuerierPackage=*/ "");
+                        /*globalQuerierPackage=*/ "",
+                        /*logger=*/ null);
 
         List<AppSearchSchema> schemas =
                 Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1748,7 +1778,8 @@
                         appsearchDir,
                         context,
                         VisibilityStore.NO_OP_USER_ID,
-                        /*globalQuerierPackage=*/ "");
+                        /*globalQuerierPackage=*/ "",
+                        /*logger=*/ null);
         expectThrows(
                 AppSearchException.class,
                 () ->
@@ -1774,7 +1805,8 @@
                         appsearchDir,
                         context,
                         VisibilityStore.NO_OP_USER_ID,
-                        /*globalQuerierPackage=*/ "");
+                        /*globalQuerierPackage=*/ "",
+                        /*logger=*/ null);
 
         List<AppSearchSchema> schemas =
                 Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1835,7 +1867,8 @@
                         appsearchDir,
                         context,
                         VisibilityStore.NO_OP_USER_ID,
-                        /*globalQuerierPackage=*/ "");
+                        /*globalQuerierPackage=*/ "",
+                        /*logger=*/ null);
         expectThrows(
                 AppSearchException.class,
                 () ->
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 1194e76..5989bb6 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
@@ -23,13 +23,21 @@
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSchema;
 import android.app.appsearch.GenericDocument;
+import android.app.appsearch.SearchResultPage;
+import android.app.appsearch.SearchSpec;
 import android.content.Context;
 
 import androidx.test.core.app.ApplicationProvider;
 
 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.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.proto.InitializeStatsProto;
 import com.android.server.appsearch.proto.PutDocumentStatsProto;
+import com.android.server.appsearch.proto.QueryStatsProto;
+import com.android.server.appsearch.proto.ScoringSpecProto;
+import com.android.server.appsearch.proto.TermMatchType;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -54,23 +62,93 @@
                         mTemporaryFolder.newFolder(),
                         context,
                         VisibilityStore.NO_OP_USER_ID,
-                        /*globalQuerierPackage=*/ context.getPackageName());
+                        /*globalQuerierPackage=*/ context.getPackageName(),
+                        /*logger=*/ null);
         mLogger = new TestLogger();
     }
 
     // Test only not thread safe.
     public class TestLogger implements AppSearchLogger {
+        @Nullable CallStats mCallStats;
         @Nullable PutDocumentStats mPutDocumentStats;
+        @Nullable InitializeStats mInitializeStats;
+        @Nullable SearchStats mSearchStats;
 
         @Override
         public void logStats(@NonNull CallStats stats) {
-            throw new UnsupportedOperationException();
+            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;
+        }
+    }
+
+    @Test
+    public void testAppSearchLoggerHelper_testCopyNativeStats_initialize() {
+        int nativeLatencyMillis = 3;
+        int nativeDocumentStoreRecoveryCause = InitializeStatsProto.RecoveryCause.DATA_LOSS_VALUE;
+        int nativeIndexRestorationCause =
+                InitializeStatsProto.RecoveryCause.INCONSISTENT_WITH_GROUND_TRUTH_VALUE;
+        int nativeSchemaStoreRecoveryCause =
+                InitializeStatsProto.RecoveryCause.TOTAL_CHECKSUM_MISMATCH_VALUE;
+        int nativeDocumentStoreRecoveryLatencyMillis = 7;
+        int nativeIndexRestorationLatencyMillis = 8;
+        int nativeSchemaStoreRecoveryLatencyMillis = 9;
+        int nativeDocumentStoreDataStatus =
+                InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE;
+        int nativeNumDocuments = 11;
+        int nativeNumSchemaTypes = 12;
+        InitializeStatsProto.Builder nativeInitBuilder =
+                InitializeStatsProto.newBuilder()
+                        .setLatencyMs(nativeLatencyMillis)
+                        .setDocumentStoreRecoveryCause(
+                                InitializeStatsProto.RecoveryCause.forNumber(
+                                        nativeDocumentStoreRecoveryCause))
+                        .setIndexRestorationCause(
+                                InitializeStatsProto.RecoveryCause.forNumber(
+                                        nativeIndexRestorationCause))
+                        .setSchemaStoreRecoveryCause(
+                                InitializeStatsProto.RecoveryCause.forNumber(
+                                        nativeSchemaStoreRecoveryCause))
+                        .setDocumentStoreRecoveryLatencyMs(nativeDocumentStoreRecoveryLatencyMillis)
+                        .setIndexRestorationLatencyMs(nativeIndexRestorationLatencyMillis)
+                        .setSchemaStoreRecoveryLatencyMs(nativeSchemaStoreRecoveryLatencyMillis)
+                        .setDocumentStoreDataStatus(
+                                InitializeStatsProto.DocumentStoreDataStatus.forNumber(
+                                        nativeDocumentStoreDataStatus))
+                        .setNumDocuments(nativeNumDocuments)
+                        .setNumSchemaTypes(nativeNumSchemaTypes);
+        InitializeStats.Builder initBuilder = new InitializeStats.Builder();
+
+        AppSearchLoggerHelper.copyNativeStats(nativeInitBuilder.build(), initBuilder);
+
+        InitializeStats iStats = initBuilder.build();
+        assertThat(iStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+        assertThat(iStats.getDocumentStoreRecoveryCause())
+                .isEqualTo(nativeDocumentStoreRecoveryCause);
+        assertThat(iStats.getIndexRestorationCause()).isEqualTo(nativeIndexRestorationCause);
+        assertThat(iStats.getSchemaStoreRecoveryCause()).isEqualTo(nativeSchemaStoreRecoveryCause);
+        assertThat(iStats.getDocumentStoreRecoveryLatencyMillis())
+                .isEqualTo(nativeDocumentStoreRecoveryLatencyMillis);
+        assertThat(iStats.getIndexRestorationLatencyMillis())
+                .isEqualTo(nativeIndexRestorationLatencyMillis);
+        assertThat(iStats.getSchemaStoreRecoveryLatencyMillis())
+                .isEqualTo(nativeSchemaStoreRecoveryLatencyMillis);
+        assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus);
+        assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments);
+        assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes);
     }
 
     @Test
@@ -111,10 +189,97 @@
         assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens);
     }
 
+    @Test
+    public void testAppSearchLoggerHelper_testCopyNativeStats_search() {
+        int nativeLatencyMillis = 4;
+        int nativeNumTerms = 5;
+        // TODO(b/185804196) query length needs to be added in the native stats.
+        // int nativeQueryLength = 6;
+        int nativeNumNamespacesFiltered = 7;
+        int nativeNumSchemaTypesFiltered = 8;
+        int nativeRequestedPageSize = 9;
+        int nativeNumResultsReturnedCurrentPage = 10;
+        boolean nativeIsFirstPage = true;
+        int nativeParseQueryLatencyMillis = 11;
+        int nativeRankingStrategy = ScoringSpecProto.RankingStrategy.Code.CREATION_TIMESTAMP_VALUE;
+        int nativeNumDocumentsScored = 13;
+        int nativeScoringLatencyMillis = 14;
+        int nativeRankingLatencyMillis = 15;
+        int nativeNumResultsWithSnippets = 16;
+        int nativeDocumentRetrievingLatencyMillis = 17;
+        QueryStatsProto nativeQueryStats =
+                QueryStatsProto.newBuilder()
+                        .setLatencyMs(nativeLatencyMillis)
+                        .setNumTerms(nativeNumTerms)
+                        .setNumNamespacesFiltered(nativeNumNamespacesFiltered)
+                        .setNumSchemaTypesFiltered(nativeNumSchemaTypesFiltered)
+                        .setRequestedPageSize(nativeRequestedPageSize)
+                        .setNumResultsReturnedCurrentPage(nativeNumResultsReturnedCurrentPage)
+                        .setIsFirstPage(nativeIsFirstPage)
+                        .setParseQueryLatencyMs(nativeParseQueryLatencyMillis)
+                        .setRankingStrategy(
+                                ScoringSpecProto.RankingStrategy.Code.forNumber(
+                                        nativeRankingStrategy))
+                        .setNumDocumentsScored(nativeNumDocumentsScored)
+                        .setScoringLatencyMs(nativeScoringLatencyMillis)
+                        .setRankingLatencyMs(nativeRankingLatencyMillis)
+                        .setNumResultsWithSnippets(nativeNumResultsWithSnippets)
+                        .setDocumentRetrievalLatencyMs(nativeDocumentRetrievingLatencyMillis)
+                        .build();
+        SearchStats.Builder qBuilder =
+                new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, "packageName")
+                        .setDatabase("database");
+
+        AppSearchLoggerHelper.copyNativeStats(nativeQueryStats, qBuilder);
+
+        SearchStats sStats = qBuilder.build();
+        assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+        assertThat(sStats.getTermCount()).isEqualTo(nativeNumTerms);
+        // assertThat(sStats.getNativeQueryLength()).isEqualTo(nativeQueryLength);
+        assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(nativeNumNamespacesFiltered);
+        assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(nativeNumSchemaTypesFiltered);
+        assertThat(sStats.getRequestedPageSize()).isEqualTo(nativeRequestedPageSize);
+        assertThat(sStats.getCurrentPageReturnedResultCount())
+                .isEqualTo(nativeNumResultsReturnedCurrentPage);
+        assertThat(sStats.isFirstPage()).isTrue();
+        assertThat(sStats.getParseQueryLatencyMillis()).isEqualTo(nativeParseQueryLatencyMillis);
+        assertThat(sStats.getRankingStrategy()).isEqualTo(nativeRankingStrategy);
+        assertThat(sStats.getScoredDocumentCount()).isEqualTo(nativeNumDocumentsScored);
+        assertThat(sStats.getScoringLatencyMillis()).isEqualTo(nativeScoringLatencyMillis);
+        assertThat(sStats.getRankingLatencyMillis()).isEqualTo(nativeRankingLatencyMillis);
+        assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(nativeNumResultsWithSnippets);
+        assertThat(sStats.getDocumentRetrievingLatencyMillis())
+                .isEqualTo(nativeDocumentRetrievingLatencyMillis);
+    }
+
     //
     // Testing actual logging
     //
     @Test
+    public void testLoggingStats_initialize() throws Exception {
+        Context context = ApplicationProvider.getApplicationContext();
+
+        AppSearchImpl appSearchImpl =
+                AppSearchImpl.create(
+                        mTemporaryFolder.newFolder(),
+                        context,
+                        VisibilityStore.NO_OP_USER_ID,
+                        /*globalQuerierPackage=*/ context.getPackageName(),
+                        mLogger);
+
+        InitializeStats iStats = mLogger.mInitializeStats;
+        assertThat(iStats).isNotNull();
+        assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
+        assertThat(iStats.getTotalLatencyMillis()).isGreaterThan(0);
+        assertThat(iStats.hasDeSync()).isFalse();
+        assertThat(iStats.getNativeLatencyMillis()).isGreaterThan(0);
+        assertThat(iStats.getDocumentStoreDataStatus())
+                .isEqualTo(InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE);
+        assertThat(iStats.getDocumentCount()).isEqualTo(0);
+        assertThat(iStats.getSchemaTypeCount()).isEqualTo(0);
+    }
+
+    @Test
     public void testLoggingStats_putDocument() throws Exception {
         // Insert schema
         final String testPackageName = "testPackage";
@@ -141,4 +306,53 @@
         // The rest of native stats have been tested in testCopyNativeStats
         assertThat(pStats.getNativeDocumentSizeBytes()).isGreaterThan(0);
     }
+
+    @Test
+    public void testLoggingStats_search() throws Exception {
+        // Insert schema
+        final String testPackageName = "testPackage";
+        final String testDatabase = "testDatabase";
+        List<AppSearchSchema> schemas =
+                Collections.singletonList(new AppSearchSchema.Builder("type").build());
+        mAppSearchImpl.setSchema(
+                testPackageName,
+                testDatabase,
+                schemas,
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false,
+                /*version=*/ 0);
+        GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
+        mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger);
+
+        // No query filters specified. package2 should only get its own documents back.
+        SearchSpec searchSpec =
+                new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
+        SearchResultPage searchResultPage =
+                mAppSearchImpl.query(
+                        testPackageName,
+                        testDatabase,
+                        /*QueryExpression=*/ "",
+                        searchSpec,
+                        /*logger=*/ mLogger);
+
+        assertThat(searchResultPage.getResults()).hasSize(1);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
+
+        SearchStats sStats = mLogger.mSearchStats;
+
+        assertThat(sStats).isNotNull();
+        assertThat(sStats.getPackageName()).isEqualTo(testPackageName);
+        assertThat(sStats.getDatabase()).isEqualTo(testDatabase);
+        assertThat(sStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
+        assertThat(sStats.getTotalLatencyMillis()).isGreaterThan(0);
+        assertThat(sStats.getVisibilityScope()).isEqualTo(SearchStats.VISIBILITY_SCOPE_LOCAL);
+        assertThat(sStats.getTermCount()).isEqualTo(0);
+        // assertThat(sStats.getNativeQueryLength()).isEqualTo(0);
+        assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(1);
+        assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(1);
+        assertThat(sStats.getCurrentPageReturnedResultCount()).isEqualTo(1);
+        assertThat(sStats.isFirstPage()).isTrue();
+        assertThat(sStats.getScoredDocumentCount()).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 8dbf249..5c7ccfc 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
@@ -134,4 +134,135 @@
         assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed);
         assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens);
     }
+
+    @Test
+    public void testAppSearchStats_InitializeStats() {
+        int prepareSchemaAndNamespacesLatencyMillis = 1;
+        int prepareVisibilityFileLatencyMillis = 2;
+        int nativeLatencyMillis = 3;
+        int nativeDocumentStoreRecoveryCause = 4;
+        int nativeIndexRestorationCause = 5;
+        int nativeSchemaStoreRecoveryCause = 6;
+        int nativeDocumentStoreRecoveryLatencyMillis = 7;
+        int nativeIndexRestorationLatencyMillis = 8;
+        int nativeSchemaStoreRecoveryLatencyMillis = 9;
+        int nativeDocumentStoreDataStatus = 10;
+        int nativeNumDocuments = 11;
+        int nativeNumSchemaTypes = 12;
+
+        final InitializeStats.Builder iStatsBuilder =
+                new InitializeStats.Builder()
+                        .setStatusCode(TEST_STATUS_CODE)
+                        .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
+                        .setHasDeSync(/* hasDeSyncs= */ true)
+                        .setPrepareSchemaAndNamespacesLatencyMillis(
+                                prepareSchemaAndNamespacesLatencyMillis)
+                        .setPrepareVisibilityStoreLatencyMillis(prepareVisibilityFileLatencyMillis)
+                        .setNativeLatencyMillis(nativeLatencyMillis)
+                        .setDocumentStoreRecoveryCause(nativeDocumentStoreRecoveryCause)
+                        .setIndexRestorationCause(nativeIndexRestorationCause)
+                        .setSchemaStoreRecoveryCause(nativeSchemaStoreRecoveryCause)
+                        .setDocumentStoreRecoveryLatencyMillis(
+                                nativeDocumentStoreRecoveryLatencyMillis)
+                        .setIndexRestorationLatencyMillis(nativeIndexRestorationLatencyMillis)
+                        .setSchemaStoreRecoveryLatencyMillis(nativeSchemaStoreRecoveryLatencyMillis)
+                        .setDocumentStoreDataStatus(nativeDocumentStoreDataStatus)
+                        .setDocumentCount(nativeNumDocuments)
+                        .setSchemaTypeCount(nativeNumSchemaTypes);
+        final InitializeStats iStats = iStatsBuilder.build();
+
+        assertThat(iStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
+        assertThat(iStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
+        assertThat(iStats.hasDeSync()).isTrue();
+        assertThat(iStats.getPrepareSchemaAndNamespacesLatencyMillis())
+                .isEqualTo(prepareSchemaAndNamespacesLatencyMillis);
+        assertThat(iStats.getPrepareVisibilityStoreLatencyMillis())
+                .isEqualTo(prepareVisibilityFileLatencyMillis);
+        assertThat(iStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+        assertThat(iStats.getDocumentStoreRecoveryCause())
+                .isEqualTo(nativeDocumentStoreRecoveryCause);
+        assertThat(iStats.getIndexRestorationCause()).isEqualTo(nativeIndexRestorationCause);
+        assertThat(iStats.getSchemaStoreRecoveryCause()).isEqualTo(nativeSchemaStoreRecoveryCause);
+        assertThat(iStats.getDocumentStoreRecoveryLatencyMillis())
+                .isEqualTo(nativeDocumentStoreRecoveryLatencyMillis);
+        assertThat(iStats.getIndexRestorationLatencyMillis())
+                .isEqualTo(nativeIndexRestorationLatencyMillis);
+        assertThat(iStats.getSchemaStoreRecoveryLatencyMillis())
+                .isEqualTo(nativeSchemaStoreRecoveryLatencyMillis);
+        assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus);
+        assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments);
+        assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes);
+    }
+
+    @Test
+    public void testAppSearchStats_SearchStats() {
+        int rewriteSearchSpecLatencyMillis = 1;
+        int rewriteSearchResultLatencyMillis = 2;
+        int visibilityScope = SearchStats.VISIBILITY_SCOPE_LOCAL;
+        int nativeLatencyMillis = 4;
+        int nativeNumTerms = 5;
+        int nativeQueryLength = 6;
+        int nativeNumNamespacesFiltered = 7;
+        int nativeNumSchemaTypesFiltered = 8;
+        int nativeRequestedPageSize = 9;
+        int nativeNumResultsReturnedCurrentPage = 10;
+        boolean nativeIsFirstPage = true;
+        int nativeParseQueryLatencyMillis = 11;
+        int nativeRankingStrategy = 12;
+        int nativeNumDocumentsScored = 13;
+        int nativeScoringLatencyMillis = 14;
+        int nativeRankingLatencyMillis = 15;
+        int nativeNumResultsSnippeted = 16;
+        int nativeDocumentRetrievingLatencyMillis = 17;
+        final SearchStats.Builder sStatsBuilder =
+                new SearchStats.Builder(visibilityScope, TEST_PACKAGE_NAME)
+                        .setDatabase(TEST_DATA_BASE)
+                        .setStatusCode(TEST_STATUS_CODE)
+                        .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
+                        .setRewriteSearchSpecLatencyMillis(rewriteSearchSpecLatencyMillis)
+                        .setRewriteSearchResultLatencyMillis(rewriteSearchResultLatencyMillis)
+                        .setNativeLatencyMillis(nativeLatencyMillis)
+                        .setTermCount(nativeNumTerms)
+                        .setQueryLength(nativeQueryLength)
+                        .setFilteredNamespaceCount(nativeNumNamespacesFiltered)
+                        .setFilteredSchemaTypeCount(nativeNumSchemaTypesFiltered)
+                        .setRequestedPageSize(nativeRequestedPageSize)
+                        .setCurrentPageReturnedResultCount(nativeNumResultsReturnedCurrentPage)
+                        .setIsFirstPage(nativeIsFirstPage)
+                        .setParseQueryLatencyMillis(nativeParseQueryLatencyMillis)
+                        .setRankingStrategy(nativeRankingStrategy)
+                        .setScoredDocumentCount(nativeNumDocumentsScored)
+                        .setScoringLatencyMillis(nativeScoringLatencyMillis)
+                        .setRankingLatencyMillis(nativeRankingLatencyMillis)
+                        .setResultWithSnippetsCount(nativeNumResultsSnippeted)
+                        .setDocumentRetrievingLatencyMillis(nativeDocumentRetrievingLatencyMillis);
+        final SearchStats sStats = sStatsBuilder.build();
+
+        assertThat(sStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+        assertThat(sStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
+        assertThat(sStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
+        assertThat(sStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
+        assertThat(sStats.getRewriteSearchSpecLatencyMillis())
+                .isEqualTo(rewriteSearchSpecLatencyMillis);
+        assertThat(sStats.getRewriteSearchResultLatencyMillis())
+                .isEqualTo(rewriteSearchResultLatencyMillis);
+        assertThat(sStats.getVisibilityScope()).isEqualTo(visibilityScope);
+        assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+        assertThat(sStats.getTermCount()).isEqualTo(nativeNumTerms);
+        assertThat(sStats.getQueryLength()).isEqualTo(nativeQueryLength);
+        assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(nativeNumNamespacesFiltered);
+        assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(nativeNumSchemaTypesFiltered);
+        assertThat(sStats.getRequestedPageSize()).isEqualTo(nativeRequestedPageSize);
+        assertThat(sStats.getCurrentPageReturnedResultCount())
+                .isEqualTo(nativeNumResultsReturnedCurrentPage);
+        assertThat(sStats.isFirstPage()).isTrue();
+        assertThat(sStats.getParseQueryLatencyMillis()).isEqualTo(nativeParseQueryLatencyMillis);
+        assertThat(sStats.getRankingStrategy()).isEqualTo(nativeRankingStrategy);
+        assertThat(sStats.getScoredDocumentCount()).isEqualTo(nativeNumDocumentsScored);
+        assertThat(sStats.getScoringLatencyMillis()).isEqualTo(nativeScoringLatencyMillis);
+        assertThat(sStats.getRankingLatencyMillis()).isEqualTo(nativeRankingLatencyMillis);
+        assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(nativeNumResultsSnippeted);
+        assertThat(sStats.getDocumentRetrievingLatencyMillis())
+                .isEqualTo(nativeDocumentRetrievingLatencyMillis);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 81570a1..fe0df58 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -253,6 +253,8 @@
 
         doReturn(new String[] {admin.getPackageName()}).when(mServices.ipackageManager)
             .getPackagesForUid(eq(packageUid));
+        doReturn(new String[] {admin.getPackageName()}).when(mServices.packageManager)
+                .getPackagesForUid(eq(packageUid));
         // Set up getPackageInfo().
         markPackageAsInstalled(admin.getPackageName(), ai, UserHandle.getUserId(packageUid));
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index c16e498..ec5228f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -108,6 +108,7 @@
 import com.android.server.pm.ShortcutUser.PackageWithUser;
 
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -416,8 +417,11 @@
         mManager.pushDynamicShortcut(s1);
         assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), "s1");
         assertEquals(0, getCallerShortcut("s1").getRank());
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s1"), eq(USER_0));
 
         // Test push when other shortcuts exist
+        Mockito.reset(mMockUsageStatsManagerInternal);
         assertTrue(mManager.setDynamicShortcuts(list(s1, s2)));
         assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), "s1", "s2");
         mManager.pushDynamicShortcut(s3);
@@ -426,25 +430,38 @@
         assertEquals(0, getCallerShortcut("s3").getRank());
         assertEquals(1, getCallerShortcut("s1").getRank());
         assertEquals(2, getCallerShortcut("s2").getRank());
+        verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s1"), eq(USER_0));
+        verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s2"), eq(USER_0));
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s3"), eq(USER_0));
 
         mInjectedCurrentTimeMillis += INTERVAL; // reset
 
         // Push with set rank
+        Mockito.reset(mMockUsageStatsManagerInternal);
         s4.setRank(2);
         mManager.pushDynamicShortcut(s4);
         assertEquals(2, getCallerShortcut("s4").getRank());
         assertEquals(3, getCallerShortcut("s2").getRank());
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s4"), eq(USER_0));
 
         // Push existing shortcut with set rank
+        Mockito.reset(mMockUsageStatsManagerInternal);
         final ShortcutInfo s4_2 = makeShortcut("s4");
         s4_2.setRank(4);
         mManager.pushDynamicShortcut(s4_2);
         assertEquals(2, getCallerShortcut("s2").getRank());
         assertEquals(3, getCallerShortcut("s4").getRank());
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s4"), eq(USER_0));
 
         mInjectedCurrentTimeMillis += INTERVAL; // reset
 
         // Test push as last
+        Mockito.reset(mMockUsageStatsManagerInternal);
         mManager.pushDynamicShortcut(s5);
         assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()),
                 "s1", "s2", "s3", "s4", "s5");
@@ -453,25 +470,34 @@
         assertEquals(2, getCallerShortcut("s1").getRank());
         assertEquals(3, getCallerShortcut("s2").getRank());
         assertEquals(4, getCallerShortcut("s4").getRank());
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s5"), eq(USER_0));
 
         // Push when max has already reached
+        Mockito.reset(mMockUsageStatsManagerInternal);
         mManager.pushDynamicShortcut(s6);
         assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()),
                 "s1", "s2", "s3", "s5", "s6");
         assertEquals(0, getCallerShortcut("s6").getRank());
         assertEquals(1, getCallerShortcut("s5").getRank());
         assertEquals(4, getCallerShortcut("s2").getRank());
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s6"), eq(USER_0));
 
         mInjectedCurrentTimeMillis += INTERVAL; // reset
 
         // Push with different activity
+        Mockito.reset(mMockUsageStatsManagerInternal);
         s7.setActivity(makeComponent(ShortcutActivity2.class));
         mManager.pushDynamicShortcut(s7);
         assertEquals(makeComponent(ShortcutActivity2.class),
                 getCallerShortcut("s7").getActivity());
         assertEquals(0, getCallerShortcut("s7").getRank());
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s7"), eq(USER_0));
 
         // Push to update shortcut with different activity
+        Mockito.reset(mMockUsageStatsManagerInternal);
         final ShortcutInfo s1_2 = makeShortcut("s1");
         s1_2.setActivity(makeComponent(ShortcutActivity2.class));
         s1_2.setRank(1);
@@ -482,10 +508,13 @@
         assertEquals(1, getCallerShortcut("s5").getRank());
         assertEquals(2, getCallerShortcut("s3").getRank());
         assertEquals(3, getCallerShortcut("s2").getRank());
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s1"), eq(USER_0));
 
         mInjectedCurrentTimeMillis += INTERVAL; // reset
 
         // Test push when dropped shortcut is cached
+        Mockito.reset(mMockUsageStatsManagerInternal);
         s8.setLongLived();
         s8.setRank(100);
         mManager.pushDynamicShortcut(s8);
@@ -494,14 +523,19 @@
             mInjectCheckAccessShortcutsPermission = true;
             mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s8"), HANDLE_USER_0,
                     CACHE_OWNER_0);
+            verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                    eq(CALLING_PACKAGE_1), eq("s8"), eq(USER_0));
         });
 
+        Mockito.reset(mMockUsageStatsManagerInternal);
         mManager.pushDynamicShortcut(s9);
         assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()),
                 "s1", "s2", "s3", "s5", "s6", "s7", "s9");
         // Verify s13 stayed as cached
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
                 "s8");
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s9"), eq(USER_0));
     }
 
     public void testUnlimitedCalls() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index ca77049..7241fa0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -2138,7 +2138,6 @@
             mManager.reportShortcutUsed("s2");
             verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
                     eq(CALLING_PACKAGE_1), eq("s2"), eq(USER_10));
-
         });
         runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
             // Try with a different package.
@@ -2158,7 +2157,6 @@
             mManager.reportShortcutUsed("s3");
             verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
                     eq(CALLING_PACKAGE_2), eq("s3"), eq(USER_10));
-
         });
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index d13687c..4d2d2f1 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -307,6 +307,56 @@
         assertEquals(1, mIntegerCaptor.getValue().intValue());
     }
 
+    @Test
+    public void create_invalidSensor() throws Exception {
+        Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
+        when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of());
+
+        String configString = "<device-state-config>\n"
+                + "    <device-state>\n"
+                + "        <identifier>1</identifier>\n"
+                + "        <name>CLOSED</name>\n"
+                + "        <conditions>\n"
+                + "            <sensor>\n"
+                + "                <type>" + sensor.getStringType() + "</type>\n"
+                + "                <name>" + sensor.getName() + "</name>\n"
+                + "                <value>\n"
+                + "                    <max>90</max>\n"
+                + "                </value>\n"
+                + "            </sensor>\n"
+                + "        </conditions>\n"
+                + "    </device-state>\n"
+                + "    <device-state>\n"
+                + "        <identifier>2</identifier>\n"
+                + "        <name>HALF_OPENED</name>\n"
+                + "        <conditions>\n"
+                + "            <sensor>\n"
+                + "                <type>" + sensor.getStringType() + "</type>\n"
+                + "                <name>" + sensor.getName() + "</name>\n"
+                + "                <value>\n"
+                + "                    <min-inclusive>90</min-inclusive>\n"
+                + "                    <max>180</max>\n"
+                + "                </value>\n"
+                + "            </sensor>\n"
+                + "        </conditions>\n"
+                + "    </device-state>\n"
+                + "</device-state-config>\n";
+        DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+        DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
+                config);
+
+        DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+        provider.setListener(listener);
+
+        verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+        assertArrayEquals(
+                new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"),
+                        }, mDeviceStateArrayCaptor.getValue());
+        // onStateChanged() should be called because the provider could not find the sensor.
+        verify(listener).onStateChanged(mIntegerCaptor.capture());
+        assertEquals(1, mIntegerCaptor.getValue().intValue());
+    }
+
     private static Sensor newSensor(String name, String type) throws Exception {
         Constructor<Sensor> constructor = Sensor.class.getDeclaredConstructor();
         constructor.setAccessible(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index e1eef76..e09606e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -54,6 +54,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -1909,6 +1912,32 @@
         verify(t).show(mDisplayContent.mImeScreenshot);
     }
 
+    @UseTestDisplay(addWindows = {W_INPUT_METHOD}, addAllCommonWindows = true)
+    @Test
+    public void testShowImeScreenshot() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+        task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
+        doReturn(true).when(task).okToAnimate();
+        ArrayList<WindowContainer> sources = new ArrayList<>();
+        sources.add(activity);
+
+        mDisplayContent.setImeLayeringTarget(win);
+        spyOn(mDisplayContent);
+
+        // Expecting the IME screenshot only be attached when performing task closing transition.
+        task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
+                false /* isVoiceInteraction */, sources);
+        verify(mDisplayContent).showImeScreenshot();
+
+        clearInvocations(mDisplayContent);
+        activity.applyAnimation(null, TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, false /* enter */,
+                false /* isVoiceInteraction */, sources);
+        verify(mDisplayContent, never()).showImeScreenshot();
+    }
+
     @Test
     public void testRotateBounds_keepSamePhysicalPosition() {
         final DisplayContent dc =
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 9289ce4..67b273a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -101,6 +101,29 @@
     }
 
     @Test
+    public void getLaunchRootTask_checksFocusedRootTask() {
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        final Task rootTask = createTaskWithActivity(
+                taskDisplayArea,
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, ON_TOP, true);
+        rootTask.mCreatedByOrganizer = true;
+
+        final Task adjacentRootTask = createTask(
+                mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+        adjacentRootTask.mCreatedByOrganizer = true;
+        adjacentRootTask.mAdjacentTask = rootTask;
+        rootTask.mAdjacentTask = adjacentRootTask;
+
+        taskDisplayArea.setLaunchRootTask(rootTask,
+                new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
+
+        Task actualRootTask = taskDisplayArea.getLaunchRootTask(
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, null /* options */,
+                null /* sourceTask */, 0 /*launchFlags*/);
+        assertTrue(actualRootTask.isFocusedRootTaskOnDisplay());
+    }
+
+    @Test
     public void getLaunchRootTask_fromLaunchAdjacentFlagRoot_checksAdjacentRoot() {
         final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
         final Task rootTask = createTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 2389d2d..13ef998 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -80,6 +80,7 @@
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
+import android.view.Display;
 import android.view.DisplayInfo;
 
 import androidx.test.filters.MediumTest;
@@ -1338,6 +1339,16 @@
         verify(display).onDescendantOrientationChanged(same(task));
     }
 
+    @Test
+    public void testGetNonNullDimmerOnUntrustedDisplays() {
+        final DisplayInfo untrustedDisplayInfo = new DisplayInfo(mDisplayInfo);
+        untrustedDisplayInfo.flags &= ~Display.FLAG_TRUSTED;
+        final DisplayContent untrustedDisplay = createNewDisplay(untrustedDisplayInfo);
+        final ActivityRecord activity = createActivityRecord(untrustedDisplay);
+        activity.setOccludesParent(false);
+        assertNotNull(activity.getTask().getDimmer());
+    }
+
     private Task getTestTask() {
         final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         return task.getBottomMostTask();
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index d8bd6a5..c5fc436 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -3448,4 +3448,13 @@
     public Handler getHandler() {
         return mHandler;
     }
+
+    /**
+     * Sets this {@link ConnectionService} ready for testing purposes.
+     * @hide
+     */
+    @VisibleForTesting
+    public void setReadyForTest() {
+        mAreAccountsInitialized = true;
+    }
 }
diff --git a/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
index 179248d..33b0bbd2 100644
--- a/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
+++ b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
@@ -81,6 +81,15 @@
     }
 
     /**
+     * @return true if the SIP message start line is considered a response.
+     */
+    public static boolean isSipResponse(String startLine) {
+        String[] splitLine = splitStartLineAndVerify(startLine);
+        if (splitLine == null) return false;
+        return verifySipResponse(splitLine);
+    }
+
+    /**
      * Return the via branch parameter, which is used to identify the transaction ID (request and
      * response pair) in a SIP transaction.
      * @param headerString The string containing the headers of the SIP message.
@@ -140,7 +149,12 @@
         return !headers.isEmpty() ? headers.get(0).second : null;
     }
 
-    private static String[] splitStartLineAndVerify(String startLine) {
+    /**
+     * Validate that the start line is correct and split into its three segments.
+     * @param startLine The start line to verify and split.
+     * @return The split start line, which will always have three segments.
+     */
+    public static String[] splitStartLineAndVerify(String startLine) {
         String[] splitLine = startLine.split(" ");
         if (isStartLineMalformed(splitLine)) return null;
         return splitLine;
@@ -184,7 +198,7 @@
      *                           (This is internally an equalsIgnoreMatch comparison).
      * @return the matched header keys and values.
      */
-    private static List<Pair<String, String>> parseHeaders(String headerString,
+    public static List<Pair<String, String>> parseHeaders(String headerString,
             boolean stopAtFirstMatch, String... matchingHeaderKeys) {
         // Ensure there is no leading whitespace
         headerString = removeLeadingWhitespace(headerString);
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 3b28616..8df41fb 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -183,16 +183,6 @@
     }
 
     /**
-     * @return the absolute radio frequency channel number for this physical channel,
-     * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown.
-     * @deprecated Use {@link #getDownlinkChannelNumber()} to get the channel number.
-     */
-    @Deprecated
-    public int getChannelNumber() {
-        return getDownlinkChannelNumber();
-    }
-
-    /**
      * @return the rough frequency range for this physical channel,
      * {@link ServiceState#FREQUENCY_RANGE_UNKNOWN} if unknown.
      * @see {@link ServiceState#FREQUENCY_RANGE_LOW}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 05f5d29..179aead 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15036,6 +15036,15 @@
             "CAPABILITY_SLICING_CONFIG_SUPPORTED";
 
     /**
+     * Indicates whether PHYSICAL_CHANNEL_CONFIG HAL1.6 is supported. See comments on
+     * respective methods for more information.
+     *
+     * @hide
+     */
+    public static final String CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED =
+            "CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED";
+
+    /**
      * A list of the radio interface capability values with public valid constants.
      *
      * Here is a related list for the systemapi-only valid constants:
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 08f5613..8b6f2b5 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1568,16 +1568,6 @@
     }
 
     /**
-     * Same as {@link #getApnTypeString(int)}, but returns "Unknown" instead of an empty string
-     * when provided with an invalid int for compatibility purposes.
-     * @hide
-     */
-    public static @NonNull String getApnTypeStringInternal(@ApnType int apnType) {
-        String result = getApnTypeString(apnType);
-        return TextUtils.isEmpty(result) ? "Unknown" : result;
-    }
-
-    /**
      * Converts the string representation of an APN type to its integer representation.
      *
      * @param apnType APN type as a string
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
index ffbfde6..08513c2 100644
--- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -26,6 +26,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
+import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -561,7 +562,12 @@
         builder.setSipCniHeader(getString(KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING));
         builder.setSipAssociatedUriHeader(getString(KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING));
         if (getBoolean(KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, false)) {
-            builder.setPublicGruuUri(Uri.parse(getString(KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING)));
+            String uri = getString(KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING);
+            Uri gruuUri = null;
+            if (!TextUtils.isEmpty(uri)) {
+                gruuUri = Uri.parse(uri);
+            }
+            builder.setPublicGruuUri(gruuUri);
         }
         if (getBoolean(KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL, false)) {
             builder.setIpSecConfiguration(new SipDelegateConfiguration.IpSecConfiguration(
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index aa2e6b9..a504c9ab 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -81,13 +81,18 @@
     public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2;
 
     /**
-     * The SIP message has an invalid start line and the message can not be sent.
+     * The SIP message has an invalid start line and the message can not be sent or the start line
+     * failed validation due to the request containing a restricted SIP request method.
+     * {@link SipDelegateConnection}s can not send SIP requests for the methods: REGISTER, PUBLISH,
+     * or OPTIONS.
      */
     public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3;
 
     /**
      * One or more of the header fields in the header section of the outgoing SIP message is invalid
-     * and the SIP message can not be sent.
+     * or contains a restricted header value and the SIP message can not be sent.
+     * {@link SipDelegateConnection}s can not send SIP SUBSCRIBE requests for the "Event" header
+     * value of "presence".
      */
     public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4;
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 9ac504b..96c7c0a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -116,7 +116,7 @@
         }
     }
 
-    @Presubmit
+    @FlakyTest(bugId = 185400889)
     @Test
     open fun noUncoveredRegions() {
         testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
@@ -134,7 +134,7 @@
         testSpec.launcherWindowBecomesVisible()
     }
 
-    @Presubmit
+    @FlakyTest(bugId = 185400889)
     @Test
     open fun launcherLayerReplacesApp() {
         testSpec.launcherLayerReplacesApp(testApp)
diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
index 87cfb34..f23ba26 100644
--- a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
+++ b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
@@ -36,15 +36,15 @@
     @Test
     fun testParcelUnparcel() {
         val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST)
-        assertEquals(TEST_OWNER_UID, testInfo.ownerUid)
-        assertEquals(TEST_IFACE, testInfo.iface)
-        assertEquals(TEST_IFACE_LIST, testInfo.underlyingIfaces)
+        assertEquals(TEST_OWNER_UID, testInfo.getOwnerUid())
+        assertEquals(TEST_IFACE, testInfo.getInterface())
+        assertEquals(TEST_IFACE_LIST, testInfo.getUnderlyingInterfaces())
         assertParcelSane(testInfo, 3)
 
         val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf())
-        assertEquals(0, emptyInfo.ownerUid)
-        assertEquals(String(), emptyInfo.iface)
-        assertEquals(listOf(), emptyInfo.underlyingIfaces)
+        assertEquals(0, emptyInfo.getOwnerUid())
+        assertEquals(String(), emptyInfo.getInterface())
+        assertEquals(listOf(), emptyInfo.getUnderlyingInterfaces())
         assertParcelSane(emptyInfo, 3)
     }
 }
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index d966f70..f277e94 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5898,9 +5898,9 @@
             assertEquals("Should have exactly one VPN:", 1, infos.length);
             UnderlyingNetworkInfo info = infos[0];
             assertEquals("Unexpected VPN owner:", (int) vpnUid, info.getOwnerUid());
-            assertEquals("Unexpected VPN interface:", vpnIfname, info.getIface());
+            assertEquals("Unexpected VPN interface:", vpnIfname, info.getInterface());
             assertSameElementsNoDuplicates(underlyingIfaces,
-                    info.getUnderlyingIfaces().toArray(new String[0]));
+                    info.getUnderlyingInterfaces().toArray(new String[0]));
         } else {
             assertEquals(0, infos.length);
             return;
@@ -6044,8 +6044,8 @@
         // network for the VPN...
         verify(mStatsManager, never()).notifyNetworkStatus(any(List.class),
                 any(List.class), any() /* anyString() doesn't match null */,
-                argThat(infos -> infos.get(0).getUnderlyingIfaces().size() == 1
-                        && WIFI_IFNAME.equals(infos.get(0).getUnderlyingIfaces().get(0))));
+                argThat(infos -> infos.get(0).getUnderlyingInterfaces().size() == 1
+                        && WIFI_IFNAME.equals(infos.get(0).getUnderlyingInterfaces().get(0))));
         verifyNoMoreInteractions(mStatsManager);
         reset(mStatsManager);
 
@@ -6059,8 +6059,8 @@
         waitForIdle();
         verify(mStatsManager).notifyNetworkStatus(any(List.class),
                 any(List.class), any() /* anyString() doesn't match null */,
-                argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingIfaces().size() == 1
-                        && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingIfaces().get(0))));
+                argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingInterfaces().size() == 1
+                        && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingInterfaces().get(0))));
         mEthernetNetworkAgent.disconnect();
         waitForIdle();
         reset(mStatsManager);
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 7cfd275..9410886 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -82,7 +82,7 @@
     // Public for use in VcnGatewayConnectionTest
     public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
         final VcnGatewayConnectionConfig.Builder builder =
-                newBuilder().setRetryIntervalsMs(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
+                newBuilder().setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
 
         for (int caps : exposedCaps) {
             builder.addExposedCapability(caps);
@@ -134,7 +134,7 @@
     @Test
     public void testBuilderRequiresNonNullRetryInterval() {
         try {
-            newBuilder().setRetryIntervalsMs(null);
+            newBuilder().setRetryIntervalsMillis(null);
             fail("Expected exception due to invalid retryIntervalMs");
         } catch (IllegalArgumentException e) {
         }
@@ -143,7 +143,7 @@
     @Test
     public void testBuilderRequiresNonEmptyRetryInterval() {
         try {
-            newBuilder().setRetryIntervalsMs(new long[0]);
+            newBuilder().setRetryIntervalsMillis(new long[0]);
             fail("Expected exception due to invalid retryIntervalMs");
         } catch (IllegalArgumentException e) {
         }
@@ -174,7 +174,7 @@
 
         assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams());
 
-        assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
+        assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
         assertEquals(MAX_MTU, config.getMaxMtu());
     }
 
diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
index 3156190..582275d 100644
--- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
@@ -16,6 +16,8 @@
 
 package android.net.vcn;
 
+import static android.net.NetworkCapabilities.REDACT_ALL;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static org.junit.Assert.assertEquals;
@@ -37,6 +39,12 @@
     private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO);
 
     @Test
+    public void testRedactionDefaults() {
+        assertEquals(REDACT_ALL, CELL_UNDERLYING_INFO.getRedaction());
+        assertEquals(REDACT_ALL, WIFI_UNDERLYING_INFO.getRedaction());
+    }
+
+    @Test
     public void testGetWifiInfo() {
         assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo());
 
@@ -51,6 +59,18 @@
     }
 
     @Test
+    public void testMakeCopySetsRedactions() {
+        assertEquals(
+                REDACT_FOR_NETWORK_SETTINGS,
+                ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
+                        .getRedaction());
+        assertEquals(
+                REDACT_FOR_NETWORK_SETTINGS,
+                ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
+                        .getRedaction());
+    }
+
+    @Test
     public void testEquals() {
         assertEquals(CELL_UNDERLYING_INFO, CELL_UNDERLYING_INFO);
         assertEquals(WIFI_UNDERLYING_INFO, WIFI_UNDERLYING_INFO);
@@ -64,8 +84,29 @@
     }
 
     private void verifyParcelingIsNull(VcnTransportInfo vcnTransportInfo) {
+        // Verify redacted by default
         Parcel parcel = Parcel.obtain();
         vcnTransportInfo.writeToParcel(parcel, 0 /* flags */);
+        parcel.setDataPosition(0);
+
         assertNull(VcnTransportInfo.CREATOR.createFromParcel(parcel));
     }
+
+    @Test
+    public void testParcelUnparcelNotRedactedForSysUi() {
+        verifyParcelingForSysUi(CELL_UNDERLYING_INFO);
+        verifyParcelingForSysUi(WIFI_UNDERLYING_INFO);
+    }
+
+    private void verifyParcelingForSysUi(VcnTransportInfo vcnTransportInfo) {
+        // Allow fully unredacted; SysUI will have all the relevant permissions.
+        final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy(0);
+        final Parcel parcel = Parcel.obtain();
+        unRedacted.writeToParcel(parcel, 0 /* flags */);
+        parcel.setDataPosition(0);
+
+        final VcnTransportInfo unparceled = VcnTransportInfo.CREATOR.createFromParcel(parcel);
+        assertEquals(vcnTransportInfo, unparceled);
+        assertEquals(REDACT_ALL, unparceled.getRedaction());
+    }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 90ee738..eedaac4 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -343,6 +343,31 @@
         assertFalse(mGatewayConnection.isInSafeMode());
     }
 
+    @Test
+    public void testSubsequentFailedValidationTriggersSafeMode() throws Exception {
+        triggerChildOpened();
+        mTestLooper.dispatchAll();
+
+        triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
+        assertFalse(mGatewayConnection.isInSafeMode());
+
+        // Trigger a failed validation, and the subsequent safemode timeout.
+        triggerValidation(NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+        mTestLooper.dispatchAll();
+
+        final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mDeps, times(2))
+                .newWakeupMessage(
+                        eq(mVcnContext),
+                        any(),
+                        eq(VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM),
+                        runnableCaptor.capture());
+        runnableCaptor.getValue().run();
+        mTestLooper.dispatchAll();
+
+        assertTrue(mGatewayConnection.isInSafeMode());
+    }
+
     private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
         triggerChildOpened();
         mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index 044bef5..a88f112 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -38,7 +38,7 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mFirstRetryInterval = mConfig.getRetryIntervalsMs()[0];
+        mFirstRetryInterval = mConfig.getRetryIntervalsMillis()[0];
 
         mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
         mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState);
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index b78f48c..6364ccd 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -78,6 +78,8 @@
       return "interpolator";
     case ResourceType::kLayout:
       return "layout";
+    case ResourceType::kMacro:
+      return "macro";
     case ResourceType::kMenu:
       return "menu";
     case ResourceType::kMipmap:
@@ -119,6 +121,7 @@
     {"integer", ResourceType::kInteger},
     {"interpolator", ResourceType::kInterpolator},
     {"layout", ResourceType::kLayout},
+    {"macro", ResourceType::kMacro},
     {"menu", ResourceType::kMenu},
     {"mipmap", ResourceType::kMipmap},
     {"navigation", ResourceType::kNavigation},
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index cf93870..307c21d 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -57,6 +57,7 @@
   kInteger,
   kInterpolator,
   kLayout,
+  kMacro,
   kMenu,
   kMipmap,
   kNavigation,
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 24c60b7..1efabbb 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -627,6 +627,16 @@
     }
 
     return true;
+  } else if (resource_type == "macro") {
+    if (!maybe_name) {
+      diag_->Error(DiagMessage(out_resource->source)
+                   << "<" << parser->element_name() << "> missing 'name' attribute");
+      return false;
+    }
+
+    out_resource->name.type = ResourceType::kMacro;
+    out_resource->name.entry = maybe_name.value().to_string();
+    return ParseMacro(parser, out_resource);
   }
 
   if (can_be_item) {
@@ -726,16 +736,8 @@
   return true;
 }
 
-/**
- * Reads the entire XML subtree and attempts to parse it as some Item,
- * with typeMask denoting which items it can be. If allowRawValue is
- * true, a RawString is returned if the XML couldn't be parsed as
- * an Item. If allowRawValue is false, nullptr is returned in this
- * case.
- */
-std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
-                                               const uint32_t type_mask,
-                                               const bool allow_raw_value) {
+std::optional<FlattenedXmlSubTree> ResourceParser::CreateFlattenSubTree(
+    xml::XmlPullParser* parser) {
   const size_t begin_xml_line = parser->line_number();
 
   std::string raw_value;
@@ -745,30 +747,60 @@
     return {};
   }
 
-  if (!style_string.spans.empty()) {
+  return FlattenedXmlSubTree{.raw_value = raw_value,
+                             .style_string = style_string,
+                             .untranslatable_sections = untranslatable_sections,
+                             .namespace_resolver = parser,
+                             .source = source_.WithLine(begin_xml_line)};
+}
+
+/**
+ * Reads the entire XML subtree and attempts to parse it as some Item,
+ * with typeMask denoting which items it can be. If allowRawValue is
+ * true, a RawString is returned if the XML couldn't be parsed as
+ * an Item. If allowRawValue is false, nullptr is returned in this
+ * case.
+ */
+std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser, const uint32_t type_mask,
+                                               const bool allow_raw_value) {
+  auto sub_tree = CreateFlattenSubTree(parser);
+  if (!sub_tree.has_value()) {
+    return {};
+  }
+  return ParseXml(sub_tree.value(), type_mask, allow_raw_value, *table_, config_, *diag_);
+}
+
+std::unique_ptr<Item> ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub_tree,
+                                               const uint32_t type_mask, const bool allow_raw_value,
+                                               ResourceTable& table,
+                                               const android::ConfigDescription& config,
+                                               IDiagnostics& diag) {
+  if (!xmlsub_tree.style_string.spans.empty()) {
     // This can only be a StyledString.
     std::unique_ptr<StyledString> styled_string =
-        util::make_unique<StyledString>(table_->string_pool.MakeRef(
-            style_string, StringPool::Context(StringPool::Context::kNormalPriority, config_)));
-    styled_string->untranslatable_sections = std::move(untranslatable_sections);
+        util::make_unique<StyledString>(table.string_pool.MakeRef(
+            xmlsub_tree.style_string,
+            StringPool::Context(StringPool::Context::kNormalPriority, config)));
+    styled_string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
     return std::move(styled_string);
   }
 
   auto on_create_reference = [&](const ResourceName& name) {
     // name.package can be empty here, as it will assume the package name of the
     // table.
-    std::unique_ptr<Id> id = util::make_unique<Id>();
-    id->SetSource(source_.WithLine(begin_xml_line));
-    table_->AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), diag_);
+    auto id = util::make_unique<Id>();
+    id->SetSource(xmlsub_tree.source);
+    return table.AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), &diag);
   };
 
   // Process the raw value.
-  std::unique_ptr<Item> processed_item =
-      ResourceUtils::TryParseItemForAttribute(raw_value, type_mask, on_create_reference);
+  std::unique_ptr<Item> processed_item = ResourceUtils::TryParseItemForAttribute(
+      xmlsub_tree.raw_value, type_mask, on_create_reference);
   if (processed_item) {
     // Fix up the reference.
-    if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
-      ResolvePackage(parser, ref);
+    if (auto ref = ValueCast<Reference>(processed_item.get())) {
+      ref->allow_raw = allow_raw_value;
+      ResolvePackage(xmlsub_tree.namespace_resolver, ref);
     }
     return processed_item;
   }
@@ -777,17 +809,16 @@
   if (type_mask & android::ResTable_map::TYPE_STRING) {
     // Use the trimmed, escaped string.
     std::unique_ptr<String> string = util::make_unique<String>(
-        table_->string_pool.MakeRef(style_string.str, StringPool::Context(config_)));
-    string->untranslatable_sections = std::move(untranslatable_sections);
+        table.string_pool.MakeRef(xmlsub_tree.style_string.str, StringPool::Context(config)));
+    string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
     return std::move(string);
   }
 
   if (allow_raw_value) {
     // We can't parse this so return a RawString if we are allowed.
-    return util::make_unique<RawString>(
-        table_->string_pool.MakeRef(util::TrimWhitespace(raw_value),
-                                    StringPool::Context(config_)));
-  } else if (util::TrimWhitespace(raw_value).empty()) {
+    return util::make_unique<RawString>(table.string_pool.MakeRef(
+        util::TrimWhitespace(xmlsub_tree.raw_value), StringPool::Context(config)));
+  } else if (util::TrimWhitespace(xmlsub_tree.raw_value).empty()) {
     // If the text is empty, and the value is not allowed to be a string, encode it as a @null.
     return ResourceUtils::MakeNull();
   }
@@ -850,6 +881,35 @@
   return true;
 }
 
+bool ResourceParser::ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+  auto sub_tree = CreateFlattenSubTree(parser);
+  if (!sub_tree) {
+    return false;
+  }
+
+  if (out_resource->config != ConfigDescription::DefaultConfig()) {
+    diag_->Error(DiagMessage(out_resource->source)
+                 << "<macro> tags cannot be declared in configurations other than the default "
+                    "configuration'");
+    return false;
+  }
+
+  auto macro = std::make_unique<Macro>();
+  macro->raw_value = std::move(sub_tree->raw_value);
+  macro->style_string = std::move(sub_tree->style_string);
+  macro->untranslatable_sections = std::move(sub_tree->untranslatable_sections);
+
+  for (const auto& decl : parser->package_decls()) {
+    macro->alias_namespaces.emplace_back(
+        Macro::Namespace{.alias = decl.prefix,
+                         .package_name = decl.package.package,
+                         .is_private = decl.package.private_namespace});
+  }
+
+  out_resource->value = std::move(macro);
+  return true;
+}
+
 bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   if (options_.visibility) {
     diag_->Error(DiagMessage(out_resource->source)
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index af0db8c..5c92def 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -57,6 +57,14 @@
   Maybe<Visibility::Level> visibility;
 };
 
+struct FlattenedXmlSubTree {
+  std::string raw_value;
+  StyleString style_string;
+  std::vector<UntranslatableSection> untranslatable_sections;
+  xml::IPackageDeclStack* namespace_resolver;
+  Source source;
+};
+
 /*
  * Parses an XML file for resources and adds them to a ResourceTable.
  */
@@ -67,9 +75,16 @@
                  const ResourceParserOptions& options = {});
   bool Parse(xml::XmlPullParser* parser);
 
+  static std::unique_ptr<Item> ParseXml(const FlattenedXmlSubTree& xmlsub_tree, uint32_t type_mask,
+                                        bool allow_raw_value, ResourceTable& table,
+                                        const android::ConfigDescription& config,
+                                        IDiagnostics& diag);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ResourceParser);
 
+  std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser);
+
   // Parses the XML subtree as a StyleString (flattened XML representation for strings with
   // formatting). If parsing fails, false is returned and the out parameters are left in an
   // unspecified state. Otherwise,
@@ -96,7 +111,7 @@
 
   bool ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource, uint32_t format);
   bool ParseString(xml::XmlPullParser* parser, ParsedResource* out_resource);
-
+  bool ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
@@ -108,8 +123,7 @@
   bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
   Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
                                                const android::StringPiece& tag);
-  bool ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
-                  ParsedResource* out_resource);
+  bool ParseStyle(ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
   bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 4a509be..279ebcba 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -336,6 +336,90 @@
   EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_ANY));
 }
 
+TEST_F(ResourceParserTest, ParseMacro) {
+  std::string input = R"(<macro name="foo">12345</macro>)";
+  ASSERT_TRUE(TestParse(input));
+
+  Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+  ASSERT_THAT(macro, NotNull());
+  EXPECT_THAT(macro->raw_value, Eq("12345"));
+  EXPECT_THAT(macro->style_string.str, Eq("12345"));
+  EXPECT_THAT(macro->style_string.spans, IsEmpty());
+  EXPECT_THAT(macro->untranslatable_sections, IsEmpty());
+  EXPECT_THAT(macro->alias_namespaces, IsEmpty());
+}
+
+TEST_F(ResourceParserTest, ParseMacroUntranslatableSection) {
+  std::string input = R"(<macro name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+This being <b><xliff:g>human</xliff:g></b> is a guest house.</macro>)";
+  ASSERT_TRUE(TestParse(input));
+
+  Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+  ASSERT_THAT(macro, NotNull());
+  EXPECT_THAT(macro->raw_value, Eq("\nThis being human is a guest house."));
+  EXPECT_THAT(macro->style_string.str, Eq(" This being human is a guest house."));
+  EXPECT_THAT(macro->style_string.spans.size(), Eq(1));
+  EXPECT_THAT(macro->style_string.spans[0].name, Eq("b"));
+  EXPECT_THAT(macro->style_string.spans[0].first_char, Eq(12));
+  EXPECT_THAT(macro->style_string.spans[0].last_char, Eq(16));
+  ASSERT_THAT(macro->untranslatable_sections.size(), Eq(1));
+  EXPECT_THAT(macro->untranslatable_sections[0].start, Eq(12));
+  EXPECT_THAT(macro->untranslatable_sections[0].end, Eq(17));
+  EXPECT_THAT(macro->alias_namespaces, IsEmpty());
+}
+
+TEST_F(ResourceParserTest, ParseMacroNamespaces) {
+  std::string input = R"(<macro name="foo" xmlns:app="http://schemas.android.com/apk/res/android">
+@app:string/foo</macro>)";
+  ASSERT_TRUE(TestParse(input));
+
+  Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+  ASSERT_THAT(macro, NotNull());
+  EXPECT_THAT(macro->raw_value, Eq("\n@app:string/foo"));
+  EXPECT_THAT(macro->style_string.str, Eq("@app:string/foo"));
+  EXPECT_THAT(macro->style_string.spans, IsEmpty());
+  EXPECT_THAT(macro->untranslatable_sections, IsEmpty());
+  EXPECT_THAT(macro->alias_namespaces.size(), Eq(1));
+  EXPECT_THAT(macro->alias_namespaces[0].alias, Eq("app"));
+  EXPECT_THAT(macro->alias_namespaces[0].package_name, Eq("android"));
+  EXPECT_THAT(macro->alias_namespaces[0].is_private, Eq(false));
+}
+
+TEST_F(ResourceParserTest, ParseMacroReference) {
+  std::string input = R"(<string name="res_string">@macro/foo</string>)";
+  ASSERT_TRUE(TestParse(input));
+
+  Reference* macro = test::GetValue<Reference>(&table_, "string/res_string");
+  ASSERT_THAT(macro, NotNull());
+  EXPECT_THAT(macro->type_flags, Eq(ResTable_map::TYPE_STRING));
+  EXPECT_THAT(macro->allow_raw, Eq(false));
+
+  input = R"(<style name="foo">
+               <item name="bar">@macro/foo</item>
+             </style>)";
+
+  ASSERT_TRUE(TestParse(input));
+  Style* style = test::GetValue<Style>(&table_, "style/foo");
+  ASSERT_THAT(style, NotNull());
+  EXPECT_THAT(style->entries.size(), Eq(1));
+
+  macro = ValueCast<Reference>(style->entries[0].value.get());
+  ASSERT_THAT(macro, NotNull());
+  EXPECT_THAT(macro->type_flags, Eq(0U));
+  EXPECT_THAT(macro->allow_raw, Eq(true));
+}
+
+TEST_F(ResourceParserTest, ParseMacroNoNameFail) {
+  std::string input = R"(<macro>12345</macro>)";
+  ASSERT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, ParseMacroNonDefaultConfigurationFail) {
+  const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
+  std::string input = R"(<macro name="foo">12345</macro>)";
+  ASSERT_FALSE(TestParse(input, watch_config));
+}
+
 // Old AAPT allowed attributes to be defined under different configurations, but ultimately
 // stored them with the default configuration. Check that we have the same behavior.
 TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 5b43df6..e0e80ac 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -628,7 +628,7 @@
 
 std::unique_ptr<Item> TryParseItemForAttribute(
     const StringPiece& value, uint32_t type_mask,
-    const std::function<void(const ResourceName&)>& on_create_reference) {
+    const std::function<bool(const ResourceName&)>& on_create_reference) {
   using android::ResTable_map;
 
   auto null_or_empty = TryParseNullOrEmpty(value);
@@ -639,8 +639,11 @@
   bool create = false;
   auto reference = TryParseReference(value, &create);
   if (reference) {
+    reference->type_flags = type_mask;
     if (create && on_create_reference) {
-      on_create_reference(reference->name.value());
+      if (!on_create_reference(reference->name.value())) {
+        return {};
+      }
     }
     return std::move(reference);
   }
@@ -689,7 +692,7 @@
  */
 std::unique_ptr<Item> TryParseItemForAttribute(
     const StringPiece& str, const Attribute* attr,
-    const std::function<void(const ResourceName&)>& on_create_reference) {
+    const std::function<bool(const ResourceName&)>& on_create_reference) {
   using android::ResTable_map;
 
   const uint32_t type_mask = attr->type_mask;
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index f77766e..be493db 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -204,11 +204,11 @@
  */
 std::unique_ptr<Item> TryParseItemForAttribute(
     const android::StringPiece& value, const Attribute* attr,
-    const std::function<void(const ResourceName&)>& on_create_reference = {});
+    const std::function<bool(const ResourceName&)>& on_create_reference = {});
 
 std::unique_ptr<Item> TryParseItemForAttribute(
     const android::StringPiece& value, uint32_t type_mask,
-    const std::function<void(const ResourceName&)>& on_create_reference = {});
+    const std::function<bool(const ResourceName&)>& on_create_reference = {});
 
 uint32_t AndroidTypeToAttributeTypeMask(uint16_t type);
 
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 574bd2e..2a90f26 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -111,12 +111,15 @@
   if (!other) {
     return false;
   }
-  return reference_type == other->reference_type &&
-         private_reference == other->private_reference && id == other->id &&
-         name == other->name;
+  return reference_type == other->reference_type && private_reference == other->private_reference &&
+         id == other->id && name == other->name && type_flags == other->type_flags;
 }
 
 bool Reference::Flatten(android::Res_value* out_value) const {
+  if (name && name.value().type == ResourceType::kMacro) {
+    return false;
+  }
+
   const ResourceId resid = id.value_or_default(ResourceId(0));
   const bool dynamic = resid.is_valid() && is_dynamic;
 
@@ -551,7 +554,7 @@
   return this_type_mask == that_type_mask;
 }
 
-std::string Attribute::MaskString() const {
+std::string Attribute::MaskString(uint32_t type_mask) {
   if (type_mask == android::ResTable_map::TYPE_ANY) {
     return "any";
   }
@@ -650,6 +653,10 @@
   return out.str();
 }
 
+std::string Attribute::MaskString() const {
+  return MaskString(type_mask);
+}
+
 void Attribute::Print(std::ostream* out) const {
   *out << "(attr) " << MaskString();
 
@@ -1017,6 +1024,21 @@
        << " [" << util::Joiner(entries, ", ") << "]";
 }
 
+bool Macro::Equals(const Value* value) const {
+  const Macro* other = ValueCast<Macro>(value);
+  if (!other) {
+    return false;
+  }
+  return other->raw_value == raw_value && other->style_string.spans == style_string.spans &&
+         other->style_string.str == style_string.str &&
+         other->untranslatable_sections == untranslatable_sections &&
+         other->alias_namespaces == alias_namespaces;
+}
+
+void Macro::Print(std::ostream* out) const {
+  *out << "(macro) ";
+}
+
 bool operator<(const Reference& a, const Reference& b) {
   int cmp = a.name.value_or_default({}).compare(b.name.value_or_default({}));
   if (cmp != 0) return cmp < 0;
@@ -1149,4 +1171,9 @@
   return CopyValueFields(std::move(new_value), value);
 }
 
+std::unique_ptr<Macro> CloningValueTransformer::TransformDerived(const Macro* value) {
+  auto new_value = std::make_unique<Macro>(*value);
+  return CopyValueFields(std::move(new_value), value);
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 025864d..d11b013 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -164,6 +164,8 @@
   Reference::Type reference_type;
   bool private_reference = false;
   bool is_dynamic = false;
+  std::optional<uint32_t> type_flags;
+  bool allow_raw;
 
   Reference();
   explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
@@ -311,6 +313,8 @@
   bool IsCompatibleWith(const Attribute& attr) const;
 
   std::string MaskString() const;
+  static std::string MaskString(uint32_t type_mask);
+
   void Print(std::ostream* out) const override;
   bool Matches(const Item& item, DiagMessage* out_msg = nullptr) const;
 };
@@ -362,6 +366,28 @@
   void MergeWith(Styleable* styleable);
 };
 
+struct Macro : public TransformableValue<Macro, BaseValue<Macro>> {
+  std::string raw_value;
+  StyleString style_string;
+  std::vector<UntranslatableSection> untranslatable_sections;
+
+  struct Namespace {
+    std::string alias;
+    std::string package_name;
+    bool is_private;
+
+    bool operator==(const Namespace& right) const {
+      return alias == right.alias && package_name == right.package_name &&
+             is_private == right.is_private;
+    }
+  };
+
+  std::vector<Namespace> alias_namespaces;
+
+  bool Equals(const Value* value) const override;
+  void Print(std::ostream* out) const override;
+};
+
 template <typename T>
 typename std::enable_if<std::is_base_of<Value, T>::value, std::ostream&>::type operator<<(
     std::ostream& out, const std::unique_ptr<T>& value) {
@@ -388,6 +414,7 @@
   std::unique_ptr<Array> TransformDerived(const Array* value) override;
   std::unique_ptr<Plural> TransformDerived(const Plural* value) override;
   std::unique_ptr<Styleable> TransformDerived(const Styleable* value) override;
+  std::unique_ptr<Macro> TransformDerived(const Macro* value) override;
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 4247ec5..b45c040 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -273,6 +273,7 @@
     Styleable styleable = 3;
     Array array = 4;
     Plural plural = 5;
+    MacroBody macro = 6;
   }
 }
 
@@ -304,6 +305,13 @@
 
   // Whether this reference is dynamic.
   Boolean is_dynamic = 5;
+
+  // The type flags used when compiling the reference. Used for substituting the contents of macros.
+  uint32 type_flags = 6;
+
+  // Whether raw string values would have been accepted in place of this reference definition. Used
+  // for substituting the contents of macros.
+  bool allow_raw = 7;
 }
 
 // A value that represents an ID. This is just a placeholder, as ID values are used to occupy a
@@ -591,3 +599,32 @@
   // The optional interpreted/compiled version of the `value` string.
   Item compiled_item = 6;
 }
+
+message MacroBody {
+  string raw_string = 1;
+  StyleString style_string = 2;
+  repeated UntranslatableSection untranslatable_sections = 3;
+  repeated NamespaceAlias namespace_stack = 4;
+  SourcePosition source = 5;
+}
+
+message NamespaceAlias {
+  string prefix = 1;
+  string package_name = 2;
+  bool is_private = 3;
+}
+
+message StyleString {
+  message Span {
+    string name = 1;
+    uint32 start_index = 2;
+    uint32 end_index = 3;
+  }
+  string str = 1;
+  repeated Span spans = 2;
+}
+
+message UntranslatableSection {
+  uint64 start_index = 1;
+  uint64 end_index = 2;
+}
\ No newline at end of file
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 1006ca9..3457e0b 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -36,6 +36,10 @@
   std::string name;
   uint32_t first_char;
   uint32_t last_char;
+
+  bool operator==(const Span& right) const {
+    return name == right.name && first_char == right.first_char && last_char == right.last_char;
+  }
 };
 
 struct StyleString {
diff --git a/tools/aapt2/ValueTransformer.cpp b/tools/aapt2/ValueTransformer.cpp
index 6eb2e30..2d7996b 100644
--- a/tools/aapt2/ValueTransformer.cpp
+++ b/tools/aapt2/ValueTransformer.cpp
@@ -46,5 +46,6 @@
 VALUE_CREATE_VALUE_DECL(Array);
 VALUE_CREATE_VALUE_DECL(Plural);
 VALUE_CREATE_VALUE_DECL(Styleable);
+VALUE_CREATE_VALUE_DECL(Macro);
 
 }  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/ValueTransformer.h b/tools/aapt2/ValueTransformer.h
index 6925111..6fc4a19 100644
--- a/tools/aapt2/ValueTransformer.h
+++ b/tools/aapt2/ValueTransformer.h
@@ -37,6 +37,7 @@
 struct Array;
 struct Plural;
 struct Styleable;
+struct Macro;
 
 #define AAPT_TRANSFORM_VALUE(T)                                    \
   virtual std::unique_ptr<T> TransformDerived(const T* value) = 0; \
@@ -97,6 +98,7 @@
   AAPT_TRANSFORM_VALUE(Array);
   AAPT_TRANSFORM_VALUE(Plural);
   AAPT_TRANSFORM_VALUE(Styleable);
+  AAPT_TRANSFORM_VALUE(Macro);
 
  protected:
   StringPool* const pool_;
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 4e74ec3..d0c9d89 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -43,6 +43,9 @@
   virtual void Visit(Array* value) { VisitAny(value); }
   virtual void Visit(Plural* value) { VisitAny(value); }
   virtual void Visit(Styleable* value) { VisitAny(value); }
+  virtual void Visit(Macro* value) {
+    VisitAny(value);
+  }
 };
 
 // Const version of ValueVisitor.
@@ -92,6 +95,9 @@
   virtual void Visit(const Styleable* value) {
     VisitAny(value);
   }
+  virtual void Visit(const Macro* value) {
+    VisitAny(value);
+  }
 };
 
 // NOLINT, do not add parentheses around T.
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 2c57fb2..e4d0f3b 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -462,7 +462,7 @@
   // that existing projects have out-of-date references which pass compilation.
   xml::StripAndroidStudioAttributes(doc->root.get());
 
-  XmlReferenceLinker xml_linker;
+  XmlReferenceLinker xml_linker(table);
   if (!options_.do_not_fail_on_missing_resources && !xml_linker.Consume(context_, doc)) {
     return {};
   }
@@ -2112,7 +2112,7 @@
         std::unique_ptr<xml::XmlResource> split_manifest =
             GenerateSplitManifest(app_info_, *split_constraints_iter);
 
-        XmlReferenceLinker linker;
+        XmlReferenceLinker linker(&final_table_);
         if (!linker.Consume(context_, split_manifest.get())) {
           context_->GetDiagnostics()->Error(DiagMessage()
                                             << "failed to create Split AndroidManifest.xml");
@@ -2143,7 +2143,7 @@
       // So we give it a package name so it can see local resources.
       manifest_xml->file.name.package = context_->GetCompilationPackage();
 
-      XmlReferenceLinker manifest_linker;
+      XmlReferenceLinker manifest_linker(&final_table_);
       if (options_.merge_only || manifest_linker.Consume(context_, manifest_xml.get())) {
         if (options_.generate_proguard_rules_path &&
             !proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) {
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index d1e6d39..3118eb8 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -24,6 +24,7 @@
 
 using testing::Eq;
 using testing::HasSubstr;
+using testing::IsNull;
 using testing::Ne;
 using testing::NotNull;
 
@@ -532,4 +533,109 @@
   EXPECT_THAT(*result, Eq(0x01fd0072));
 }
 
+TEST_F(LinkTest, MacroSubstitution) {
+  StdErrDiagnostics diag;
+  const std::string values =
+      R"(<resources xmlns:an="http://schemas.android.com/apk/res/android">
+           <macro name="is_enabled">true</macro>
+           <macro name="deep_is_enabled">@macro/is_enabled</macro>
+           <macro name="attr_ref">?is_enabled_attr</macro>
+           <macro name="raw_string">Hello World!</macro>
+           <macro name="android_ref">@an:color/primary_text_dark</macro>
+
+           <attr name="is_enabled_attr" />
+           <public type="attr" name="is_enabled_attr" id="0x7f010000"/>
+
+           <string name="is_enabled_str">@macro/is_enabled</string>
+           <bool name="is_enabled_bool">@macro/deep_is_enabled</bool>
+
+           <array name="my_array">
+             <item>@macro/is_enabled</item>
+           </array>
+
+           <style name="MyStyle">
+              <item name="android:background">@macro/attr_ref</item>
+              <item name="android:fontFamily">@macro/raw_string</item>
+           </style>
+         </resources>)";
+
+  const std::string xml_values =
+      R"(<SomeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                     android:background="@macro/android_ref"
+                     android:fontFamily="@macro/raw_string">
+         </SomeLayout>)";
+
+  // Build a library with a public attribute
+  const std::string lib_res = GetTestPath("test-res");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), values, lib_res, &diag));
+  ASSERT_TRUE(CompileFile(GetTestPath("res/layout/layout.xml"), xml_values, lib_res, &diag));
+
+  const std::string lib_apk = GetTestPath("test.apk");
+  // clang-format off
+  auto lib_link_args = LinkCommandBuilder(this)
+      .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
+      .AddCompiledResDir(lib_res, &diag)
+      .AddFlag("--no-auto-version")
+      .Build(lib_apk);
+  // clang-format on
+  ASSERT_TRUE(Link(lib_link_args, &diag));
+
+  auto apk = LoadedApk::LoadApkFromPath(lib_apk, &diag);
+  ASSERT_THAT(apk, NotNull());
+
+  // Test that the type flags determines the value type
+  auto actual_bool =
+      test::GetValue<BinaryPrimitive>(apk->GetResourceTable(), "com.test:bool/is_enabled_bool");
+  ASSERT_THAT(actual_bool, NotNull());
+  EXPECT_EQ(android::Res_value::TYPE_INT_BOOLEAN, actual_bool->value.dataType);
+  EXPECT_EQ(0xffffffffu, actual_bool->value.data);
+
+  auto actual_str =
+      test::GetValue<String>(apk->GetResourceTable(), "com.test:string/is_enabled_str");
+  ASSERT_THAT(actual_str, NotNull());
+  EXPECT_EQ(*actual_str->value, "true");
+
+  // Test nested data structures
+  auto actual_array = test::GetValue<Array>(apk->GetResourceTable(), "com.test:array/my_array");
+  ASSERT_THAT(actual_array, NotNull());
+  EXPECT_THAT(actual_array->elements.size(), Eq(1));
+
+  auto array_el_ref = ValueCast<BinaryPrimitive>(actual_array->elements[0].get());
+  ASSERT_THAT(array_el_ref, NotNull());
+  EXPECT_THAT(array_el_ref->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN));
+  EXPECT_THAT(array_el_ref->value.data, Eq(0xffffffffu));
+
+  auto actual_style = test::GetValue<Style>(apk->GetResourceTable(), "com.test:style/MyStyle");
+  ASSERT_THAT(actual_style, NotNull());
+  EXPECT_THAT(actual_style->entries.size(), Eq(2));
+
+  {
+    auto style_el = ValueCast<Reference>(actual_style->entries[0].value.get());
+    ASSERT_THAT(style_el, NotNull());
+    EXPECT_THAT(style_el->reference_type, Eq(Reference::Type::kAttribute));
+    EXPECT_THAT(style_el->id, Eq(0x7f010000));
+  }
+
+  {
+    auto style_el = ValueCast<String>(actual_style->entries[1].value.get());
+    ASSERT_THAT(style_el, NotNull());
+    EXPECT_THAT(*style_el->value, Eq("Hello World!"));
+  }
+
+  // Test substitution in compiled xml files
+  auto xml = apk->LoadXml("res/layout/layout.xml", &diag);
+  ASSERT_THAT(xml, NotNull());
+
+  auto& xml_attrs = xml->root->attributes;
+  ASSERT_THAT(xml_attrs.size(), Eq(2));
+
+  auto attr_value = ValueCast<Reference>(xml_attrs[0].compiled_value.get());
+  ASSERT_THAT(attr_value, NotNull());
+  EXPECT_THAT(attr_value->reference_type, Eq(Reference::Type::kResource));
+  EXPECT_THAT(attr_value->id, Eq(0x01060001));
+
+  EXPECT_THAT(xml_attrs[1].compiled_value.get(), IsNull());
+  EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 17d11a6..74ecf47 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -567,13 +567,10 @@
   }
 
   bool FlattenTypes(BigBuffer* buffer) {
-    // Sort the types by their IDs. They will be inserted into the StringPool in
-    // this order.
-
     size_t expected_type_id = 1;
     for (const ResourceTableTypeView& type : package_.types) {
-      if (type.type == ResourceType::kStyleable) {
-        // Styleables aren't real Resource Types, they are represented in the R.java file.
+      if (type.type == ResourceType::kStyleable || type.type == ResourceType::kMacro) {
+        // Styleables and macros are not real resource types.
         continue;
       }
 
diff --git a/tools/aapt2/format/binary/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
index c24488b..d97e888 100644
--- a/tools/aapt2/format/binary/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -222,7 +222,7 @@
             android:id="@id/foo"
             app:foo="@id/foo" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   // The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f).
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 498d5a2..ec331df 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -656,6 +656,38 @@
     }
     out_ref->name = name_ref.ToResourceName();
   }
+  if (pb_ref.type_flags() != 0) {
+    out_ref->type_flags = pb_ref.type_flags();
+  }
+  out_ref->allow_raw = pb_ref.allow_raw();
+  return true;
+}
+
+static bool DeserializeMacroFromPb(const pb::MacroBody& pb_ref, Macro* out_ref,
+                                   std::string* out_error) {
+  out_ref->raw_value = pb_ref.raw_string();
+
+  if (pb_ref.has_style_string()) {
+    out_ref->style_string.str = pb_ref.style_string().str();
+    for (const auto& span : pb_ref.style_string().spans()) {
+      out_ref->style_string.spans.emplace_back(Span{
+          .name = span.name(), .first_char = span.start_index(), .last_char = span.end_index()});
+    }
+  }
+
+  for (const auto& untranslatable_section : pb_ref.untranslatable_sections()) {
+    out_ref->untranslatable_sections.emplace_back(
+        UntranslatableSection{.start = static_cast<size_t>(untranslatable_section.start_index()),
+                              .end = static_cast<size_t>(untranslatable_section.end_index())});
+  }
+
+  for (const auto& namespace_decls : pb_ref.namespace_stack()) {
+    out_ref->alias_namespaces.emplace_back(
+        Macro::Namespace{.alias = namespace_decls.prefix(),
+                         .package_name = namespace_decls.package_name(),
+                         .is_private = namespace_decls.is_private()});
+  }
+
   return true;
 }
 
@@ -801,6 +833,15 @@
         value = std::move(plural);
       } break;
 
+      case pb::CompoundValue::kMacro: {
+        const pb::MacroBody& pb_macro = pb_compound_value.macro();
+        auto macro = std::make_unique<Macro>();
+        if (!DeserializeMacroFromPb(pb_macro, macro.get(), out_error)) {
+          return {};
+        }
+        value = std::move(macro);
+      } break;
+
       default:
         LOG(FATAL) << "unknown compound value: " << (int)pb_compound_value.value_case();
         break;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index f13f82d..d2f0336 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -440,6 +440,36 @@
   if (ref.is_dynamic) {
     pb_ref->mutable_is_dynamic()->set_value(ref.is_dynamic);
   }
+  if (ref.type_flags) {
+    pb_ref->set_type_flags(*ref.type_flags);
+  }
+  pb_ref->set_allow_raw(ref.allow_raw);
+}
+
+static void SerializeMacroToPb(const Macro& ref, pb::MacroBody* pb_macro) {
+  pb_macro->set_raw_string(ref.raw_value);
+
+  auto pb_style_str = pb_macro->mutable_style_string();
+  pb_style_str->set_str(ref.style_string.str);
+  for (const auto& span : ref.style_string.spans) {
+    auto pb_span = pb_style_str->add_spans();
+    pb_span->set_name(span.name);
+    pb_span->set_start_index(span.first_char);
+    pb_span->set_end_index(span.last_char);
+  }
+
+  for (const auto& untranslatable_section : ref.untranslatable_sections) {
+    auto pb_section = pb_macro->add_untranslatable_sections();
+    pb_section->set_start_index(untranslatable_section.start);
+    pb_section->set_end_index(untranslatable_section.end);
+  }
+
+  for (const auto& namespace_decls : ref.alias_namespaces) {
+    auto pb_namespace = pb_macro->add_namespace_stack();
+    pb_namespace->set_prefix(namespace_decls.alias);
+    pb_namespace->set_package_name(namespace_decls.package_name);
+    pb_namespace->set_is_private(namespace_decls.is_private);
+  }
 }
 
 template <typename T>
@@ -643,6 +673,11 @@
     }
   }
 
+  void Visit(const Macro* macro) override {
+    pb::MacroBody* pb_macro = out_value_->mutable_compound_value()->mutable_macro();
+    SerializeMacroToPb(*macro, pb_macro);
+  }
+
   void VisitAny(const Value* unknown) override {
     LOG(FATAL) << "unimplemented value: " << *unknown;
   }
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 591ba149..e563eda 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -894,4 +894,38 @@
   EXPECT_THAT(*(s->value), Eq("foo"));
 }
 
+TEST(ProtoSerializeTest, SerializeMacro) {
+  auto original = std::make_unique<Macro>();
+  original->raw_value = "\nThis being human is a guest house.";
+  original->style_string.str = " This being human is a guest house.";
+  original->style_string.spans.emplace_back(Span{.name = "b", .first_char = 12, .last_char = 16});
+  original->untranslatable_sections.emplace_back(UntranslatableSection{.start = 12, .end = 17});
+  original->alias_namespaces.emplace_back(
+      Macro::Namespace{.alias = "prefix", .package_name = "package.name", .is_private = true});
+
+  CloningValueTransformer cloner(nullptr);
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+                                             .Add(NewResourceBuilder("com.app.a:macro/foo")
+                                                      .SetValue(original->Transform(cloner))
+                                                      .Build())
+                                             .Build();
+
+  ResourceTable new_table;
+  pb::ResourceTable pb_table;
+  MockFileCollection files;
+  std::string error;
+  SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+  ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+  EXPECT_THAT(error, IsEmpty());
+
+  Macro* deserialized = test::GetValue<Macro>(&new_table, "com.app.a:macro/foo");
+  ASSERT_THAT(deserialized, NotNull());
+  EXPECT_THAT(deserialized->raw_value, Eq(original->raw_value));
+  EXPECT_THAT(deserialized->style_string.str, Eq(original->style_string.str));
+  EXPECT_THAT(deserialized->style_string.spans, Eq(original->style_string.spans));
+  EXPECT_THAT(deserialized->untranslatable_sections, Eq(original->untranslatable_sections));
+  EXPECT_THAT(deserialized->alias_namespaces, Eq(original->alias_namespaces));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index e1e2e01..de6524d 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -616,8 +616,9 @@
 
   for (const auto& package : table_->packages) {
     for (const auto& type : package->types) {
-      if (type->type == ResourceType::kAttrPrivate) {
-        // We generate these as part of the kAttr type, so skip them here.
+      if (type->type == ResourceType::kAttrPrivate || type->type == ResourceType::kMacro) {
+        // We generate kAttrPrivate as part of the kAttr type, so skip them here.
+        // Macros are not actual resources, so skip them as well.
         continue;
       }
 
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index d08b61e..40395ed 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -570,4 +570,25 @@
   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_dynamic_attr=1;"));
 }
 
+TEST(JavaClassGeneratorTest, SkipMacros) {
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddValue("android:macro/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
+          .Build();
+
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder()
+          .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+          .SetNameManglerPolicy(NameManglerPolicy{"android"})
+          .Build();
+  JavaClassGenerator generator(context.get(), table.get(), {});
+
+  std::string output;
+  StringOutputStream out(&output);
+  EXPECT_TRUE(generator.Generate("android", &out));
+  out.Flush();
+
+  EXPECT_THAT(output, Not(HasSubstr("bar")));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index b7dfec3..e104066 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -264,7 +264,7 @@
       </View>)");
   foo_layout->file.name = test::ParseNameOrDie("com.foo:layout/foo");
 
-  XmlReferenceLinker xml_linker;
+  XmlReferenceLinker xml_linker(nullptr);
   ASSERT_TRUE(xml_linker.Consume(context.get(), bar_layout.get()));
   ASSERT_TRUE(xml_linker.Consume(context.get(), foo_layout.get()));
 
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index c9b8d39..be6c930 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -133,12 +133,14 @@
 // Once an XmlResource is processed by this linker, it is ready to be flattened.
 class XmlReferenceLinker : public IXmlResourceConsumer {
  public:
-  XmlReferenceLinker() = default;
+  explicit XmlReferenceLinker(ResourceTable* table) : table_(table) {
+  }
 
   bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(XmlReferenceLinker);
+  ResourceTable* table_;
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 8e49fab..4ac25bd 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -21,6 +21,7 @@
 #include "androidfw/ResourceTypes.h"
 
 #include "Diagnostics.h"
+#include "ResourceParser.h"
 #include "ResourceTable.h"
 #include "ResourceUtils.h"
 #include "ResourceValues.h"
@@ -37,128 +38,153 @@
 using ::android::base::StringPrintf;
 
 namespace aapt {
-
 namespace {
-
-// The ReferenceLinkerVisitor will follow all references and make sure they point
-// to resources that actually exist, either in the local resource table, or as external
-// symbols. Once the target resource has been found, the ID of the resource will be assigned
-// to the reference object.
-//
-// NOTE: All of the entries in the ResourceTable must be assigned IDs.
-class ReferenceLinkerVisitor : public DescendingValueVisitor {
- public:
-  using DescendingValueVisitor::Visit;
-
-  ReferenceLinkerVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
-                         StringPool* string_pool, xml::IPackageDeclStack* decl)
-      : callsite_(callsite),
-        context_(context),
-        symbols_(symbols),
-        package_decls_(decl),
-        string_pool_(string_pool) {}
-
-  void Visit(Reference* ref) override {
-    if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, package_decls_)) {
-      error_ = true;
-    }
+struct LoggingResourceName {
+  LoggingResourceName(const Reference& ref, const CallSite& callsite,
+                      const xml::IPackageDeclStack* decls)
+      : ref_(ref), callsite_(callsite), decls_(decls) {
   }
 
-  // We visit the Style specially because during this phase, values of attributes are
-  // all RawString values. Now that we are expected to resolve all symbols, we can
-  // lookup the attributes to find out which types are allowed for the attributes' values.
-  void Visit(Style* style) override {
-    if (style->parent) {
-      Visit(&style->parent.value());
+  const Reference& ref_;
+  const CallSite& callsite_;
+  const xml::IPackageDeclStack* decls_;
+};
+
+inline ::std::ostream& operator<<(::std::ostream& out, const LoggingResourceName& name) {
+  if (!name.ref_.name) {
+    out << name.ref_.id.value();
+    return out;
+  }
+
+  out << name.ref_.name.value();
+
+  Reference fully_qualified = name.ref_;
+  xml::ResolvePackage(name.decls_, &fully_qualified);
+
+  ResourceName& full_name = fully_qualified.name.value();
+  if (full_name.package.empty()) {
+    full_name.package = name.callsite_.package;
+  }
+
+  if (full_name != name.ref_.name.value()) {
+    out << " (aka " << full_name << ")";
+  }
+  return out;
+}
+
+}  // namespace
+
+std::unique_ptr<Reference> ReferenceLinkerTransformer::TransformDerived(const Reference* value) {
+  auto linked_item =
+      ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
+  if (linked_item) {
+    auto linked_item_ptr = linked_item.release();
+    if (auto ref = ValueCast<Reference>(linked_item_ptr)) {
+      return std::unique_ptr<Reference>(ref);
     }
+    context_->GetDiagnostics()->Error(DiagMessage(value->GetSource())
+                                      << "value of '"
+                                      << LoggingResourceName(*value, callsite_, package_decls_)
+                                      << "' must be a resource reference");
+    delete linked_item_ptr;
+  }
 
-    for (Style::Entry& entry : style->entries) {
-      std::string err_str;
+  error_ = true;
+  return CloningValueTransformer::TransformDerived(value);
+}
 
-      // Transform the attribute reference so that it is using the fully qualified package
-      // name. This will also mark the reference as being able to see private resources if
-      // there was a '*' in the reference or if the package came from the private namespace.
-      Reference transformed_reference = entry.key;
-      ResolvePackage(package_decls_, &transformed_reference);
+std::unique_ptr<Style> ReferenceLinkerTransformer::TransformDerived(const Style* style) {
+  // We visit the Style specially because during this phase, values of attributes are either
+  // RawString or Reference values. Now that we are expected to resolve all symbols, we can lookup
+  // the attributes to find out which types are allowed for the attributes' values.
+  auto new_style = CloningValueTransformer::TransformDerived(style);
+  if (new_style->parent) {
+    new_style->parent = *TransformDerived(&style->parent.value());
+  }
 
-      // Find the attribute in the symbol table and check if it is visible from this callsite.
-      const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
-          transformed_reference, callsite_, context_, symbols_, &err_str);
-      if (symbol) {
-        // Assign our style key the correct ID. The ID may not exist.
-        entry.key.id = symbol->id;
+  for (Style::Entry& entry : new_style->entries) {
+    std::string err_str;
 
-        // Try to convert the value to a more specific, typed value based on the attribute it is
-        // set to.
-        entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
+    // Transform the attribute reference so that it is using the fully qualified package
+    // name. This will also mark the reference as being able to see private resources if
+    // there was a '*' in the reference or if the package came from the private namespace.
+    Reference transformed_reference = entry.key;
+    ResolvePackage(package_decls_, &transformed_reference);
 
-        // Link/resolve the final value (mostly if it's a reference).
-        entry.value->Accept(this);
+    // Find the attribute in the symbol table and check if it is visible from this callsite.
+    const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
+        transformed_reference, callsite_, context_, symbols_, &err_str);
+    if (symbol) {
+      // Assign our style key the correct ID. The ID may not exist.
+      entry.key.id = symbol->id;
 
-        // Now verify that the type of this item is compatible with the
-        // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
-        // check is fast and we avoid creating a DiagMessage when the match is successful.
-        if (!symbol->attribute->Matches(*entry.value, nullptr)) {
-          // The actual type of this item is incompatible with the attribute.
-          DiagMessage msg(entry.key.GetSource());
+      // Link/resolve the final value if it's a reference.
+      entry.value = entry.value->Transform(*this);
 
-          // Call the matches method again, this time with a DiagMessage so we fill in the actual
-          // error message.
-          symbol->attribute->Matches(*entry.value, &msg);
-          context_->GetDiagnostics()->Error(msg);
-          error_ = true;
-        }
+      // Try to convert the value to a more specific, typed value based on the attribute it is
+      // set to.
+      entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
 
-      } else {
+      // Now verify that the type of this item is compatible with the
+      // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
+      // check is fast and we avoid creating a DiagMessage when the match is successful.
+      if (!symbol->attribute->Matches(*entry.value, nullptr)) {
+        // The actual type of this item is incompatible with the attribute.
         DiagMessage msg(entry.key.GetSource());
-        msg << "style attribute '";
-        ReferenceLinker::WriteResourceName(entry.key, callsite_, package_decls_, &msg);
-        msg << "' " << err_str;
+
+        // Call the matches method again, this time with a DiagMessage so we fill in the actual
+        // error message.
+        symbol->attribute->Matches(*entry.value, &msg);
         context_->GetDiagnostics()->Error(msg);
         error_ = true;
       }
+    } else {
+      context_->GetDiagnostics()->Error(DiagMessage(entry.key.GetSource())
+                                        << "style attribute '"
+                                        << LoggingResourceName(entry.key, callsite_, package_decls_)
+                                        << "' " << err_str);
+
+      error_ = true;
     }
   }
+  return new_style;
+}
 
-  bool HasError() {
-    return error_;
+std::unique_ptr<Item> ReferenceLinkerTransformer::TransformItem(const Reference* value) {
+  auto linked_value =
+      ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
+  if (linked_value) {
+    return linked_value;
   }
+  error_ = true;
+  return CloningValueTransformer::TransformDerived(value);
+}
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
+// Transform a RawString value into a more specific, appropriate value, based on the
+// Attribute. If a non RawString value is passed in, this is an identity transform.
+std::unique_ptr<Item> ReferenceLinkerTransformer::ParseValueWithAttribute(
+    std::unique_ptr<Item> value, const Attribute* attr) {
+  if (RawString* raw_string = ValueCast<RawString>(value.get())) {
+    std::unique_ptr<Item> transformed =
+        ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
 
-  // Transform a RawString value into a more specific, appropriate value, based on the
-  // Attribute. If a non RawString value is passed in, this is an identity transform.
-  std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
-                                                const Attribute* attr) {
-    if (RawString* raw_string = ValueCast<RawString>(value.get())) {
-      std::unique_ptr<Item> transformed =
-          ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
-
-      // If we could not parse as any specific type, try a basic STRING.
-      if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
-        StringBuilder string_builder;
-        string_builder.AppendText(*raw_string->value);
-        if (string_builder) {
-          transformed =
-              util::make_unique<String>(string_pool_->MakeRef(string_builder.to_string()));
-        }
-      }
-
-      if (transformed) {
-        return transformed;
+    // If we could not parse as any specific type, try a basic STRING.
+    if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
+      StringBuilder string_builder;
+      string_builder.AppendText(*raw_string->value);
+      if (string_builder) {
+        transformed = util::make_unique<String>(pool_->MakeRef(string_builder.to_string()));
       }
     }
-    return value;
-  }
 
-  const CallSite& callsite_;
-  IAaptContext* context_;
-  SymbolTable* symbols_;
-  xml::IPackageDeclStack* package_decls_;
-  StringPool* string_pool_;
-  bool error_ = false;
-};
+    if (transformed) {
+      return transformed;
+    }
+  }
+  return value;
+}
+
+namespace {
 
 class EmptyDeclStack : public xml::IPackageDeclStack {
  public:
@@ -175,6 +201,27 @@
   DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
 };
 
+struct MacroDeclStack : public xml::IPackageDeclStack {
+  explicit MacroDeclStack(std::vector<Macro::Namespace> namespaces)
+      : alias_namespaces_(std::move(namespaces)) {
+  }
+
+  Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
+    if (alias.empty()) {
+      return xml::ExtractedPackage{{}, true /*private*/};
+    }
+    for (auto it = alias_namespaces_.rbegin(); it != alias_namespaces_.rend(); ++it) {
+      if (alias == StringPiece(it->alias)) {
+        return xml::ExtractedPackage{it->package_name, it->is_private};
+      }
+    }
+    return {};
+  }
+
+ private:
+  std::vector<Macro::Namespace> alias_namespaces_;
+};
+
 // The symbol is visible if it is public, or if the reference to it is requesting private access
 // or if the callsite comes from the same package.
 bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
@@ -220,8 +267,6 @@
       // If the callsite package is the same as the current compilation package,
       // check the feature split dependencies as well. Feature split resources
       // can be referenced without a namespace, just like the base package.
-      // TODO: modify the package name of included splits instead of having the
-      // symbol table look up the resource in in every package. b/136105066
       if (callsite.package == context->GetCompilationPackage()) {
         const auto& split_name_dependencies = context->GetSplitNameDependencies();
         for (const std::string& split_name : split_name_dependencies) {
@@ -295,29 +340,6 @@
   return xml::AaptAttribute(*symbol->attribute, symbol->id);
 }
 
-void ReferenceLinker::WriteResourceName(const Reference& ref, const CallSite& callsite,
-                                        const xml::IPackageDeclStack* decls, DiagMessage* out_msg) {
-  CHECK(out_msg != nullptr);
-  if (!ref.name) {
-    *out_msg << ref.id.value();
-    return;
-  }
-
-  *out_msg << ref.name.value();
-
-  Reference fully_qualified = ref;
-  xml::ResolvePackage(decls, &fully_qualified);
-
-  ResourceName& full_name = fully_qualified.name.value();
-  if (full_name.package.empty()) {
-    full_name.package = callsite.package;
-  }
-
-  if (full_name != ref.name.value()) {
-    *out_msg << " (aka " << full_name << ")";
-  }
-}
-
 void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
                                          const xml::IPackageDeclStack* decls,
                                          DiagMessage* out_msg) {
@@ -348,18 +370,71 @@
   }
 }
 
-bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
-                                    IAaptContext* context, SymbolTable* symbols,
-                                    const xml::IPackageDeclStack* decls) {
-  CHECK(reference != nullptr);
-  if (!reference->name && !reference->id) {
+std::unique_ptr<Item> ReferenceLinker::LinkReference(const CallSite& callsite,
+                                                     const Reference& reference,
+                                                     IAaptContext* context, SymbolTable* symbols,
+                                                     ResourceTable* table,
+                                                     const xml::IPackageDeclStack* decls) {
+  if (!reference.name && !reference.id) {
     // This is @null.
-    return true;
+    return std::make_unique<Reference>(reference);
   }
 
-  Reference transformed_reference = *reference;
+  Reference transformed_reference = reference;
   xml::ResolvePackage(decls, &transformed_reference);
 
+  if (transformed_reference.name.value().type == ResourceType::kMacro) {
+    if (transformed_reference.name.value().package.empty()) {
+      transformed_reference.name.value().package = callsite.package;
+    }
+
+    auto result = table->FindResource(transformed_reference.name.value());
+    if (!result || result.value().entry->values.empty()) {
+      context->GetDiagnostics()->Error(
+          DiagMessage(reference.GetSource())
+          << "failed to find definition for "
+          << LoggingResourceName(transformed_reference, callsite, decls));
+      return {};
+    }
+
+    auto& macro_values = result.value().entry->values;
+    CHECK(macro_values.size() == 1) << "Macros can only be defined in the default configuration.";
+
+    auto macro = ValueCast<Macro>(macro_values[0]->value.get());
+    CHECK(macro != nullptr) << "Value of macro resource is not a Macro (actual "
+                            << *macro_values[0]->value << ")";
+
+    // Re-create the state used to parse the macro tag to compile the macro contents as if it was
+    // defined inline
+    uint32_t type_flags = 0;
+    if (reference.type_flags.has_value()) {
+      type_flags = reference.type_flags.value();
+    }
+
+    MacroDeclStack namespace_stack(macro->alias_namespaces);
+    FlattenedXmlSubTree sub_tree{.raw_value = macro->raw_value,
+                                 .style_string = macro->style_string,
+                                 .untranslatable_sections = macro->untranslatable_sections,
+                                 .namespace_resolver = &namespace_stack,
+                                 .source = macro->GetSource()};
+
+    auto new_value = ResourceParser::ParseXml(sub_tree, type_flags, reference.allow_raw, *table,
+                                              macro_values[0]->config, *context->GetDiagnostics());
+    if (new_value == nullptr) {
+      context->GetDiagnostics()->Error(
+          DiagMessage(reference.GetSource())
+          << "failed to substitute macro "
+          << LoggingResourceName(transformed_reference, callsite, decls)
+          << ": failed to parse contents as one of type(s) " << Attribute::MaskString(type_flags));
+      return {};
+    }
+
+    if (auto ref = ValueCast<Reference>(new_value.get())) {
+      return LinkReference(callsite, *ref, context, symbols, table, decls);
+    }
+    return new_value;
+  }
+
   std::string err_str;
   const SymbolTable::Symbol* s =
       ResolveSymbolCheckVisibility(transformed_reference, callsite, context, symbols, &err_str);
@@ -367,17 +442,17 @@
     // The ID may not exist. This is fine because of the possibility of building
     // against libraries without assigned IDs.
     // Ex: Linking against own resources when building a static library.
-    reference->id = s->id;
-    reference->is_dynamic = s->is_dynamic;
-    return true;
+    auto new_ref = std::make_unique<Reference>(reference);
+    new_ref->id = s->id;
+    new_ref->is_dynamic = s->is_dynamic;
+    return std::move(new_ref);
   }
 
-  DiagMessage error_msg(reference->GetSource());
-  error_msg << "resource ";
-  WriteResourceName(*reference, callsite, decls, &error_msg);
-  error_msg << " " << err_str;
-  context->GetDiagnostics()->Error(error_msg);
-  return false;
+  context->GetDiagnostics()->Error(DiagMessage(reference.GetSource())
+                                   << "resource "
+                                   << LoggingResourceName(transformed_reference, callsite, decls)
+                                   << " " << err_str);
+  return {};
 }
 
 bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
@@ -412,14 +487,15 @@
 
         // The context of this resource is the package in which it is defined.
         const CallSite callsite{name.package};
-        ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
-                                       &table->string_pool, &decl_stack);
+        ReferenceLinkerTransformer reference_transformer(callsite, context,
+                                                         context->GetExternalSymbols(),
+                                                         &table->string_pool, table, &decl_stack);
 
         for (auto& config_value : entry->values) {
-          config_value->value->Accept(&visitor);
+          config_value->value = config_value->value->Transform(reference_transformer);
         }
 
-        if (visitor.HasError()) {
+        if (reference_transformer.HasError()) {
           error = true;
         }
       }
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 1256709..770f1e5 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -28,6 +28,41 @@
 
 namespace aapt {
 
+// A ValueTransformer that returns fully linked versions of resource and macro references.
+class ReferenceLinkerTransformer : public CloningValueTransformer {
+ public:
+  ReferenceLinkerTransformer(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
+                             StringPool* string_pool, ResourceTable* table,
+                             xml::IPackageDeclStack* decl)
+      : CloningValueTransformer(string_pool),
+        callsite_(callsite),
+        context_(context),
+        symbols_(symbols),
+        table_(table),
+        package_decls_(decl) {
+  }
+
+  std::unique_ptr<Reference> TransformDerived(const Reference* value) override;
+  std::unique_ptr<Item> TransformItem(const Reference* value) override;
+  std::unique_ptr<Style> TransformDerived(const Style* value) override;
+
+  bool HasError() {
+    return error_;
+  }
+
+ private:
+  // Transform a RawString value into a more specific, appropriate value, based on the
+  // Attribute. If a non RawString value is passed in, this is an identity transform.
+  std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value, const Attribute* attr);
+
+  const CallSite& callsite_;
+  IAaptContext* context_;
+  SymbolTable* symbols_;
+  ResourceTable* table_;
+  xml::IPackageDeclStack* package_decls_;
+  bool error_ = false;
+};
+
 // Resolves all references to resources in the ResourceTable and assigns them IDs.
 // The ResourceTable must already have IDs assigned to each resource.
 // Once the ResourceTable is processed by this linker, it is ready to be flattened.
@@ -70,19 +105,28 @@
 
   // Writes the resource name to the DiagMessage, using the
   // "orig_name (aka <transformed_name>)" syntax.
-  static void WriteResourceName(const Reference& orig, const CallSite& callsite,
-                                const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
+  /*static void WriteResourceName(const Reference& orig, const CallSite& callsite,
+                                const xml::IPackageDeclStack* decls, DiagMessage* out_msg);*/
 
   // Same as WriteResourceName but omits the 'attr' part.
   static void WriteAttributeName(const Reference& ref, const CallSite& callsite,
                                  const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
 
-  // Transforms the package name of the reference to the fully qualified package name using
-  // the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible
-  // to the reference at the callsite, the reference is updated with an ID.
-  // Returns false on failure, and an error message is logged to the IDiagnostics in the context.
-  static bool LinkReference(const CallSite& callsite, Reference* reference, IAaptContext* context,
-                            SymbolTable* symbols, const xml::IPackageDeclStack* decls);
+  // Returns a fully linked version a resource reference.
+  //
+  // If the reference points to a non-macro resource, the xml::IPackageDeclStack is used to
+  // determine the fully qualified name of the referenced resource. If the symbol is visible
+  // to the reference at the callsite, a copy of the reference with an updated updated ID is
+  // returned.
+  //
+  // If the reference points to a macro, the ResourceTable is used to find the macro definition and
+  // substitute its contents in place of the reference.
+  //
+  // Returns nullptr on failure, and an error message is logged to the IDiagnostics in the context.
+  static std::unique_ptr<Item> LinkReference(const CallSite& callsite, const Reference& reference,
+                                             IAaptContext* context, SymbolTable* symbols,
+                                             ResourceTable* table,
+                                             const xml::IPackageDeclStack* decls);
 
   // Links all references in the ResourceTable.
   bool Consume(IAaptContext* context, ResourceTable* table) override;
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 228c5bd74..2d8f0d3 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -365,4 +365,22 @@
   EXPECT_THAT(s, IsNull());
 }
 
+TEST(ReferenceLinkerTest, MacroFailToFindDefinition) {
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddReference("com.app.test:string/foo", ResourceId(0x7f020000), "com.app.test:macro/bar")
+          .Build();
+
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder()
+          .SetCompilationPackage("com.app.test")
+          .SetPackageId(0x7f)
+          .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+          .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+          .Build();
+
+  ReferenceLinker linker;
+  ASSERT_FALSE(linker.Consume(context.get(), table.get()));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/link/XmlCompatVersioner_test.cpp b/tools/aapt2/link/XmlCompatVersioner_test.cpp
index a98ab0f..d638096 100644
--- a/tools/aapt2/link/XmlCompatVersioner_test.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner_test.cpp
@@ -82,7 +82,7 @@
           app:foo="16dp"
           foo="bar"/>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   XmlCompatVersioner::Rules rules;
@@ -121,7 +121,7 @@
           app:foo="16dp"
           foo="bar"/>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   XmlCompatVersioner::Rules rules;
@@ -181,7 +181,7 @@
       <View xmlns:android="http://schemas.android.com/apk/res/android"
           android:paddingHorizontal="24dp" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   XmlCompatVersioner::Rules rules;
@@ -256,7 +256,7 @@
           android:paddingLeft="16dp"
           android:paddingRight="16dp"/>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   Item* padding_horizontal_value =
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index c3c16b9..aaa085e 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -33,49 +33,18 @@
 
 namespace {
 
-// Visits all references (including parents of styles, references in styles, arrays, etc) and
-// links their symbolic name to their Resource ID, performing mangling and package aliasing
-// as needed.
-class ReferenceVisitor : public DescendingValueVisitor {
- public:
-  using DescendingValueVisitor::Visit;
-
-  ReferenceVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
-                   xml::IPackageDeclStack* decls)
-      : callsite_(callsite), context_(context), symbols_(symbols), decls_(decls), error_(false) {}
-
-  void Visit(Reference* ref) override {
-    if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, decls_)) {
-      error_ = true;
-    }
-  }
-
-  bool HasError() const {
-    return error_;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor);
-
-  const CallSite& callsite_;
-  IAaptContext* context_;
-  SymbolTable* symbols_;
-  xml::IPackageDeclStack* decls_;
-  bool error_;
-};
-
 // Visits each xml Element and compiles the attributes within.
 class XmlVisitor : public xml::PackageAwareVisitor {
  public:
   using xml::PackageAwareVisitor::Visit;
 
-  XmlVisitor(const Source& source, const CallSite& callsite, IAaptContext* context,
-             SymbolTable* symbols)
+  XmlVisitor(const Source& source, StringPool* pool, const CallSite& callsite,
+             IAaptContext* context, ResourceTable* table, SymbolTable* symbols)
       : source_(source),
         callsite_(callsite),
         context_(context),
         symbols_(symbols),
-        reference_visitor_(callsite, context, symbols, this) {
+        reference_transformer_(callsite, context, symbols, pool, table, this) {
   }
 
   void Visit(xml::Element* el) override {
@@ -127,7 +96,7 @@
       if (attr.compiled_value) {
         // With a compiledValue, we must resolve the reference and assign it an ID.
         attr.compiled_value->SetSource(source);
-        attr.compiled_value->Accept(&reference_visitor_);
+        attr.compiled_value = attr.compiled_value->Transform(reference_transformer_);
       } else if ((attribute->type_mask & android::ResTable_map::TYPE_STRING) == 0) {
         // We won't be able to encode this as a string.
         DiagMessage msg(source);
@@ -143,7 +112,7 @@
   }
 
   bool HasError() {
-    return error_ || reference_visitor_.HasError();
+    return error_ || reference_transformer_.HasError();
   }
 
  private:
@@ -154,7 +123,7 @@
   IAaptContext* context_;
   SymbolTable* symbols_;
 
-  ReferenceVisitor reference_visitor_;
+  ReferenceLinkerTransformer reference_transformer_;
   bool error_ = false;
 };
 
@@ -173,7 +142,8 @@
     callsite.package = context->GetCompilationPackage();
   }
 
-  XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols());
+  XmlVisitor visitor(resource->file.source, &resource->string_pool, callsite, context, table_,
+                     context->GetExternalSymbols());
   if (resource->root) {
     resource->root->Accept(&visitor);
     return !visitor.HasError();
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 0ce2e50..ddf5b9a 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -91,7 +91,7 @@
             nonAaptAttrRef="@id/id"
             class="hello" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* view_el = doc->root.get();
@@ -144,7 +144,7 @@
       <View xmlns:android="http://schemas.android.com/apk/res/android"
           android:colorAccent="@android:color/hidden" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
 }
 
@@ -153,7 +153,7 @@
     <View xmlns:android="http://schemas.android.com/apk/res/android"
           android:colorAccent="@*android:color/hidden" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 }
 
@@ -162,7 +162,7 @@
       <View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
           support:colorAccent="#ff0000" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* view_el = doc->root.get();
@@ -181,7 +181,7 @@
       <View xmlns:app="http://schemas.android.com/apk/res-auto"
           app:colorAccent="@app:color/red" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* view_el = doc->root.get();
@@ -203,7 +203,7 @@
         <View xmlns:app="http://schemas.android.com/apk/res/com.app.test" app:attr="@app:id/id"/>
       </View>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* view_el = doc->root.get();
@@ -239,7 +239,7 @@
       <View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
           android:attr="@id/id"/>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* view_el = doc->root.get();
@@ -261,7 +261,7 @@
   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
     <gradient />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* gradient_el = doc->root.get();
@@ -283,7 +283,7 @@
   <gradient xmlns:android="http://schemas.android.com/apk/res/android"
       android:angle="90"/>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* gradient_el = doc->root.get();
@@ -305,7 +305,7 @@
   <gradient xmlns:android="http://schemas.android.com/apk/res/android" />)");
   context_->SetMinSdkVersion(30);
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* gradient_el = doc->root.get();
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index a023494..182203d 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -177,6 +177,10 @@
   return event_queue_.front().data2;
 }
 
+const std::vector<XmlPullParser::PackageDecl>& XmlPullParser::package_decls() const {
+  return package_aliases_;
+}
+
 XmlPullParser::const_iterator XmlPullParser::begin_attributes() const {
   return event_queue_.front().attributes.begin();
 }
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 6ebaa28..5da2d4b 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -123,6 +123,13 @@
    */
   Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
 
+  struct PackageDecl {
+    std::string prefix;
+    ExtractedPackage package;
+  };
+
+  const std::vector<PackageDecl>& package_decls() const;
+
   //
   // Remaining methods are for retrieving information about attributes
   // associated with a StartElement.
@@ -180,11 +187,6 @@
   const std::string empty_;
   size_t depth_;
   std::stack<std::string> namespace_uris_;
-
-  struct PackageDecl {
-    std::string prefix;
-    ExtractedPackage package;
-  };
   std::vector<PackageDecl> package_aliases_;
 };