Merge "TEST_MAPPING for BatteryStats" into sc-dev
diff --git a/Android.bp b/Android.bp
index e4c5c37..908280e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -371,7 +371,6 @@
     srcs: [
         // Java/AIDL sources under frameworks/base
         ":framework-blobstore-sources",
-        ":framework-connectivity-sources", // framework-connectivity is not yet a module
         ":framework-core-sources",
         ":framework-drm-sources",
         ":framework-graphics-nonupdatable-sources",
@@ -437,6 +436,7 @@
     name: "framework-updatable-sources",
     srcs: [
         ":framework-appsearch-sources",
+        ":framework-connectivity-sources",
         ":framework-graphics-srcs",
         ":framework-mediaprovider-sources",
         ":framework-permission-sources",
@@ -639,6 +639,7 @@
     defaults: ["framework-aidl-export-defaults"],
     srcs: [
         ":framework-non-updatable-sources",
+        ":framework-connectivity-sources",
         "core/java/**/*.logtags",
     ],
     // See comment on framework-atb-backward-compatibility module below
@@ -700,6 +701,8 @@
     apex_available: ["//apex_available:platform"],
     visibility: [
         "//frameworks/base",
+        // TODO: remove when framework-connectivity can build against API
+        "//frameworks/base/packages/Connectivity/framework",
         // TODO(b/147128803) remove the below lines
         "//frameworks/base/apex/appsearch/framework",
         "//frameworks/base/apex/blobstore/framework",
@@ -1452,6 +1455,7 @@
     ],
     libs: [
         "framework-annotations-lib",
+        "framework-connectivity",
         "unsupportedappusage",
     ],
     visibility: [
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 4bd524f..bff222e 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -58,6 +58,9 @@
         local_include_dirs: [
             "apex/media/aidl/stable",
             "media/aidl",
+            // TODO: move to include-dirs for packages/modules/Connectivity when this moves out of
+            // frameworks/base
+            "packages/Connectivity/framework/aidl-export",
             "telephony/java",
         ],
         include_dirs: ["frameworks/av/aidl"],
@@ -310,6 +313,7 @@
         "art.module.public.api.stubs",
         "conscrypt.module.public.api.stubs",
         "framework-appsearch.stubs",
+        "framework-connectivity.stubs",
         "framework-graphics.stubs",
         "framework-media.stubs",
         "framework-mediaprovider.stubs",
@@ -334,6 +338,7 @@
         "art.module.public.api.stubs",
         "conscrypt.module.public.api.stubs",
         "framework-appsearch.stubs.system",
+        "framework-connectivity.stubs.system",
         "framework-graphics.stubs.system",
         "framework-media.stubs.system",
         "framework-mediaprovider.stubs.system",
@@ -374,6 +379,7 @@
         "art.module.public.api.stubs",
         "conscrypt.module.public.api.stubs",
         "framework-appsearch.stubs.system",
+        "framework-connectivity.stubs.system",
         "framework-graphics.stubs.system",
         "framework-media.stubs.system",
         "framework-mediaprovider.stubs.system",
diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp
index 19a66ad..638403d 100644
--- a/apct-tests/perftests/contentcapture/Android.bp
+++ b/apct-tests/perftests/contentcapture/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "ContentCapturePerfTests",
     srcs: ["src/**/*.java"],
diff --git a/apct-tests/perftests/inputmethod/Android.bp b/apct-tests/perftests/inputmethod/Android.bp
index 463ac9b..f2f1f75 100644
--- a/apct-tests/perftests/inputmethod/Android.bp
+++ b/apct-tests/perftests/inputmethod/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "ImePerfTests",
     srcs: ["src/**/*.java"],
diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp
index ab44dd9..87f65a9 100644
--- a/apex/appsearch/Android.bp
+++ b/apex/appsearch/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 apex {
     name: "com.android.appsearch",
     manifest: "apex_manifest.json",
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index 8ba7954..5def55f 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 filegroup {
     name: "framework-appsearch-sources",
     srcs: [
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index cc79f6b..168c7c2 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -148,7 +148,7 @@
     method public void remove(@NonNull android.app.appsearch.RemoveByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
     method public void remove(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
     method public void reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
-    method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
+    method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec);
     method public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.SetSchemaResponse>>);
   }
 
@@ -217,7 +217,7 @@
 
   public class GlobalSearchSession implements java.io.Closeable {
     method public void close();
-    method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
+    method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec);
   }
 
   public class PackageIdentifier {
@@ -287,7 +287,7 @@
 
   public class SearchResults implements java.io.Closeable {
     method public void close();
-    method public void getNextPage(@NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.List<android.app.appsearch.SearchResult>>>);
+    method public void getNextPage(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.List<android.app.appsearch.SearchResult>>>);
   }
 
   public final class SearchSpec {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
index 519c14f..31ab259 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
@@ -28,11 +28,19 @@
 import java.util.Map;
 
 /**
- * Provides access to multiple {@link AppSearchResult}s from a batch operation accepting multiple
- * inputs.
+ * Provides results for AppSearch batch operations which encompass multiple documents.
  *
- * @param <KeyType> The type of the keys for {@link #getSuccesses} and {@link #getFailures}.
- * @param <ValueType> The type of result objects associated with the keys.
+ * <p>Individual results of a batch operation are separated into two maps: one for successes and one
+ * for failures. For successes, {@link #getSuccesses()} will return a map of keys to instances of
+ * the value type. For failures, {@link #getFailures()} will return a map of keys to {@link
+ * AppSearchResult} objects.
+ *
+ * <p>Alternatively, {@link #getAll()} returns a map of keys to {@link AppSearchResult} objects for
+ * both successes and failures.
+ *
+ * @see AppSearchSession#put
+ * @see AppSearchSession#getByUri
+ * @see AppSearchSession#remove
  */
 public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
     @NonNull private final Map<KeyType, ValueType> mSuccesses;
@@ -75,8 +83,11 @@
     }
 
     /**
-     * Returns a {@link Map} of all successful keys mapped to the successful {@link
-     * AppSearchResult}s they produced.
+     * Returns a {@link Map} of keys mapped to instances of the value type for all successful
+     * individual results.
+     *
+     * <p>Example: {@link AppSearchSession#getByUri} returns an {@link AppSearchBatchResult}. Each
+     * key (a URI of {@code String} type) will map to a {@link GenericDocument} object.
      *
      * <p>The values of the {@link Map} will not be {@code null}.
      */
@@ -86,8 +97,8 @@
     }
 
     /**
-     * Returns a {@link Map} of all failed keys mapped to the failed {@link AppSearchResult}s they
-     * produced.
+     * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all failed
+     * individual results.
      *
      * <p>The values of the {@link Map} will not be {@code null}.
      */
@@ -97,7 +108,8 @@
     }
 
     /**
-     * Returns a {@link Map} of all keys mapped to the {@link AppSearchResult}s they produced.
+     * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all
+     * individual results.
      *
      * <p>The values of the {@link Map} will not be {@code null}.
      */
@@ -149,8 +161,8 @@
     /**
      * Builder for {@link AppSearchBatchResult} objects.
      *
-     * @param <KeyType> The type of keys.
-     * @param <ValueType> The type of result objects associated with the keys.
+     * <p>Once {@link #build} is called, the instance can no longer be used.
+     *
      * @hide
      */
     public static final class Builder<KeyType, ValueType> {
@@ -160,9 +172,11 @@
         private boolean mBuilt = false;
 
         /**
-         * Associates the {@code key} with the given successful return value.
+         * Associates the {@code key} with the provided successful return value.
          *
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
+         *
+         * @throws IllegalStateException if the builder has already been used.
          */
         @NonNull
         public Builder<KeyType, ValueType> setSuccess(
@@ -173,9 +187,11 @@
         }
 
         /**
-         * Associates the {@code key} with the given failure code and error message.
+         * Associates the {@code key} with the provided failure code and error message.
          *
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
+         *
+         * @throws IllegalStateException if the builder has already been used.
          */
         @NonNull
         public Builder<KeyType, ValueType> setFailure(
@@ -188,9 +204,11 @@
         }
 
         /**
-         * Associates the {@code key} with the given {@code result}.
+         * Associates the {@code key} with the provided {@code result}.
          *
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
+         *
+         * @throws IllegalStateException if the builder has already been used.
          */
         @NonNull
         public Builder<KeyType, ValueType> setResult(
@@ -209,7 +227,11 @@
             return this;
         }
 
-        /** Builds an {@link AppSearchBatchResult} from the contents of this {@link Builder}. */
+        /**
+         * Builds an {@link AppSearchBatchResult} object from the contents of this {@link Builder}.
+         *
+         * @throws IllegalStateException if the builder has already been used.
+         */
         @NonNull
         public AppSearchBatchResult<KeyType, ValueType> build() {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 69d4e53..6a5975e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -36,12 +36,89 @@
 import java.util.function.Consumer;
 
 /**
- * This class provides access to the centralized AppSearch index maintained by the system.
+ * Provides access to the centralized AppSearch index maintained by the system.
  *
- * <p>Apps can index structured text documents with AppSearch, which can then be retrieved through
- * the query API.
+ * <p>AppSearch is a search library for managing structured data featuring:
+ * <ul>
+ *     <li>A fully offline on-device solution
+ *     <li>A set of APIs for applications to index documents and retrieve them via full-text search
+ *     <li>APIs for applications to allow the System to display their content on system UI surfaces
+ *     <li>Similarly, APIs for applications to allow the System to share their content with other
+ *     specified applications.
+ * </ul>
+ *
+ * <p>Applications create a database by opening an {@link AppSearchSession}.
+ *
+ * <p>Example:
+ * <pre>
+ * AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
+ *
+ * AppSearchManager.SearchContext searchContext = new AppSearchManager.SearchContext.Builder().
+ *    setDatabaseName(dbName).build());
+ * appSearchManager.createSearchSession(searchContext, mExecutor, appSearchSessionResult -&gt; {
+ *      mAppSearchSession = appSearchSessionResult.getResultValue();
+ * });</pre>
+ *
+ * <p>After opening the session, a schema must be set in order to define the organizational
+ * structure of data. The schema is set by calling {@link AppSearchSession#setSchema}. The schema
+ * is composed of a collection of {@link AppSearchSchema} objects, each of which defines a unique
+ * type of data.
+ *
+ * <p>Example:
+ * <pre>
+ * AppSearchSchema emailSchemaType = new AppSearchSchema.Builder("Email")
+ *     .addProperty(new StringPropertyConfig.Builder("subject")
+ *        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ *        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ *        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ *    .build()
+ * ).build();
+ *
+ * SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(emailSchemaType).build();
+ * mAppSearchSession.set(request, mExecutor, appSearchResult -&gt; {
+ *      if (appSearchResult.isSuccess()) {
+ *           //Schema has been successfully set.
+ *      }
+ * });</pre>
+ *
+ * <p>The basic unit of data in AppSearch is represented as a {@link GenericDocument} object,
+ * containing a URI, namespace, time-to-live, score, and properties. A namespace organizes a
+ * logical group of documents. For example, a namespace can be created to group documents on a
+ * per-account basis. A URI identifies a single document within a namespace. The combination
+ * of URI and namespace uniquely identifies a {@link GenericDocument} in the database.
+ *
+ * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database
+ * and indexed by calling {@link AppSearchSession#put}.
+ *
+ * <p>Example:
+ * <pre>
+ * // Although for this example we use GenericDocument directly, we recommend extending
+ * // GenericDocument to create specific types (i.e. Email) with specific setters/getters.
+ * GenericDocument email = new GenericDocument.Builder<>(URI, EMAIL_SCHEMA_TYPE)
+ *     .setNamespace(NAMESPACE)
+ *     .setPropertyString(“subject”, EMAIL_SUBJECT)
+ *     .setScore(EMAIL_SCORE)
+ *     .build();
+ *
+ * PutDocumentsRequest request = new PutDocumentsRequest.Builder().addGenericDocuments(email)
+ *     .build();
+ * mAppSearchSession.put(request, mExecutor, appSearchBatchResult -&gt; {
+ *      if (appSearchBatchResult.isSuccess()) {
+ *           //All documents have been successfully indexed.
+ *      }
+ * });</pre>
+ *
+ * <p>Searching within the database is done by calling {@link AppSearchSession#search} and providing
+ * the query string to search for, as well as a {@link SearchSpec}.
+ *
+ * <p>Alternatively, {@link AppSearchSession#getByUri} can be called to retrieve documents by URI
+ * and namespace.
+ *
+ * <p>Document removal is done either by time-to-live expiration, or explicitly calling a remove
+ * operation. Remove operations can be done by URI and namespace via
+ * {@link AppSearchSession#remove(RemoveByUriRequest, Executor, BatchResultCallback)},
+ * or by query via {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}.
  */
-// TODO(b/148046169): This class header needs a detailed example/tutorial.
 @SystemService(Context.APP_SEARCH_SERVICE)
 public class AppSearchManager {
     /**
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 3c02d10..24cc60e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -41,7 +41,7 @@
  * Represents a connection to an AppSearch storage system where {@link GenericDocument}s can be
  * placed and queried.
  *
- * This class is thread safe.
+ * <p>This class is thread safe.
  */
 public final class AppSearchSession implements Closeable {
     private static final String TAG = "AppSearchSession";
@@ -102,92 +102,23 @@
     }
 
     /**
-     * Sets the schema that will be used by documents provided to the {@link #put} method.
+     * Sets the schema that represents the organizational structure of data within the AppSearch
+     * database.
      *
-     * <p>The schema provided here is compared to the stored copy of the schema previously supplied
-     * to {@link #setSchema}, if any, to determine how to treat existing documents. The following
-     * types of schema modifications are always safe and are made without deleting any existing
-     * documents:
+     * <p>Upon creating an {@link AppSearchSession}, {@link #setSchema} should be called. If the
+     * schema needs to be updated, or it has not been previously set, then the provided schema will
+     * be saved and persisted to disk. Otherwise, {@link #setSchema} is handled efficiently as a
+     * no-op call.
      *
-     * <ul>
-     *   <li>Addition of new types
-     *   <li>Addition of new {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or
-     *       {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} properties to a
-     *       type
-     *   <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an {@link
-     *       AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link
-     *       AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} property.
-     * </ul>
-     *
-     * <p>The following types of schema changes are not backwards-compatible:
-     *
-     * <ul>
-     *   <li>Removal of an existing type
-     *   <li>Removal of a property from a type
-     *   <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property
-     *   <li>For properties of {@code Document} type, changing the schema type of {@code Document}s
-     *       of that property
-     *   <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an {@link
-     *       AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link
-     *       AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property).
-     *   <li>Adding a {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property.
-     * </ul>
-     *
-     * <p>Supplying a schema with such changes will, by default, result in this call completing its
-     * future with an {@link android.app.appsearch.exceptions.AppSearchException} with a code of
-     * {@link AppSearchResult#RESULT_INVALID_SCHEMA} and a message describing the incompatibility.
-     * In this case the previously set schema will remain active.
-     *
-     * <p>If you need to make non-backwards-compatible changes as described above, you can either:
-     *
-     * <ul>
-     *   <li>Set the {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In
-     *       this case, instead of completing its future with an {@link
-     *       android.app.appsearch.exceptions.AppSearchException} with the {@link
-     *       AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not
-     *       compatible with the new schema will be deleted and the incompatible schema will be
-     *       applied. Incompatible types and deleted types will be set into {@link
-     *       SetSchemaResponse#getIncompatibleTypes()} and {@link
-     *       SetSchemaResponse#getDeletedTypes()}, respectively.
-     *   <li>Add a {@link android.app.appsearch.AppSearchSchema.Migrator} for each incompatible type
-     *       and make no deletion. The migrator will migrate documents from it's old schema version
-     *       to the new version. Migrated types will be set into both {@link
-     *       SetSchemaResponse#getIncompatibleTypes()} and {@link
-     *       SetSchemaResponse#getMigratedTypes()}. See the migration section below.
-     * </ul>
-     *
-     * <p>It is a no-op to set the same schema as has been previously set; this is handled
-     * efficiently.
-     *
-     * <p>By default, documents are visible on platform surfaces. To opt out, call {@code
-     * SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any
-     * visibility settings apply only to the schemas that are included in the {@code request}.
-     * Visibility settings for a schema type do not apply or persist across {@link
-     * SetSchemaRequest}s.
-     *
-     * <p>Migration: make non-backwards-compatible changes will delete all stored documents in old
-     * schema. You can save your documents by setting {@link
-     * android.app.appsearch.AppSearchSchema.Migrator} via the {@link
-     * SetSchemaRequest.Builder#setMigrator} for each type you want to save.
-     *
-     * <p>{@link android.app.appsearch.AppSearchSchema.Migrator#onDowngrade} or {@link
-     * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} will be triggered if the version
-     * number of the schema stored in AppSearch is different with the version in the request.
-     *
-     * <p>If any error or Exception occurred in the {@link
-     * android.app.appsearch.AppSearchSchema.Migrator#onDowngrade}, {@link
-     * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} or {@link
-     * android.app.appsearch.AppSearchMigrationHelper.Transformer#transform}, the migration will be
-     * terminated, the setSchema request will be rejected unless the schema changes are
-     * backwards-compatible, and stored documents won't have any observable changes.
-     *
-     * @param request The schema update request.
+     * @param request the schema to set or update the AppSearch database to.
      * @param executor Executor on which to invoke the callback.
      * @param callback Callback to receive errors resulting from setting the schema. If the
      *                 operation succeeds, the callback will be invoked with {@code null}.
      * @see android.app.appsearch.AppSearchSchema.Migrator
      * @see android.app.appsearch.AppSearchMigrationHelper.Transformer
      */
+    // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
+    //  exposed.
     public void setSchema(
             @NonNull SetSchemaRequest request,
             @NonNull @CallbackExecutor Executor executor,
@@ -280,12 +211,13 @@
     }
 
     /**
-     * Indexes documents into AppSearch.
+     * Indexes documents into the {@link AppSearchSession} database.
      *
-     * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a
-     * schema type previously registered via the {@link #setSchema} method.
+     * <p>Each {@link GenericDocument} object must have a {@code schemaType} field set to an {@link
+     * AppSearchSchema} type that has been previously registered by calling the {@link #setSchema}
+     * method.
      *
-     * @param request {@link PutDocumentsRequest} containing documents to be indexed
+     * @param request containing documents to be indexed.
      * @param executor Executor on which to invoke the callback.
      * @param callback Callback to receive pending result of performing this operation. The keys
      *                 of the returned {@link AppSearchBatchResult} are the URIs of the input
@@ -463,21 +395,15 @@
      * @param queryExpression query string to search.
      * @param searchSpec spec for setting document filters, adding projection, setting term match
      *     type, etc.
-     * @param executor        Executor on which to invoke the callback of the following request
-     *                        {@link SearchResults#getNextPage}.
      * @return a {@link SearchResults} object for retrieved matched documents.
      */
     @NonNull
-    public SearchResults search(
-            @NonNull String queryExpression,
-            @NonNull SearchSpec searchSpec,
-            @NonNull @CallbackExecutor Executor executor) {
+    public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
         Objects.requireNonNull(queryExpression);
         Objects.requireNonNull(searchSpec);
-        Objects.requireNonNull(executor);
         Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         return new SearchResults(mService, mPackageName, mDatabaseName, queryExpression,
-                searchSpec, mUserId, executor);
+                searchSpec, mUserId);
     }
 
     /**
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
index 09bca4f..8dd9dc1 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -98,8 +98,7 @@
      * <p>Document access can also be granted to system UIs by specifying {@link
      * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} when building a schema.
      *
-     * <p>See {@link AppSearchSession#search} for a detailed explanation on
-     * forming a query string.
+     * <p>See {@link AppSearchSession#search} for a detailed explanation on forming a query string.
      *
      * <p>This method is lightweight. The heavy work will be done in {@link
      * SearchResults#getNextPage}.
@@ -107,21 +106,15 @@
      * @param queryExpression query string to search.
      * @param searchSpec spec for setting document filters, adding projection, setting term match
      *     type, etc.
-     * @param executor        Executor on which to invoke the callback of the following request
-     *                        {@link SearchResults#getNextPage}.
      * @return a {@link SearchResults} object for retrieved matched documents.
      */
     @NonNull
-    public SearchResults search(
-            @NonNull String queryExpression,
-            @NonNull SearchSpec searchSpec,
-            @NonNull @CallbackExecutor Executor executor) {
+    public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
         Objects.requireNonNull(queryExpression);
         Objects.requireNonNull(searchSpec);
-        Objects.requireNonNull(executor);
         Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed");
         return new SearchResults(mService, mPackageName, /*databaseName=*/null, queryExpression,
-                searchSpec, mUserId, executor);
+                searchSpec, mUserId);
     }
 
     /** Closes the {@link GlobalSearchSession}. */
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index a63e015..531c984 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -33,12 +33,16 @@
 import java.util.function.Consumer;
 
 /**
- * SearchResults are a returned object from a query API.
+ * Encapsulates results of a search operation.
  *
- * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets based
- * on request.
+ * <p>Each {@link AppSearchSession#search} operation returns a list of {@link SearchResult} objects,
+ * referred to as a "page", limited by the size configured by {@link
+ * SearchSpec.Builder#setResultCountPerPage}.
  *
- * <p>Should close this object after finish fetching results.
+ * <p>To fetch a page of results, call {@link #getNextPage}.
+ *
+ * <p>All instances of {@link SearchResults} must call {@link SearchResults#close()} after the
+ * results are fetched.
  *
  * <p>This class is not thread safe.
  */
@@ -61,8 +65,6 @@
     @UserIdInt
     private final int mUserId;
 
-    private final Executor mExecutor;
-
     private long mNextPageToken;
 
     private boolean mIsFirstLoad = true;
@@ -75,28 +77,31 @@
             @Nullable String databaseName,
             @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec,
-            @UserIdInt int userId,
-            @NonNull @CallbackExecutor Executor executor) {
+            @UserIdInt int userId) {
         mService = Objects.requireNonNull(service);
         mPackageName = packageName;
         mDatabaseName = databaseName;
         mQueryExpression = Objects.requireNonNull(queryExpression);
         mSearchSpec = Objects.requireNonNull(searchSpec);
         mUserId = userId;
-        mExecutor = Objects.requireNonNull(executor);
     }
 
     /**
-     * Gets a whole page of {@link SearchResult}s.
+     * Retrieves the next page of {@link SearchResult} objects.
      *
-     * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an empty
-     * list.
+     * <p>The page size is configured by {@link SearchSpec.Builder#setResultCountPerPage}.
      *
-     * <p>The page size is set by {@link SearchSpec.Builder#setResultCountPerPage}.
+     * <p>Continue calling this method to access results until it returns an empty list, signifying
+     * there are no more results.
      *
+     * @param executor Executor on which to invoke the callback.
      * @param callback Callback to receive the pending result of performing this operation.
      */
-    public void getNextPage(@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
+    public void getNextPage(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
         Preconditions.checkState(!mIsClosed, "SearchResults has already been closed");
         try {
             if (mIsFirstLoad) {
@@ -104,14 +109,14 @@
                 if (mDatabaseName == null) {
                     // Global query, there's no one package-database combination to check.
                     mService.globalQuery(mPackageName, mQueryExpression,
-                            mSearchSpec.getBundle(), mUserId, wrapCallback(callback));
+                            mSearchSpec.getBundle(), mUserId, wrapCallback(executor, callback));
                 } else {
                     // Normal local query, pass in specified database.
                     mService.query(mPackageName, mDatabaseName, mQueryExpression,
-                            mSearchSpec.getBundle(), mUserId, wrapCallback(callback));
+                            mSearchSpec.getBundle(), mUserId, wrapCallback(executor, callback));
                 }
             } else {
-                mService.getNextPage(mNextPageToken, mUserId, wrapCallback(callback));
+                mService.getNextPage(mNextPageToken, mUserId, wrapCallback(executor, callback));
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -131,10 +136,11 @@
     }
 
     private IAppSearchResultCallback wrapCallback(
+            @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
         return new IAppSearchResultCallback.Stub() {
             public void onResult(AppSearchResult result) {
-                mExecutor.execute(() -> invokeCallback(result, callback));
+                executor.execute(() -> invokeCallback(result, callback));
             }
         };
     }
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
index 57700f8..01473be 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
@@ -30,8 +30,6 @@
 /**
  * Encapsulates a request to index documents into an {@link AppSearchSession} database.
  *
- * <p>@see AppSearchSession#putDocuments
- *
  * @see AppSearchSession#put
  */
 public final class PutDocumentsRequest {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index 2caa94a5e..426a903 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -32,6 +32,41 @@
 /**
  * Encapsulates a request to update the schema of an {@link AppSearchSession} database.
  *
+ * <p>The schema is composed of a collection of {@link AppSearchSchema} objects, each of which
+ * defines a unique type of data.
+ *
+ * <p>The first call to SetSchemaRequest will set the provided schema and store it within the {@link
+ * AppSearchSession} database.
+ *
+ * <p>Subsequent calls will compare the provided schema to the previously saved schema, to determine
+ * how to treat existing documents.
+ *
+ * <p>The following types of schema modifications are always safe and are made without deleting any
+ * existing documents:
+ *
+ * <ul>
+ *   <li>Addition of new {@link AppSearchSchema} types
+ *   <li>Addition of new properties to an existing {@link AppSearchSchema} type
+ *   <li>Changing the cardinality of a property to be less restrictive
+ * </ul>
+ *
+ * <p>The following types of schema changes are not backwards compatible:
+ *
+ * <ul>
+ *   <li>Removal of an existing {@link AppSearchSchema} type
+ *   <li>Removal of a property from an existing {@link AppSearchSchema} type
+ *   <li>Changing the data type of an existing property
+ *   <li>Changing the cardinality of a property to be more restrictive
+ * </ul>
+ *
+ * <p>Providing a schema with incompatible changes, will throw an {@link
+ * android.app.appsearch.exceptions.AppSearchException}, with a message describing the
+ * incompatibility. As a result, the previously set schema will remain unchanged.
+ *
+ * <p>Backward incompatible changes can be made by setting {@link
+ * SetSchemaRequest.Builder#setForceOverride} method to {@code true}. This deletes all documents
+ * that are incompatible with the new schema. The new schema is then saved and persisted to disk.
+ *
  * @see AppSearchSession#setSchema
  */
 public final class SetSchemaRequest {
@@ -54,14 +89,15 @@
         mForceOverride = forceOverride;
     }
 
-    /** Returns the schemas that are part of this request. */
+    /** Returns the {@link AppSearchSchema} types that are part of this request. */
     @NonNull
     public Set<AppSearchSchema> getSchemas() {
         return Collections.unmodifiableSet(mSchemas);
     }
 
     /**
-     * Returns the set of schema types that have opted out of being visible on system UI surfaces.
+     * Returns all the schema types that are opted out of being displayed and visible on any system
+     * UI surface.
      */
     @NonNull
     public Set<String> getSchemasNotVisibleToSystemUi() {
@@ -70,10 +106,9 @@
 
     /**
      * Returns a mapping of schema types to the set of packages that have access to that schema
-     * type. Each package is represented by a {@link PackageIdentifier}. name and byte[]
-     * certificate.
+     * type.
      *
-     * <p>This method is inefficient to call repeatedly.
+     * <p>It’s inefficient to call this method repeatedly.
      */
     @NonNull
     public Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackages() {
@@ -91,9 +126,8 @@
     }
 
     /**
-     * Returns a mapping of schema types to the set of packages that have access to that schema
-     * type. Each package is represented by a {@link PackageIdentifier}. name and byte[]
-     * certificate.
+     * Returns a mapping of {@link AppSearchSchema} types to the set of packages that have access to
+     * that schema type.
      *
      * <p>A more efficient version of {@link #getSchemasVisibleToPackages}, but it returns a
      * modifiable map. This is not meant to be unhidden and should only be used by internal classes.
@@ -110,7 +144,11 @@
         return mForceOverride;
     }
 
-    /** Builder for {@link SetSchemaRequest} objects. */
+    /**
+     * Builder for {@link SetSchemaRequest} objects.
+     *
+     * <p>Once {@link #build} is called, the instance can no longer be used.
+     */
     public static final class Builder {
         private final Set<AppSearchSchema> mSchemas = new ArraySet<>();
         private final Set<String> mSchemasNotVisibleToSystemUi = new ArraySet<>();
@@ -121,9 +159,11 @@
         private boolean mBuilt = false;
 
         /**
-         * Adds one or more types to the schema.
+         * Adds one or more {@link AppSearchSchema} types to the schema.
          *
-         * <p>Any documents of these types will be visible on system UI surfaces by default.
+         * <p>An {@link AppSearchSchema} object represents one type of structured data.
+         *
+         * @throws IllegalStateException if the builder has already been used.
          */
         @NonNull
         public Builder addSchemas(@NonNull AppSearchSchema... schemas) {
@@ -132,9 +172,11 @@
         }
 
         /**
-         * Adds one or more types to the schema.
+         * Adds a collection of {@link AppSearchSchema} objects to the schema.
          *
-         * <p>Any documents of these types will be visible on system UI surfaces by default.
+         * <p>An {@link AppSearchSchema} object represents one type of structured data.
+         *
+         * @throws IllegalStateException if the builder has already been used.
          */
         @NonNull
         public Builder addSchemas(@NonNull Collection<AppSearchSchema> schemas) {
@@ -145,10 +187,17 @@
         }
 
         /**
-         * Sets visibility on system UI surfaces for the given {@code schemaType}.
+         * Sets whether or not documents from the provided {@code schemaType} will be displayed and
+         * visible on any system UI surface.
+         *
+         * <p>This setting applies to the provided {@code schemaType} only, and does not persist
+         * across {@link AppSearchSession#setSchema} calls.
+         *
+         * <p>By default, documents are displayed and visible on system UI surfaces.
          *
          * @param schemaType The schema type to set visibility on.
          * @param visible Whether the {@code schemaType} will be visible or not.
+         * @throws IllegalStateException if the builder has already been used.
          */
         // Merged list available from getSchemasNotVisibleToSystemUi
         @SuppressLint("MissingGetterMatchingBuilder")
@@ -167,11 +216,25 @@
         }
 
         /**
-         * Sets visibility for a package for the given {@code schemaType}.
+         * Sets whether or not documents from the provided {@code schemaType} can be read by the
+         * specified package.
+         *
+         * <p>Each package is represented by a {@link PackageIdentifier}, containing a package name
+         * and a byte array of type {@link android.content.pm.PackageManager#CERT_INPUT_SHA256}.
+         *
+         * <p>To opt into one-way data sharing with another application, the developer will need to
+         * explicitly grant the other application’s package name and certificate Read access to its
+         * data.
+         *
+         * <p>For two-way data sharing, both applications need to explicitly grant Read access to
+         * one another.
+         *
+         * <p>By default, data sharing between applications is disabled.
          *
          * @param schemaType The schema type to set visibility on.
          * @param visible Whether the {@code schemaType} will be visible or not.
          * @param packageIdentifier Represents the package that will be granted visibility.
+         * @throws IllegalStateException if the builder has already been used.
          */
         // Merged list available from getSchemasVisibleToPackages
         @SuppressLint("MissingGetterMatchingBuilder")
@@ -224,13 +287,15 @@
         }
 
         /**
-         * Configures the {@link SetSchemaRequest} to delete any existing documents that don't
-         * follow the new schema.
+         * Sets whether or not to override the current schema in the {@link AppSearchSession}
+         * database.
          *
-         * <p>By default, this is {@code false} and schema incompatibility causes the {@link
-         * AppSearchSession#setSchema} call to fail.
+         * <p>Call this method whenever backward incompatible changes need to be made by setting
+         * {@code forceOverride} to {@code true}. As a result, during execution of the setSchema
+         * operation, all documents that are incompatible with the new schema will be deleted and
+         * the new schema will be saved and persisted.
          *
-         * @see AppSearchSession#setSchema
+         * <p>By default, this is {@code false}.
          */
         @NonNull
         public Builder setForceOverride(boolean forceOverride) {
@@ -239,10 +304,11 @@
         }
 
         /**
-         * Builds a new {@link SetSchemaRequest}.
+         * Builds a new {@link SetSchemaRequest} object.
          *
-         * @throws IllegalArgumentException If schema types were referenced, but the corresponding
-         *     {@link AppSearchSchema} was never added.
+         * @throws IllegalArgumentException if schema types were referenced, but the corresponding
+         *     {@link AppSearchSchema} type was never added.
+         * @throws IllegalStateException if the builder has already been used.
          */
         @NonNull
         public SetSchemaRequest build() {
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index 2fd5c73..57ee1ec 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -11,6 +11,15 @@
 // 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 java_library {
     name: "service-appsearch",
     installable: true,
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 6c2e30e..e2c211b 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
@@ -654,6 +654,35 @@
         }
     }
 
+    /**
+     * Returns a mapping of package names to all the databases owned by that package.
+     *
+     * <p>This method is inefficient to call repeatedly.
+     */
+    @NonNull
+    public Map<String, Set<String>> getPackageToDatabases() {
+        mReadWriteLock.readLock().lock();
+        try {
+            Map<String, Set<String>> packageToDatabases = new ArrayMap<>();
+            for (String prefix : mSchemaMapLocked.keySet()) {
+                String packageName = getPackageName(prefix);
+
+                Set<String> databases = packageToDatabases.get(packageName);
+                if (databases == null) {
+                    databases = new ArraySet<>();
+                    packageToDatabases.put(packageName, databases);
+                }
+
+                String databaseName = getDatabaseName(prefix);
+                databases.add(databaseName);
+            }
+
+            return packageToDatabases;
+        } finally {
+            mReadWriteLock.readLock().unlock();
+        }
+    }
+
     @GuardedBy("mReadWriteLock")
     private SearchResultPage doQueryLocked(
             @NonNull Set<String> prefixes,
@@ -1211,26 +1240,8 @@
         return schemaProto.getSchema();
     }
 
-    /**
-     * Returns true if the {@code packageName} and {@code databaseName} has the {@code schemaType}
-     */
-    @GuardedBy("mReadWriteLock")
-    boolean hasSchemaTypeLocked(
-            @NonNull String packageName, @NonNull String databaseName, @NonNull String schemaType) {
-        Preconditions.checkNotNull(packageName);
-        Preconditions.checkNotNull(databaseName);
-        Preconditions.checkNotNull(schemaType);
-
-        String prefix = createPrefix(packageName, databaseName);
-        Set<String> schemaTypes = mSchemaMapLocked.get(prefix);
-        if (schemaTypes == null) {
-            return false;
-        }
-
-        return schemaTypes.contains(prefix + schemaType);
-    }
-
     /** Returns a set of all prefixes AppSearchImpl knows about. */
+    // TODO(b/180058203): Remove this method once platform has switched away from using this method.
     @GuardedBy("mReadWriteLock")
     @NonNull
     Set<String> getPrefixesLocked() {
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index fc0299e..2c9477a 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I895f5fb3bcb4be0642c6193000e57d80aafe2166
+I593dfd22279739e5f578e07d36a55cf02ee942c5
diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp
index 54d5039..eb072af 100644
--- a/apex/appsearch/testing/Android.bp
+++ b/apex/appsearch/testing/Android.bp
@@ -11,6 +11,15 @@
 // 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 java_library {
     name: "AppSearchTestUtils",
     srcs: ["java/**/*.java"],
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
index afa633a..9ef6e0b 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
@@ -124,8 +124,7 @@
     @NonNull
     public SearchResultsShim search(
             @NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
-        SearchResults searchResults =
-                mAppSearchSession.search(queryExpression, searchSpec, mExecutor);
+        SearchResults searchResults = mAppSearchSession.search(queryExpression, searchSpec);
         return new SearchResultsShimImpl(searchResults, mExecutor);
     }
 
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
index 6595d8d..69a4c18 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
@@ -75,8 +75,7 @@
     @Override
     public SearchResultsShim search(
             @NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
-        SearchResults searchResults =
-                mGlobalSearchSession.search(queryExpression, searchSpec, mExecutor);
+        SearchResults searchResults = mGlobalSearchSession.search(queryExpression, searchSpec);
         return new SearchResultsShimImpl(searchResults, mExecutor);
     }
 
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java
index 75add81..5f26e8c 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java
@@ -47,7 +47,7 @@
     @NonNull
     public ListenableFuture<List<SearchResult>> getNextPage() {
         SettableFuture<AppSearchResult<List<SearchResult>>> future = SettableFuture.create();
-        mSearchResults.getNextPage(future::set);
+        mSearchResults.getNextPage(mExecutor, future::set);
         return Futures.transform(future, AppSearchResult::getResultValue, mExecutor);
     }
 
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
index 34c7ccb..1428fb1 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
@@ -33,90 +33,16 @@
 public interface AppSearchSessionShim extends Closeable {
 
     /**
-     * Sets the schema that will be used by documents provided to the {@link #put} method.
+     * Sets the schema that represents the organizational structure of data within the AppSearch
+     * database.
      *
-     * <p>The schema provided here is compared to the stored copy of the schema previously supplied
-     * to {@link #setSchema}, if any, to determine how to treat existing documents. The following
-     * types of schema modifications are always safe and are made without deleting any existing
-     * documents:
+     * <p>Upon creating an {@link AppSearchSessionShim}, {@link #setSchema} should be called. If the
+     * schema needs to be updated, or it has not been previously set, then the provided schema will
+     * be saved and persisted to disk. Otherwise, {@link #setSchema} is handled efficiently as a
+     * no-op call.
      *
-     * <ul>
-     *   <li>Addition of new types
-     *   <li>Addition of new {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or
-     *       {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} properties to a
-     *       type
-     *   <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an {@link
-     *       AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link
-     *       AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} property.
-     * </ul>
-     *
-     * <p>The following types of schema changes are not backwards-compatible:
-     *
-     * <ul>
-     *   <li>Removal of an existing type
-     *   <li>Removal of a property from a type
-     *   <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property
-     *   <li>For properties of {@code Document} type, changing the schema type of {@code Document}s
-     *       of that property
-     *   <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an {@link
-     *       AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link
-     *       AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property).
-     *   <li>Adding a {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property.
-     * </ul>
-     *
-     * <p>Supplying a schema with such changes will, by default, result in this call completing its
-     * future with an {@link android.app.appsearch.exceptions.AppSearchException} with a code of
-     * {@link AppSearchResult#RESULT_INVALID_SCHEMA} and a message describing the incompatibility.
-     * In this case the previously set schema will remain active.
-     *
-     * <p>If you need to make non-backwards-compatible changes as described above, you can either:
-     *
-     * <ul>
-     *   <li>Set the {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In
-     *       this case, instead of completing its future with an {@link
-     *       android.app.appsearch.exceptions.AppSearchException} with the {@link
-     *       AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not
-     *       compatible with the new schema will be deleted and the incompatible schema will be
-     *       applied. Incompatible types and deleted types will be set into {@link
-     *       SetSchemaResponse#getIncompatibleTypes()} and {@link
-     *       SetSchemaResponse#getDeletedTypes()}, respectively.
-     *   <li>Add a {@link android.app.appsearch.AppSearchSchema.Migrator} for each incompatible type
-     *       and make no deletion. The migrator will migrate documents from it's old schema version
-     *       to the new version. Migrated types will be set into both {@link
-     *       SetSchemaResponse#getIncompatibleTypes()} and {@link
-     *       SetSchemaResponse#getMigratedTypes()}. See the migration section below.
-     * </ul>
-     *
-     * <p>It is a no-op to set the same schema as has been previously set; this is handled
-     * efficiently.
-     *
-     * <p>By default, documents are visible on platform surfaces. To opt out, call {@code
-     * SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any
-     * visibility settings apply only to the schemas that are included in the {@code request}.
-     * Visibility settings for a schema type do not apply or persist across {@link
-     * SetSchemaRequest}s.
-     *
-     * <p>Migration: make non-backwards-compatible changes will delete all stored documents in old
-     * schema. You can save your documents by setting {@link
-     * android.app.appsearch.AppSearchSchema.Migrator} via the {@link
-     * SetSchemaRequest.Builder#setMigrator} for each type you want to save.
-     *
-     * <p>{@link android.app.appsearch.AppSearchSchema.Migrator#onDowngrade} or {@link
-     * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} will be triggered if the version
-     * number of the schema stored in AppSearch is different with the version in the request.
-     *
-     * <p>If any error or Exception occurred in the {@link
-     * android.app.appsearch.AppSearchSchema.Migrator#onDowngrade}, {@link
-     * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} or {@link
-     * android.app.appsearch.AppSearchMigrationHelper.Transformer#transform}, the migration will be
-     * terminated, the setSchema request will be rejected unless the schema changes are
-     * backwards-compatible, and stored documents won't have any observable changes.
-     *
-     * @param request The schema update request.
-     * @return A {@link ListenableFuture} with exception if we hit any error. Or the pending {@link
-     *     SetSchemaResponse} of performing this operation, if the schema has been successfully set.
-     * @see android.app.appsearch.AppSearchSchema.Migrator
-     * @see android.app.appsearch.AppSearchMigrationHelper.Transformer
+     * @param request the schema to set or update the AppSearch database to.
+     * @return a {@link ListenableFuture} which resolves to a {@link SetSchemaResponse} object.
      */
     // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
     //  exposed.
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 7c7b210..77146e0 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -23,7 +23,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
@@ -202,7 +202,7 @@
      * @hide
      */
     @ChangeId
-    @Disabled // TODO (b/171306433): Enable starting S.
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
     public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L;
 
     @UnsupportedAppUsage
diff --git a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
index 5693abe..43d4873 100644
--- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
+++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
@@ -43,10 +43,10 @@
     boolean isPowerSaveWhitelistApp(String name);
     @UnsupportedAppUsage(maxTargetSdk = 30,
      publicAlternatives = "Use SystemApi {@code PowerWhitelistManager#whitelistAppTemporarily(String, int, String)}.")
-    void addPowerSaveTempWhitelistApp(String name, long duration, int userId, String reason);
-    long addPowerSaveTempWhitelistAppForMms(String name, int userId, String reason);
-    long addPowerSaveTempWhitelistAppForSms(String name, int userId, String reason);
-    long whitelistAppTemporarily(String name, int userId, String reason);
+    void addPowerSaveTempWhitelistApp(String name, long duration, int userId, int reasonCode, String reason);
+    long addPowerSaveTempWhitelistAppForMms(String name, int userId, int reasonCode, String reason);
+    long addPowerSaveTempWhitelistAppForSms(String name, int userId, int reasonCode, String reason);
+    long whitelistAppTemporarily(String name, int userId, int reasonCode, String reason);
     void exitIdle(String reason);
     int setPreIdleTimeoutMode(int Mode);
     void resetPreIdleTimeoutMode();
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index df0e157..df690d0 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -16,8 +16,16 @@
 
 package android.os;
 
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -94,6 +102,239 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface TempAllowListType {}
 
+    /* Reason code for BG-FGS-launch. */
+    /**
+     * BG-FGS-launch is denied.
+     * @hide
+     */
+    public static final int REASON_DENIED = -1;
+    /**
+     * The default reason code if reason is unknown.
+     */
+    public static final int REASON_UNKNOWN = 0;
+    /**
+     * Use REASON_OTHER if there is no better choice.
+     */
+    public static final int REASON_OTHER = 1;
+    /** @hide */
+    public static final int REASON_PROC_STATE_PERSISTENT = 10;
+    /** @hide */
+    public static final int REASON_PROC_STATE_PERSISTENT_UI = 11;
+    /** @hide */
+    public static final int REASON_PROC_STATE_TOP = 12;
+    /** @hide */
+    public static final int REASON_PROC_STATE_BTOP = 13;
+    /** @hide */
+    public static final int REASON_PROC_STATE_FGS = 14;
+    /** @hide */
+    public static final int REASON_PROC_STATE_BFGS = 15;
+    /** @hide */
+    public static final int REASON_UID_VISIBLE = 50;
+    /** @hide */
+    public static final int REASON_SYSTEM_UID = 51;
+    /** @hide */
+    public static final int REASON_ACTIVITY_STARTER = 52;
+    /** @hide */
+    public static final int REASON_START_ACTIVITY_FLAG = 53;
+    /** @hide */
+    public static final int REASON_FGS_BINDING = 54;
+    /** @hide */
+    public static final int REASON_DEVICE_OWNER = 55;
+    /** @hide */
+    public static final int REASON_PROFILE_OWNER = 56;
+    /** @hide */
+    public static final int REASON_COMPANION_DEVICE_MANAGER = 57;
+    /**
+     * START_ACTIVITIES_FROM_BACKGROUND permission.
+     * @hide
+     */
+    public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = 58;
+    /**
+     * START_FOREGROUND_SERVICES_FROM_BACKGROUND permission.
+     * @hide
+     */
+    public static final int REASON_BACKGROUND_FGS_PERMISSION = 59;
+    /** @hide */
+    public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60;
+    /** @hide */
+    public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61;
+    /** @hide */
+    public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62;
+    /** @hide */
+    public static final int REASON_DEVICE_DEMO_MODE = 63;
+    /** @hide */
+    public static final int REASON_EXEMPTED_PACKAGE = 64;
+    /** @hide */
+    public static final int REASON_ALLOWLISTED_PACKAGE  = 65;
+    /**
+     * If it's because of a role,
+     * @hide
+     */
+    public static final int REASON_APPOP = 66;
+
+    /* BG-FGS-launch is allowed by temp-allowlist or system-allowlist.
+    Reason code for temp and system allowlist starts here.
+    */
+    public static final int REASON_GEOFENCING = 100;
+    public static final int REASON_PUSH_MESSAGING = 101;
+    public static final int REASON_ACTIVITY_RECOGNITION = 102;
+
+    /**
+     * Broadcast ACTION_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_BOOT_COMPLETED = 103;
+    /**
+     * Broadcast ACTION_PRE_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_PRE_BOOT_COMPLETED = 104;
+
+    /**
+     * Broadcast ACTION_LOCKED_BOOT_COMPLETED.
+     * @hide
+     */
+    public static final int REASON_LOCKED_BOOT_COMPLETED = 105;
+    /**
+     * Device idle system allowlist, including EXCEPT-IDLE
+     * @hide
+     */
+    public static final int REASON_SYSTEM_ALLOW_LISTED  = 106;
+    /** @hide */
+    public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 107;
+    /**
+     * AlarmManagerService.
+     * @hide
+     */
+    public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 108;
+    /**
+     * ActiveServices.
+     * @hide
+     */
+    public static final int REASON_SERVICE_LAUNCH = 109;
+    /**
+     * KeyChainSystemService.
+     * @hide
+     */
+    public static final int REASON_KEY_CHAIN = 110;
+    /**
+     * PackageManagerService.
+     * @hide
+     */
+    public static final int REASON_PACKAGE_VERIFIER = 111;
+    /**
+     * SyncManager.
+     * @hide
+     */
+    public static final int REASON_SYNC_MANAGER = 112;
+    /**
+     * DomainVerificationProxyV1.
+     * @hide
+     */
+    public static final int REASON_DOMAIN_VERIFICATION_V1 = 113;
+    /**
+     * DomainVerificationProxyV2.
+     * @hide
+     */
+    public static final int REASON_DOMAIN_VERIFICATION_V2 = 114;
+    /** @hide */
+    public static final int REASON_VPN = 115;
+    /**
+     * NotificationManagerService.
+     * @hide
+     */
+    public static final int REASON_NOTIFICATION_SERVICE = 116;
+    /**
+     * Broadcast ACTION_MY_PACKAGE_REPLACED.
+     * @hide
+     */
+    public static final int REASON_PACKAGE_REPLACED = 117;
+    /**
+     * LocationProviderManager.
+     * @hide
+     */
+    public static final int REASON_LOCATION_PROVIDER = 118;
+    /**
+     * MediaButtonReceiver.
+     * @hide
+     */
+    public static final int REASON_MEDIA_BUTTON = 119;
+    /**
+     * InboundSmsHandler.
+     * @hide
+     */
+    public static final int REASON_EVENT_SMS = 120;
+    /**
+     * InboundSmsHandler.
+     * @hide
+     */
+    public static final int REASON_EVENT_MMS = 121;
+    /**
+     * Shell app.
+     * @hide
+     */
+    public static final int REASON_SHELL = 122;
+
+    /**
+     * The list of BG-FGS-Launch and temp-allowlist reason code.
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "REASON_" }, value = {
+            // BG-FGS-Launch reasons.
+            REASON_DENIED,
+            REASON_UNKNOWN,
+            REASON_OTHER,
+            REASON_PROC_STATE_PERSISTENT,
+            REASON_PROC_STATE_PERSISTENT_UI,
+            REASON_PROC_STATE_TOP,
+            REASON_PROC_STATE_BTOP,
+            REASON_PROC_STATE_FGS,
+            REASON_PROC_STATE_BFGS,
+            REASON_UID_VISIBLE,
+            REASON_SYSTEM_UID,
+            REASON_ACTIVITY_STARTER,
+            REASON_START_ACTIVITY_FLAG,
+            REASON_FGS_BINDING,
+            REASON_DEVICE_OWNER,
+            REASON_PROFILE_OWNER,
+            REASON_COMPANION_DEVICE_MANAGER,
+            REASON_BACKGROUND_ACTIVITY_PERMISSION,
+            REASON_BACKGROUND_FGS_PERMISSION,
+            REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION,
+            REASON_INSTR_BACKGROUND_FGS_PERMISSION,
+            REASON_SYSTEM_ALERT_WINDOW_PERMISSION,
+            REASON_DEVICE_DEMO_MODE,
+            REASON_EXEMPTED_PACKAGE,
+            REASON_ALLOWLISTED_PACKAGE,
+            REASON_APPOP,
+            // temp and system allowlist reasons.
+            REASON_GEOFENCING,
+            REASON_PUSH_MESSAGING,
+            REASON_ACTIVITY_RECOGNITION,
+            REASON_BOOT_COMPLETED,
+            REASON_PRE_BOOT_COMPLETED,
+            REASON_LOCKED_BOOT_COMPLETED,
+            REASON_SYSTEM_ALLOW_LISTED,
+            REASON_ALARM_MANAGER_ALARM_CLOCK,
+            REASON_ALARM_MANAGER_WHILE_IDLE,
+            REASON_SERVICE_LAUNCH,
+            REASON_KEY_CHAIN,
+            REASON_PACKAGE_VERIFIER,
+            REASON_SYNC_MANAGER,
+            REASON_DOMAIN_VERIFICATION_V1,
+            REASON_DOMAIN_VERIFICATION_V2,
+            REASON_VPN,
+            REASON_NOTIFICATION_SERVICE,
+            REASON_PACKAGE_REPLACED,
+            REASON_LOCATION_PROVIDER,
+            REASON_MEDIA_BUTTON,
+            REASON_EVENT_SMS,
+            REASON_EVENT_MMS,
+            REASON_SHELL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ReasonCode {}
+
     /**
      * @hide
      */
@@ -184,19 +425,34 @@
      *
      * @param packageName The package to add to the temp whitelist
      * @param durationMs  How long to keep the app on the temp whitelist for (in milliseconds)
+     * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
+     * @param reason a optional human readable reason string, could be null or empty string.
      */
     @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
-    public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) {
-        String reason = "from:" + UserHandle.formatUid(Binder.getCallingUid());
+    public void whitelistAppTemporarily(@NonNull String packageName, long durationMs,
+            @ReasonCode int reasonCode, @Nullable String reason) {
         try {
             mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(),
-                    reason);
+                    reasonCode, reason);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
+     * Add an app to the temporary whitelist for a short amount of time.
+     *
+     * @param packageName The package to add to the temp whitelist
+     * @param durationMs  How long to keep the app on the temp whitelist for (in milliseconds)
+     * @deprecated Use {@link #whitelistAppTemporarily(String, long, int, String)} instead
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+    public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) {
+        whitelistAppTemporarily(packageName, durationMs, REASON_UNKNOWN, packageName);
+    }
+
+    /**
      * Add an app to the temporary whitelist for a short amount of time for a specific reason. The
      * temporary whitelist is kept separately from the permanent whitelist and apps are
      * automatically removed from the temporary whitelist after a predetermined amount of time.
@@ -204,27 +460,179 @@
      * @param packageName The package to add to the temp whitelist
      * @param event       The reason to add the app to the temp whitelist
      * @param reason      A human-readable reason explaining why the app is temp whitelisted. Only
-     *                    used for logging purposes
+     *                    used for logging purposes. Could be null or empty string.
+     * @return The duration (in milliseconds) that the app is whitelisted for
+     * @deprecated Use {@link #whitelistAppTemporarilyForEvent(String, int, int, String)} instead
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
+    public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
+            @WhitelistEvent int event, @Nullable String reason) {
+        return whitelistAppTemporarilyForEvent(packageName, event, REASON_UNKNOWN, reason);
+    }
+
+    /**
+     * Add an app to the temporary whitelist for a short amount of time for a specific reason. The
+     * temporary whitelist is kept separately from the permanent whitelist and apps are
+     * automatically removed from the temporary whitelist after a predetermined amount of time.
+     *
+     * @param packageName The package to add to the temp whitelist
+     * @param event       The reason to add the app to the temp whitelist
+     * @param reasonCode  one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
+     * @param reason      A human-readable reason explaining why the app is temp whitelisted. Only
+     *                    used for logging purposes. Could be null or empty string.
      * @return The duration (in milliseconds) that the app is whitelisted for
      */
     @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
     public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
-            @WhitelistEvent int event, @NonNull String reason) {
+            @WhitelistEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
         try {
             switch (event) {
                 case EVENT_MMS:
                     return mService.addPowerSaveTempWhitelistAppForMms(
-                            packageName, mContext.getUserId(), reason);
+                            packageName, mContext.getUserId(), reasonCode, reason);
                 case EVENT_SMS:
                     return mService.addPowerSaveTempWhitelistAppForSms(
-                            packageName, mContext.getUserId(), reason);
+                            packageName, mContext.getUserId(), reasonCode, reason);
                 case EVENT_UNSPECIFIED:
                 default:
                     return mService.whitelistAppTemporarily(
-                            packageName, mContext.getUserId(), reason);
+                            packageName, mContext.getUserId(), reasonCode, reason);
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * @hide
+     */
+    public static @ReasonCode int getReasonCodeFromProcState(int procState) {
+        if (procState <= PROCESS_STATE_PERSISTENT) {
+            return REASON_PROC_STATE_PERSISTENT;
+        } else if (procState <= PROCESS_STATE_PERSISTENT_UI) {
+            return REASON_PROC_STATE_PERSISTENT_UI;
+        } else if (procState <= PROCESS_STATE_TOP) {
+            return REASON_PROC_STATE_TOP;
+        } else if (procState <= PROCESS_STATE_BOUND_TOP) {
+            return REASON_PROC_STATE_BTOP;
+        } else if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) {
+            return REASON_PROC_STATE_FGS;
+        } else if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+            return REASON_PROC_STATE_BFGS;
+        } else {
+            return REASON_DENIED;
+        }
+    }
+
+    /**
+     * Return string name of the integer reason code.
+     * @hide
+     * @param reasonCode
+     * @return string name of the reason code.
+     */
+    public static String reasonCodeToString(@ReasonCode int reasonCode) {
+        switch (reasonCode) {
+            case REASON_DENIED:
+                return "DENIED";
+            case REASON_UNKNOWN:
+                return "UNKNOWN";
+            case REASON_OTHER:
+                return "OTHER";
+            case REASON_PROC_STATE_PERSISTENT:
+                return "PROC_STATE_PERSISTENT";
+            case REASON_PROC_STATE_PERSISTENT_UI:
+                return "PROC_STATE_PERSISTENT_UI";
+            case REASON_PROC_STATE_TOP:
+                return "PROC_STATE_TOP";
+            case REASON_PROC_STATE_BTOP:
+                return "PROC_STATE_BTOP";
+            case REASON_PROC_STATE_FGS:
+                return "PROC_STATE_FGS";
+            case REASON_PROC_STATE_BFGS:
+                return "PROC_STATE_BFGS";
+            case REASON_UID_VISIBLE:
+                return "UID_VISIBLE";
+            case REASON_SYSTEM_UID:
+                return "SYSTEM_UID";
+            case REASON_ACTIVITY_STARTER:
+                return "ACTIVITY_STARTER";
+            case REASON_START_ACTIVITY_FLAG:
+                return "START_ACTIVITY_FLAG";
+            case REASON_FGS_BINDING:
+                return "FGS_BINDING";
+            case REASON_DEVICE_OWNER:
+                return "DEVICE_OWNER";
+            case REASON_PROFILE_OWNER:
+                return "PROFILE_OWNER";
+            case REASON_COMPANION_DEVICE_MANAGER:
+                return "COMPANION_DEVICE_MANAGER";
+            case REASON_BACKGROUND_ACTIVITY_PERMISSION:
+                return "BACKGROUND_ACTIVITY_PERMISSION";
+            case REASON_BACKGROUND_FGS_PERMISSION:
+                return "BACKGROUND_FGS_PERMISSION";
+            case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION:
+                return "INSTR_BACKGROUND_ACTIVITY_PERMISSION";
+            case REASON_INSTR_BACKGROUND_FGS_PERMISSION:
+                return "INSTR_BACKGROUND_FGS_PERMISSION";
+            case REASON_SYSTEM_ALERT_WINDOW_PERMISSION:
+                return "SYSTEM_ALERT_WINDOW_PERMISSION";
+            case REASON_DEVICE_DEMO_MODE:
+                return "DEVICE_DEMO_MODE";
+            case REASON_EXEMPTED_PACKAGE:
+                return "EXEMPTED_PACKAGE";
+            case REASON_ALLOWLISTED_PACKAGE:
+                return "ALLOWLISTED_PACKAGE";
+            case REASON_APPOP:
+                return "APPOP";
+            case REASON_GEOFENCING:
+                return "GEOFENCING";
+            case REASON_PUSH_MESSAGING:
+                return "PUSH_MESSAGING";
+            case REASON_ACTIVITY_RECOGNITION:
+                return "ACTIVITY_RECOGNITION";
+            case REASON_BOOT_COMPLETED:
+                return "BOOT_COMPLETED";
+            case REASON_PRE_BOOT_COMPLETED:
+                return "PRE_BOOT_COMPLETED";
+            case REASON_LOCKED_BOOT_COMPLETED:
+                return "LOCKED_BOOT_COMPLETED";
+            case REASON_SYSTEM_ALLOW_LISTED:
+                return "SYSTEM_ALLOW_LISTED";
+            case REASON_ALARM_MANAGER_ALARM_CLOCK:
+                return "ALARM_MANAGER_ALARM_CLOCK";
+            case REASON_ALARM_MANAGER_WHILE_IDLE:
+                return "ALARM_MANAGER_WHILE_IDLE";
+            case REASON_SERVICE_LAUNCH:
+                return "SERVICE_LAUNCH";
+            case REASON_KEY_CHAIN:
+                return "KEY_CHAIN";
+            case REASON_PACKAGE_VERIFIER:
+                return "PACKAGE_VERIFIER";
+            case REASON_SYNC_MANAGER:
+                return "SYNC_MANAGER";
+            case REASON_DOMAIN_VERIFICATION_V1:
+                return "DOMAIN_VERIFICATION_V1";
+            case REASON_DOMAIN_VERIFICATION_V2:
+                return "DOMAIN_VERIFICATION_V2";
+            case REASON_VPN:
+                return "VPN";
+            case REASON_NOTIFICATION_SERVICE:
+                return "NOTIFICATION_SERVICE";
+            case REASON_PACKAGE_REPLACED:
+                return "PACKAGE_REPLACED";
+            case REASON_LOCATION_PROVIDER:
+                return "LOCATION_PROVIDER";
+            case REASON_MEDIA_BUTTON:
+                return "MEDIA_BUTTON";
+            case REASON_EVENT_SMS:
+                return "EVENT_SMS";
+            case REASON_EVENT_MMS:
+                return "EVENT_MMS";
+            case REASON_SHELL:
+                return "SHELL";
+            default:
+                return  "(unknown:" + reasonCode + ")";
+        }
+    }
 }
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index e045b0f..5e5717d11 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import android.annotation.Nullable;
+import android.os.PowerWhitelistManager.ReasonCode;
 import android.os.PowerWhitelistManager.TempAllowListType;
 
 import com.android.server.deviceidle.IDeviceIdleConstraint;
@@ -34,6 +36,10 @@
     void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
             long duration, int userId, boolean sync, String reason);
 
+    void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
+            long duration, int userId, boolean sync, @ReasonCode int reasonCode,
+            @Nullable String reason);
+
     /**
      * Called by ActivityManagerService to directly add UID to DeviceIdleController's temp
      * allowlist.
@@ -41,11 +47,12 @@
      * @param duration duration in milliseconds
      * @param type temp allowlist type defined at {@link TempAllowListType}
      * @param sync
+     * @param reasonCode one of {@link ReasonCode}
      * @param reason
      */
     void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
-            @TempAllowListType int type, boolean sync,
-            String reason);
+            @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode,
+            @Nullable String reason);
 
     // duration in milliseconds
     long getNotificationAllowlistDuration();
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 8f7f705..ac28e82 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -16,12 +16,17 @@
 
 package com.android.server;
 
+import static android.os.PowerWhitelistManager.REASON_SHELL;
+import static android.os.PowerWhitelistManager.REASON_UNKNOWN;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.Process.INVALID_UID;
+
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
-import android.app.BroadcastOptions;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -56,6 +61,7 @@
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
+import android.os.PowerWhitelistManager.ReasonCode;
 import android.os.PowerWhitelistManager.TempAllowListType;
 import android.os.Process;
 import android.os.RemoteException;
@@ -1838,31 +1844,34 @@
         }
 
         @Override
-        public long whitelistAppTemporarily(String packageName, int userId, String reason)
-                throws RemoteException {
+        public long whitelistAppTemporarily(String packageName, int userId,
+                @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException {
             // At least 10 seconds.
             long durationMs = Math.max(10_000L, mConstants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS / 2);
-            addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason);
+            addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reasonCode,
+                    reason);
             return durationMs;
         }
 
         @Override
-        public void addPowerSaveTempWhitelistApp(String packageName, long duration,
-                int userId, String reason) throws RemoteException {
-            addPowerSaveTempAllowlistAppChecked(packageName, duration, userId, reason);
+        public void addPowerSaveTempWhitelistApp(String packageName, long duration, int userId,
+                @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException {
+            addPowerSaveTempAllowlistAppChecked(packageName, duration, userId, reasonCode, reason);
         }
 
-        @Override public long addPowerSaveTempWhitelistAppForMms(String packageName,
-                int userId, String reason) throws RemoteException {
+        @Override public long addPowerSaveTempWhitelistAppForMms(String packageName, int userId,
+                @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException {
             long durationMs = mConstants.MMS_TEMP_APP_ALLOWLIST_DURATION_MS;
-            addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason);
+            addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reasonCode,
+                    reason);
             return durationMs;
         }
 
-        @Override public long addPowerSaveTempWhitelistAppForSms(String packageName,
-                int userId, String reason) throws RemoteException {
+        @Override public long addPowerSaveTempWhitelistAppForSms(String packageName, int userId,
+                @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException {
             long durationMs = mConstants.SMS_TEMP_APP_ALLOWLIST_DURATION_MS;
-            addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason);
+            addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reasonCode,
+                    reason);
             return durationMs;
         }
 
@@ -1934,18 +1943,29 @@
         }
 
         // duration in milliseconds
+        @Deprecated
         @Override
         public void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
-                long duration, int userId, boolean sync, String reason) {
+                long duration, int userId, boolean sync, @Nullable String reason) {
             addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration,
-                    userId, sync, reason);
+                    userId, sync, REASON_UNKNOWN, reason);
+        }
+
+        @Override
+        public void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
+                long duration, int userId, boolean sync, @ReasonCode int reasonCode,
+                @Nullable String reason) {
+            addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration,
+                    userId, sync, reasonCode, reason);
         }
 
         // duration in milliseconds
         @Override
         public void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
-                @TempAllowListType int type, boolean sync, String reason) {
-            addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync, reason);
+                @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode,
+                @Nullable String reason) {
+            addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync,
+                    reasonCode, reason);
         }
 
         // duration in milliseconds
@@ -2293,7 +2313,7 @@
                 filter.addAction(Intent.ACTION_SCREEN_ON);
                 getContext().registerReceiver(mInteractivityReceiver, filter);
 
-                mLocalActivityManager.setDeviceIdleWhitelist(
+                mLocalActivityManager.setDeviceIdleAllowlist(
                         mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray);
                 mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
 
@@ -2671,7 +2691,8 @@
     }
 
     void addPowerSaveTempAllowlistAppChecked(String packageName, long duration,
-            int userId, String reason) throws RemoteException {
+            int userId, @ReasonCode int reasonCode, @Nullable String reason)
+            throws RemoteException {
         getContext().enforceCallingPermission(
                 Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
                 "No permission to change device idle whitelist");
@@ -2686,7 +2707,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             addPowerSaveTempAllowlistAppInternal(callingUid,
-                    packageName, duration, userId, true, reason);
+                    packageName, duration, userId, true, reasonCode, reason);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -2718,12 +2739,12 @@
      * app an exemption to access network and acquire wakelocks.
      */
     void addPowerSaveTempAllowlistAppInternal(int callingUid, String packageName,
-            long duration, int userId, boolean sync, String reason) {
+            long duration, int userId, boolean sync, @ReasonCode int reasonCode,
+            @Nullable String reason) {
         try {
             int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId);
             addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration,
-                    BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED, sync,
-                    reason);
+                    TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, sync, reasonCode, reason);
         } catch (NameNotFoundException e) {
         }
     }
@@ -2733,7 +2754,8 @@
      * app an exemption to access network and acquire wakelocks.
      */
     void addPowerSaveTempWhitelistAppDirectInternal(int callingUid, int uid,
-            long duration, @TempAllowListType int type, boolean sync, String reason) {
+            long duration, @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode,
+            @Nullable String reason) {
         final long timeNow = SystemClock.elapsedRealtime();
         boolean informWhitelistChanged = false;
         int appId = UserHandle.getAppId(uid);
@@ -2765,7 +2787,8 @@
                 } catch (RemoteException e) {
                 }
                 postTempActiveTimeoutMessage(uid, duration);
-                updateTempWhitelistAppIdsLocked(uid, true, duration, type);
+                updateTempWhitelistAppIdsLocked(uid, true, duration, type, reasonCode,
+                        reason, callingUid);
                 if (sync) {
                     informWhitelistChanged = true;
                 } else {
@@ -2844,12 +2867,13 @@
     }
 
     @GuardedBy("this")
-    private void onAppRemovedFromTempWhitelistLocked(int uid, String reason) {
+    private void onAppRemovedFromTempWhitelistLocked(int uid, @Nullable String reason) {
         if (DEBUG) {
             Slog.d(TAG, "Removing uid " + uid + " from temp whitelist");
         }
         final int appId = UserHandle.getAppId(uid);
-        updateTempWhitelistAppIdsLocked(uid, false, 0, 0);
+        updateTempWhitelistAppIdsLocked(uid, false, 0, 0, REASON_UNKNOWN,
+                reason, INVALID_UID);
         mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS, appId, 0)
                 .sendToTarget();
         reportTempWhitelistChangedLocked(uid, false);
@@ -3860,7 +3884,7 @@
         mPowerSaveWhitelistUserAppIdArray = buildAppIdArray(null,
                 mPowerSaveWhitelistUserApps, mPowerSaveWhitelistUserAppIds);
         if (mLocalActivityManager != null) {
-            mLocalActivityManager.setDeviceIdleWhitelist(
+            mLocalActivityManager.setDeviceIdleAllowlist(
                     mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray);
         }
         if (mLocalPowerManager != null) {
@@ -3880,9 +3904,14 @@
      * @param durationMs duration in milliseconds to add to temp allowlist, only valid when
      *                   param adding is true.
      * @param type temp allowlist type defined at {@link TempAllowListType}
+     * @prama reasonCode one of {@Link ReasonCode}
+     * @param reason A human-readable reason for logging purposes.
+     * @param callingUid the callingUid that setup this temp-allowlist, only valid when param adding
+     *                   is true.
      */
     private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs,
-            @TempAllowListType int type) {
+            @TempAllowListType int type, @ReasonCode int reasonCode, @Nullable String reason,
+            int callingUid) {
         final int size = mTempWhitelistAppIdEndTimes.size();
         if (mTempWhitelistAppIdArray.length != size) {
             mTempWhitelistAppIdArray = new int[size];
@@ -3895,8 +3924,8 @@
                 Slog.d(TAG, "Setting activity manager temp whitelist to "
                         + Arrays.toString(mTempWhitelistAppIdArray));
             }
-            mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, uid,
-                    adding, durationMs, type);
+            mLocalActivityManager.updateDeviceIdleTempAllowlist(mTempWhitelistAppIdArray, uid,
+                    adding, durationMs, type, reasonCode, reason, callingUid);
         }
         if (mLocalPowerManager != null) {
             if (DEBUG) {
@@ -4428,7 +4457,8 @@
                     if (removePkg) {
                         removePowerSaveTempAllowlistAppChecked(arg, shell.userId);
                     } else {
-                        addPowerSaveTempAllowlistAppChecked(arg, duration, shell.userId, "shell");
+                        addPowerSaveTempAllowlistAppChecked(arg, duration, shell.userId,
+                                REASON_SHELL, "shell");
                     }
                 } catch (Exception e) {
                     pw.println("Failed: " + e);
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 559a434..ea73369 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -423,6 +423,8 @@
         static final String KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA = "allow_while_idle_compat_quota";
         private static final String KEY_ALLOW_WHILE_IDLE_WINDOW = "allow_while_idle_window";
 
+        private static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps";
+
         private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
         private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
         private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
@@ -454,6 +456,8 @@
         private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 72;
 
         private static final long DEFAULT_ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour.
+        // TODO (b/171306433): Change to true by default.
+        private static final boolean DEFAULT_CRASH_NON_CLOCK_APPS = false;
 
         // Minimum futurity of a new alarm
         public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -495,6 +499,13 @@
          */
         public long ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW;
 
+        /**
+         * Whether or not to crash callers that use setExactAndAllowWhileIdle or setAlarmClock
+         * but don't hold the required permission. This is useful to catch broken
+         * apps and reverting to a softer failure in case of broken apps.
+         */
+        public boolean CRASH_NON_CLOCK_APPS = DEFAULT_CRASH_NON_CLOCK_APPS;
+
         private long mLastAllowWhileIdleWhitelistDuration = -1;
 
         Constants() {
@@ -607,6 +618,10 @@
                                     KEY_TIME_TICK_ALLOWED_WHILE_IDLE,
                                     DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE);
                             break;
+                        case KEY_CRASH_NON_CLOCK_APPS:
+                            CRASH_NON_CLOCK_APPS = properties.getBoolean(KEY_CRASH_NON_CLOCK_APPS,
+                                    DEFAULT_CRASH_NON_CLOCK_APPS);
+                            break;
                         default:
                             if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
                                 // The quotas need to be updated in order, so we can't just rely
@@ -741,6 +756,9 @@
             pw.print(KEY_TIME_TICK_ALLOWED_WHILE_IDLE, TIME_TICK_ALLOWED_WHILE_IDLE);
             pw.println();
 
+            pw.print(KEY_CRASH_NON_CLOCK_APPS, CRASH_NON_CLOCK_APPS);
+            pw.println();
+
             pw.decreaseIndent();
         }
 
@@ -1914,11 +1932,12 @@
     }
 
     /**
-     * Returns true if the given uid is on the system or user's power save exclusion list.
+     * Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact,
+     * allow-while-idle alarms.
      */
-    boolean isWhitelisted(int uid) {
-        return (mLocalDeviceIdleController == null || mLocalDeviceIdleController.isAppOnWhitelist(
-                UserHandle.getAppId(uid)));
+    boolean isExemptFromPermission(int uid) {
+        return (UserHandle.isSameApp(mSystemUiUid, uid) || mLocalDeviceIdleController == null
+                || mLocalDeviceIdleController.isAppOnWhitelist(UserHandle.getAppId(uid)));
     }
 
     /**
@@ -1949,7 +1968,7 @@
                     if (windowLength != AlarmManager.WINDOW_EXACT) {
                         needsPermission = false;
                         lowQuota = true;
-                        idleOptions = isWhitelisted(callingUid) ? mOptsWithFgs.toBundle()
+                        idleOptions = isExemptFromPermission(callingUid) ? mOptsWithFgs.toBundle()
                                 : mOptsWithoutFgs.toBundle();
                     } else if (alarmClock != null) {
                         needsPermission = true;
@@ -1966,16 +1985,22 @@
                     idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null;
                 }
                 if (needsPermission && !canScheduleExactAlarms()) {
-                    if (alarmClock == null && isWhitelisted(callingUid)) {
+                    if (alarmClock == null && isExemptFromPermission(callingUid)) {
                         // If the app is on the full system allow-list (not except-idle), we still
                         // allow the alarms, but with a lower quota to keep pre-S compatibility.
                         lowQuota = true;
                     } else {
-                        final String errorMessage = "Caller needs to hold "
+                        final String errorMessage = "Caller " + callingPackage + " needs to hold "
                                 + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set "
                                 + ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock")
                                 + " alarms.";
-                        throw new SecurityException(errorMessage);
+                        if (mConstants.CRASH_NON_CLOCK_APPS) {
+                            throw new SecurityException(errorMessage);
+                        } else {
+                            Slog.wtf(TAG, errorMessage);
+                            idleOptions = mOptsWithoutFgs.toBundle();
+                            lowQuota = allowWhileIdle;
+                        }
                     }
                 }
                 if (lowQuota) {
@@ -2933,7 +2958,7 @@
         if (UserHandle.isCore(uid) || uid == mSystemUiUid) {
             return;
         }
-        if (isWhitelisted(uid)) {
+        if (isExemptFromPermission(uid)) {
             return;
         }
         if (!CompatChanges.isChangeEnabled(
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 c5d3b7a..040a116 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -53,7 +53,6 @@
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Handler;
 import android.os.LimitExceededException;
 import android.os.Looper;
@@ -153,8 +152,7 @@
     /** The maximum number of jobs that we allow an unprivileged app to schedule */
     private static final int MAX_JOBS_PER_APP = 100;
     /** The number of the most recently completed jobs to keep track of for debugging purposes. */
-    private static final int NUM_COMPLETED_JOB_HISTORY =
-            Build.IS_USERDEBUG || Build.IS_ENG ? 25 : 0;
+    private static final int NUM_COMPLETED_JOB_HISTORY = 20;
 
     @VisibleForTesting
     public static Clock sSystemClock = Clock.systemUTC();
@@ -889,8 +887,10 @@
     }
 
     @Override
-    public void onUserStarting(@NonNull TargetUser user) {
+    public void onUserUnlocked(@NonNull TargetUser user) {
         synchronized (mLock) {
+            // Note that the user has started after its unlocked instead of when the user
+            // actually starts because the storage won't be decrypted until unlock.
             mStartedUsers = ArrayUtils.appendInt(mStartedUsers, user.getUserIdentifier());
         }
         // Let's kick any outstanding jobs for this user.
@@ -898,12 +898,6 @@
     }
 
     @Override
-    public void onUserUnlocking(@NonNull TargetUser user) {
-        // Let's kick any outstanding jobs for this user.
-        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
-    }
-
-    @Override
     public void onUserStopping(@NonNull TargetUser user) {
         synchronized (mLock) {
             mStartedUsers = ArrayUtils.removeInt(mStartedUsers, user.getUserIdentifier());
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 be91947..2a23d60 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -271,7 +271,9 @@
                 if (job.shouldTreatAsExpeditedJob()) {
                     // TODO(171305774): The job should run on the little cores. We'll probably need
                     // another binding flag for that.
-                    bindFlags = Context.BIND_AUTO_CREATE;
+                    bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+                            | Context.BIND_ALMOST_PERCEPTIBLE
+                            | Context.BIND_ALLOW_NETWORK_ACCESS;
                 } else {
                     bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
                             | Context.BIND_NOT_PERCEPTIBLE;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 824fa7f..2faa836 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -828,8 +828,8 @@
     }
 
     @NonNull
-    private ShrinkableDebits getEJQuotaLocked(final int userId,
-            @NonNull final String packageName) {
+    @VisibleForTesting
+    ShrinkableDebits getEJDebitsLocked(final int userId, @NonNull final String packageName) {
         ShrinkableDebits debits = mEJStats.get(userId, packageName);
         if (debits == null) {
             debits = new ShrinkableDebits(
@@ -931,7 +931,7 @@
 
     @VisibleForTesting
     long getRemainingEJExecutionTimeLocked(final int userId, @NonNull final String packageName) {
-        ShrinkableDebits quota = getEJQuotaLocked(userId, packageName);
+        ShrinkableDebits quota = getEJDebitsLocked(userId, packageName);
         if (quota.getStandbyBucketLocked() == NEVER_INDEX) {
             return 0;
         }
@@ -948,7 +948,7 @@
                 if (ts.endTimeElapsed < windowStartTimeElapsed) {
                     final long duration = ts.endTimeElapsed - ts.startTimeElapsed;
                     remainingMs += duration;
-                    quota.transactOnDebitsLocked(-duration);
+                    quota.transactLocked(-duration);
                     timingSessions.remove(0);
                 } else if (ts.startTimeElapsed < windowStartTimeElapsed) {
                     remainingMs += windowStartTimeElapsed - ts.startTimeElapsed;
@@ -960,15 +960,16 @@
             }
         }
 
+        TopAppTimer topAppTimer = mTopAppTrackers.get(userId, packageName);
+        if (topAppTimer != null && topAppTimer.isActive()) {
+            remainingMs += topAppTimer.getPendingReward(nowElapsed);
+        }
+
         Timer timer = mEJPkgTimers.get(userId, packageName);
         if (timer == null) {
             return remainingMs;
         }
-        // There's a case where the debits tally is 0 but a currently running HPJ still counts
-        // towards quota. If the app gets a reward in this case, the reward is lost and the HPJ
-        // run is still fully counted.
-        // TODO(171305774)/STOPSHIP: make sure getting rewards while HPJ currently executing isn't
-        // treated negatively
+
         return remainingMs - timer.getCurrentDuration(sElapsedRealtimeClock.millis());
     }
 
@@ -1081,7 +1082,7 @@
         }
 
         final long nowElapsed = sElapsedRealtimeClock.millis();
-        ShrinkableDebits quota = getEJQuotaLocked(userId, packageName);
+        ShrinkableDebits quota = getEJDebitsLocked(userId, packageName);
         final long limitMs = getEJLimitMsLocked(packageName, quota.getStandbyBucketLocked());
         final long startWindowElapsed = Math.max(0, nowElapsed - mEJLimitWindowSizeMs);
         long remainingDeadSpaceMs = remainingExecutionTimeMs;
@@ -1372,6 +1373,11 @@
     @VisibleForTesting
     void saveTimingSession(final int userId, @NonNull final String packageName,
             @NonNull final TimingSession session, boolean isExpedited) {
+        saveTimingSession(userId, packageName, session, isExpedited, 0);
+    }
+
+    private void saveTimingSession(final int userId, @NonNull final String packageName,
+            @NonNull final TimingSession session, boolean isExpedited, long debitAdjustment) {
         synchronized (mLock) {
             final SparseArrayMap<String, List<TimingSession>> sessionMap =
                     isExpedited ? mEJTimingSessions : mTimingSessions;
@@ -1382,8 +1388,9 @@
             }
             sessions.add(session);
             if (isExpedited) {
-                final ShrinkableDebits quota = getEJQuotaLocked(userId, packageName);
-                quota.transactOnDebitsLocked(session.endTimeElapsed - session.startTimeElapsed);
+                final ShrinkableDebits quota = getEJDebitsLocked(userId, packageName);
+                quota.transactLocked(session.endTimeElapsed - session.startTimeElapsed
+                        + debitAdjustment);
             } else {
                 // Adding a new session means that the current stats are now incorrect.
                 invalidateAllExecutionStatsLocked(userId, packageName);
@@ -1396,15 +1403,34 @@
     private void grantRewardForInstantEvent(
             final int userId, @NonNull final String packageName, final long credit) {
         synchronized (mLock) {
-            final ShrinkableDebits quota = getEJQuotaLocked(userId, packageName);
-            quota.transactOnDebitsLocked(-credit);
-            if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
-                    userId, packageName)) {
+            final long nowElapsed = sElapsedRealtimeClock.millis();
+            final ShrinkableDebits quota = getEJDebitsLocked(userId, packageName);
+            if (transactQuotaLocked(userId, packageName, nowElapsed, quota, credit)
+                    && maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName)) {
                 mStateChangedListener.onControllerStateChanged();
             }
         }
     }
 
+    private boolean transactQuotaLocked(final int userId, @NonNull final String packageName,
+            final long nowElapsed, @NonNull ShrinkableDebits debits, final long credit) {
+        final long oldTally = debits.getTallyLocked();
+        final long leftover = debits.transactLocked(-credit);
+        if (DEBUG) {
+            Slog.d(TAG, "debits overflowed by " + leftover);
+        }
+        boolean changed = oldTally != debits.getTallyLocked();
+        if (leftover != 0) {
+            // Only adjust timer if its active.
+            final Timer ejTimer = mEJPkgTimers.get(userId, packageName);
+            if (ejTimer != null && ejTimer.isActive()) {
+                ejTimer.updateDebitAdjustment(nowElapsed, leftover);
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
     private final class EarliestEndTimeFunctor implements Consumer<List<TimingSession>> {
         public long earliestEndElapsed = Long.MAX_VALUE;
 
@@ -1875,7 +1901,8 @@
         }
     }
 
-    private static final class ShrinkableDebits {
+    @VisibleForTesting
+    static final class ShrinkableDebits {
         /** The amount of quota remaining. Can be negative if limit changes. */
         private long mDebitTally;
         private int mStandbyBucket;
@@ -1893,8 +1920,11 @@
          * Negative if the tally should decrease (therefore increasing available quota);
          * or positive if the tally should increase (therefore decreasing available quota).
          */
-        void transactOnDebitsLocked(final long amount) {
+        long transactLocked(final long amount) {
+            final long leftover = amount < 0 && Math.abs(amount) > mDebitTally
+                    ? mDebitTally + amount : 0;
             mDebitTally = Math.max(0, mDebitTally + amount);
+            return leftover;
         }
 
         void setStandbyBucketLocked(int standbyBucket) {
@@ -1927,6 +1957,7 @@
         private final ArraySet<JobStatus> mRunningBgJobs = new ArraySet<>();
         private long mStartTimeElapsed;
         private int mBgJobCount;
+        private long mDebitAdjustment;
 
         Timer(int uid, int userId, String packageName, boolean regularJobTimer) {
             mPkg = new Package(userId, packageName);
@@ -1957,6 +1988,7 @@
                 if (mRunningBgJobs.size() == 1) {
                     // Started tracking the first job.
                     mStartTimeElapsed = sElapsedRealtimeClock.millis();
+                    mDebitAdjustment = 0;
                     if (mRegularJobTimer) {
                         // Starting the timer means that all cached execution stats are now
                         // incorrect.
@@ -1988,6 +2020,11 @@
             }
         }
 
+        void updateDebitAdjustment(long nowElapsed, long debit) {
+            // Make sure we don't have a credit larger than the expected session.
+            mDebitAdjustment = Math.max(mDebitAdjustment + debit, mStartTimeElapsed - nowElapsed);
+        }
+
         /**
          * Stops tracking all jobs and cancels any pending alarms. This should only be called if
          * the Timer is not going to be used anymore.
@@ -2003,7 +2040,8 @@
                 return;
             }
             TimingSession ts = new TimingSession(mStartTimeElapsed, nowElapsed, mBgJobCount);
-            saveTimingSession(mPkg.userId, mPkg.packageName, ts, !mRegularJobTimer);
+            saveTimingSession(mPkg.userId, mPkg.packageName, ts, !mRegularJobTimer,
+                    mDebitAdjustment);
             mBgJobCount = 0;
             // Don't reset the tracked jobs list as we need to keep tracking the current number
             // of jobs.
@@ -2030,7 +2068,7 @@
 
         long getCurrentDuration(long nowElapsed) {
             synchronized (mLock) {
-                return !isActive() ? 0 : nowElapsed - mStartTimeElapsed;
+                return !isActive() ? 0 : nowElapsed - mStartTimeElapsed + mDebitAdjustment;
             }
         }
 
@@ -2059,6 +2097,7 @@
                 // Start timing from unplug.
                 if (mRunningBgJobs.size() > 0) {
                     mStartTimeElapsed = nowElapsed;
+                    mDebitAdjustment = 0;
                     // NOTE: this does have the unfortunate consequence that if the device is
                     // repeatedly plugged in and unplugged, or an app changes foreground state
                     // very frequently, the job count for a package may be artificially high.
@@ -2128,6 +2167,11 @@
             pw.print(", ");
             pw.print(mBgJobCount);
             pw.print(" running bg jobs");
+            if (!mRegularJobTimer) {
+                pw.print(" (debit adj=");
+                pw.print(mDebitAdjustment);
+                pw.print(")");
+            }
             pw.println();
             pw.increaseIndent();
             for (int i = 0; i < mRunningBgJobs.size(); i++) {
@@ -2171,6 +2215,21 @@
             mPkg = new Package(userId, packageName);
         }
 
+        private int calculateTimeChunks(final long nowElapsed) {
+            final long totalTopTimeMs = nowElapsed - mStartTimeElapsed;
+            int numTimeChunks = (int) (totalTopTimeMs / mEJTopAppTimeChunkSizeMs);
+            final long remainderMs = totalTopTimeMs % mEJTopAppTimeChunkSizeMs;
+            if (remainderMs >= SECOND_IN_MILLIS) {
+                // "Round up"
+                numTimeChunks++;
+            }
+            return numTimeChunks;
+        }
+
+        long getPendingReward(final long nowElapsed) {
+            return mEJRewardTopAppMs * calculateTimeChunks(nowElapsed);
+        }
+
         void processEventLocked(@NonNull UsageEvents.Event event) {
             final long nowElapsed = sElapsedRealtimeClock.millis();
             switch (event.getEventType()) {
@@ -2186,21 +2245,16 @@
                     final UsageEvents.Event existingEvent =
                             mActivities.removeReturnOld(event.mInstanceId);
                     if (existingEvent != null && mActivities.size() == 0) {
-                        final long totalTopTimeMs = nowElapsed - mStartTimeElapsed;
-                        int numTimeChunks = (int) (totalTopTimeMs / mEJTopAppTimeChunkSizeMs);
-                        final long remainderMs = totalTopTimeMs % mEJTopAppTimeChunkSizeMs;
-                        if (remainderMs >= SECOND_IN_MILLIS) {
-                            // "Round up"
-                            numTimeChunks++;
-                        }
+                        final long pendingReward = getPendingReward(nowElapsed);
                         if (DEBUG) {
-                            Slog.d(TAG,
-                                    "Crediting " + mPkg + " for " + numTimeChunks + " time chunks");
+                            Slog.d(TAG, "Crediting " + mPkg + " " + pendingReward + "ms"
+                                    + " for " + calculateTimeChunks(nowElapsed) + " time chunks");
                         }
-                        final ShrinkableDebits quota =
-                                getEJQuotaLocked(mPkg.userId, mPkg.packageName);
-                        quota.transactOnDebitsLocked(-mEJRewardTopAppMs * numTimeChunks);
-                        if (maybeUpdateConstraintForPkgLocked(nowElapsed,
+                        final ShrinkableDebits debits =
+                                getEJDebitsLocked(mPkg.userId, mPkg.packageName);
+                        if (transactQuotaLocked(mPkg.userId, mPkg.packageName,
+                                nowElapsed, debits, pendingReward)
+                                && maybeUpdateConstraintForPkgLocked(nowElapsed,
                                 mPkg.userId, mPkg.packageName)) {
                             mStateChangedListener.onControllerStateChanged();
                         }
@@ -2321,7 +2375,7 @@
          */
         @Override
         public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) {
-            mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event);
+            mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event).sendToTarget();
         }
     }
 
@@ -2408,7 +2462,6 @@
     }
 
     private class QcHandler extends Handler {
-        private boolean mIsProcessing;
 
         QcHandler(Looper looper) {
             super(looper);
@@ -2417,8 +2470,6 @@
         @Override
         public void handleMessage(Message msg) {
             synchronized (mLock) {
-                mIsProcessing = true;
-
                 switch (msg.what) {
                     case MSG_REACHED_QUOTA: {
                         Package pkg = (Package) msg.obj;
@@ -2539,6 +2590,10 @@
                         final int userId = msg.arg1;
                         final UsageEvents.Event event = (UsageEvents.Event) msg.obj;
                         final String pkgName = event.getPackageName();
+                        if (DEBUG) {
+                            Slog.d(TAG, "Processing event " + event.getEventType()
+                                    + " for " + string(userId, pkgName));
+                        }
                         switch (event.getEventType()) {
                             case UsageEvents.Event.ACTIVITY_RESUMED:
                             case UsageEvents.Event.ACTIVITY_PAUSED:
@@ -2604,8 +2659,6 @@
                     }
                 }
             }
-
-            mIsProcessing = false;
         }
     }
 
@@ -3883,11 +3936,6 @@
         return mQcConstants;
     }
 
-    @VisibleForTesting
-    boolean isActiveBackgroundProcessing() {
-        return mHandler.mIsProcessing;
-    }
-
     //////////////////////////// DATA DUMP //////////////////////////////
 
     @Override
diff --git a/apex/jobscheduler/service/jni/Android.bp b/apex/jobscheduler/service/jni/Android.bp
index 4bcc165..c630217 100644
--- a/apex/jobscheduler/service/jni/Android.bp
+++ b/apex/jobscheduler/service/jni/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 cc_library_shared {
     name: "libalarm_jni",
 
diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp
index 5b24cfa..9b3399e 100644
--- a/apex/media/service/Android.bp
+++ b/apex/media/service/Android.bp
@@ -11,6 +11,15 @@
 // 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 filegroup {
     name: "service-media-s-sources",
     srcs: [
@@ -38,4 +47,3 @@
         "com.android.media",
     ],
 }
-
diff --git a/api/Android.bp b/api/Android.bp
index 15c1dfc..1fdf177 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -40,6 +40,7 @@
         ":art.module.public.api{.public.api.txt}",
         ":conscrypt.module.public.api{.public.api.txt}",
         ":framework-appsearch{.public.api.txt}",
+        ":framework-connectivity{.public.api.txt}",
         ":framework-graphics{.public.api.txt}",
         ":framework-media{.public.api.txt}",
         ":framework-mediaprovider{.public.api.txt}",
@@ -95,6 +96,7 @@
         ":art.module.public.api{.public.stubs.source}",
         ":conscrypt.module.public.api{.public.stubs.source}",
         ":framework-appsearch{.public.stubs.source}",
+        ":framework-connectivity{.public.stubs.source}",
         ":framework-graphics{.public.stubs.source}",
         ":framework-media{.public.stubs.source}",
         ":framework-mediaprovider{.public.stubs.source}",
@@ -120,6 +122,7 @@
         ":art.module.public.api{.public.removed-api.txt}",
         ":conscrypt.module.public.api{.public.removed-api.txt}",
         ":framework-appsearch{.public.removed-api.txt}",
+        ":framework-connectivity{.public.removed-api.txt}",
         ":framework-graphics{.public.removed-api.txt}",
         ":framework-media{.public.removed-api.txt}",
         ":framework-mediaprovider{.public.removed-api.txt}",
@@ -155,6 +158,7 @@
     srcs: [
         ":android.net.ipsec.ike{.system.api.txt}",
         ":framework-appsearch{.system.api.txt}",
+        ":framework-connectivity{.system.api.txt}",
         ":framework-graphics{.system.api.txt}",
         ":framework-media{.system.api.txt}",
         ":framework-mediaprovider{.system.api.txt}",
@@ -208,6 +212,7 @@
     srcs: [
         ":android.net.ipsec.ike{.system.removed-api.txt}",
         ":framework-appsearch{.system.removed-api.txt}",
+        ":framework-connectivity{.system.removed-api.txt}",
         ":framework-graphics{.system.removed-api.txt}",
         ":framework-media{.system.removed-api.txt}",
         ":framework-mediaprovider{.system.removed-api.txt}",
@@ -243,6 +248,7 @@
     srcs: [
         ":android.net.ipsec.ike{.module-lib.api.txt}",
         ":framework-appsearch{.module-lib.api.txt}",
+        ":framework-connectivity{.module-lib.api.txt}",
         ":framework-graphics{.module-lib.api.txt}",
         ":framework-media{.module-lib.api.txt}",
         ":framework-mediaprovider{.module-lib.api.txt}",
@@ -298,6 +304,7 @@
     srcs: [
         ":android.net.ipsec.ike{.module-lib.removed-api.txt}",
         ":framework-appsearch{.module-lib.removed-api.txt}",
+        ":framework-connectivity{.module-lib.removed-api.txt}",
         ":framework-graphics{.module-lib.removed-api.txt}",
         ":framework-media{.module-lib.removed-api.txt}",
         ":framework-mediaprovider{.module-lib.removed-api.txt}",
@@ -340,3 +347,49 @@
     out: ["combined-removed-dex.txt"],
     cmd: "$(location gen_combined_removed_dex.sh) $(location metalava) $(genDir) $(in) > $(out)",
 }
+
+genrule {
+    name: "services-system-server-current.txt",
+    srcs: [
+        ":service-permission{.system-server.api.txt}",
+        ":non-updatable-system-server-current.txt",
+    ],
+    out: ["system-server-current.txt"],
+    tools: ["metalava"],
+    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dists: [
+        {
+            targets: ["droidcore"],
+            dir: "api",
+            dest: "system-server-current.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/system-server/api",
+            dest: "merge-android.txt",
+        },
+    ],
+}
+
+genrule {
+    name: "services-system-server-removed.txt",
+    srcs: [
+        ":service-permission{.system-server.removed-api.txt}",
+        ":non-updatable-system-server-removed.txt",
+    ],
+    out: ["system-server-removed.txt"],
+    tools: ["metalava"],
+    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dists: [
+        {
+            targets: ["droidcore"],
+            dir: "api",
+            dest: "system-server-removed.txt",
+        },
+        {
+            targets: ["sdk", "win_sdk"],
+            dir: "apistubs/android/system-server/api",
+            dest: "merge-removed.txt",
+        },
+    ],
+}
diff --git a/cmds/abx/Android.bp b/cmds/abx/Android.bp
index 333aced..50a0b75 100644
--- a/cmds/abx/Android.bp
+++ b/cmds/abx/Android.bp
@@ -1,4 +1,21 @@
 
+package {
+    default_applicable_licenses: ["frameworks_base_cmds_abx_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "frameworks_base_cmds_abx_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 java_binary {
     name: "abx",
     wrapper: "abx",
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 4b7eda0..ed717c4 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.UserIdInt;
 import android.app.backup.BackupManager;
-import android.app.backup.BackupManager.OperationType;
 import android.app.backup.BackupManagerMonitor;
 import android.app.backup.BackupProgress;
 import android.app.backup.BackupTransport;
@@ -667,7 +666,7 @@
 
         // The rest of the 'list' options work with a restore session on the current transport
         try {
-            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP);
+            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
             if (mRestore == null) {
                 System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
                 return;
@@ -822,7 +821,7 @@
 
         try {
             boolean didRestore = false;
-            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP);
+            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
             if (mRestore == null) {
                 System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
                 return;
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 422c2be..2cda57d 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -48,6 +48,7 @@
 static struct {
     jmethodID onDeviceOpen;
     jmethodID onDeviceGetReport;
+    jmethodID onDeviceSetReport;
     jmethodID onDeviceOutput;
     jmethodID onDeviceError;
 } gDeviceCallbackClassInfo;
@@ -113,10 +114,18 @@
     checkAndClearException(env, "onDeviceGetReport");
 }
 
-void DeviceCallback::onDeviceOutput(uint8_t eventId, uint8_t rType,
+void DeviceCallback::onDeviceSetReport(uint8_t rType,
                                     const std::vector<uint8_t>& data) {
     JNIEnv* env = getJNIEnv();
-    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, eventId, rType,
+    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceSetReport, rType,
+                        toJbyteArray(env, data).get());
+    checkAndClearException(env, "onDeviceSetReport");
+}
+
+void DeviceCallback::onDeviceOutput(uint8_t rType,
+                                    const std::vector<uint8_t>& data) {
+    JNIEnv* env = getJNIEnv();
+    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, rType,
                         toJbyteArray(env, data).get());
     checkAndClearException(env, "onDeviceOutput");
 }
@@ -262,7 +271,7 @@
                 ALOGD("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id,
                       set_report.rnum, toString(data).c_str());
             }
-            mDeviceCallback->onDeviceOutput(UHID_SET_REPORT, set_report.rtype, data);
+            mDeviceCallback->onDeviceSetReport(set_report.rtype, data);
             break;
         }
         case UHID_OUTPUT: {
@@ -271,7 +280,7 @@
             if (DEBUG_OUTPUT) {
                 ALOGD("UHID_OUTPUT rtype=%" PRIu8 " data=%s", output.rtype, toString(data).c_str());
             }
-            mDeviceCallback->onDeviceOutput(UHID_OUTPUT, output.rtype, data);
+            mDeviceCallback->onDeviceOutput(output.rtype, data);
             break;
         }
         default: {
@@ -366,8 +375,10 @@
             env->GetMethodID(clazz, "onDeviceOpen", "()V");
     uhid::gDeviceCallbackClassInfo.onDeviceGetReport =
             env->GetMethodID(clazz, "onDeviceGetReport", "(II)V");
+    uhid::gDeviceCallbackClassInfo.onDeviceSetReport =
+            env->GetMethodID(clazz, "onDeviceSetReport", "(B[B)V");
     uhid::gDeviceCallbackClassInfo.onDeviceOutput =
-            env->GetMethodID(clazz, "onDeviceOutput", "(BB[B)V");
+            env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V");
     uhid::gDeviceCallbackClassInfo.onDeviceError =
             env->GetMethodID(clazz, "onDeviceError", "()V");
     if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL ||
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index bb73132..d10a9aa 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -31,7 +31,8 @@
 
     void onDeviceOpen();
     void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
-    void onDeviceOutput(uint8_t eventId, uint8_t rType, const std::vector<uint8_t>& data);
+    void onDeviceSetReport(uint8_t rType, const std::vector<uint8_t>& data);
+    void onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data);
     void onDeviceError();
 
 private:
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 37d0b1c..95b1e9a 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -199,8 +199,8 @@
             mHandler.sendMessageAtTime(msg, mTimeToSend);
         }
 
-        // native callback
-        public void onDeviceOutput(byte eventId, byte rtype, byte[] data) {
+        // Send out the report to HID command output
+        private void sendReportOutput(byte eventId, byte rtype, byte[] data) {
             JSONObject json = new JSONObject();
             try {
                 json.put("eventId", eventId);
@@ -221,6 +221,18 @@
                 throw new RuntimeException(e);
             }
 
+        }
+
+        // native callback
+        public void onDeviceSetReport(byte rtype, byte[] data) {
+            // We don't need to reply for the SET_REPORT but just send it to HID output for test
+            // verification.
+            sendReportOutput(UHID_EVENT_TYPE_SET_REPORT, rtype, data);
+        }
+
+        // native callback
+        public void onDeviceOutput(byte rtype, byte[] data) {
+            sendReportOutput(UHID_EVENT_TYPE_UHID_OUTPUT, rtype, data);
             if (mOutputs == null) {
                 Log.e(TAG, "Received OUTPUT request, but 'outputs' section is not found");
                 return;
diff --git a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
index a0361d0..ca9df39 100644
--- a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
+++ b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
@@ -182,6 +182,8 @@
                 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
             } else if (opt.equals("--rc") || opt.equals("--require-charging")) {
                 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, true);
+            } else if (opt.equals("--ej") || opt.equals("--schedule-as-ej")) {
+                mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true);
             } else if (opt.equals("-e") || opt.equals("--es") || opt.equals("--extra-string")) {
                 final String key = nextArgRequired();
                 final String value = nextArgRequired();
diff --git a/cmds/uinput/Android.bp b/cmds/uinput/Android.bp
index 0d7fed2..260cfc7 100644
--- a/cmds/uinput/Android.bp
+++ b/cmds/uinput/Android.bp
@@ -1,6 +1,23 @@
 // Copyright 2020 The Android Open Source Project
 //
 
+package {
+    default_applicable_licenses: ["frameworks_base_cmds_uinput_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "frameworks_base_cmds_uinput_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 java_binary {
     name: "uinput",
     wrapper: "uinput",
@@ -15,4 +32,4 @@
     srcs: [
         "src/com/android/commands/uinput/InputAbsInfo.aidl",
     ],
-}
\ No newline at end of file
+}
diff --git a/cmds/uinput/jni/Android.bp b/cmds/uinput/jni/Android.bp
index 199bbbd..c56adc3 100644
--- a/cmds/uinput/jni/Android.bp
+++ b/cmds/uinput/jni/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_cmds_uinput_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_cmds_uinput_license"],
+}
+
 cc_library_shared {
     name: "libuinputcommand_jni",
 
diff --git a/core/api/current.txt b/core/api/current.txt
index ef18999..5f6c9b6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -507,6 +507,7 @@
     field public static final int dashGap = 16843175; // 0x10101a7
     field public static final int dashWidth = 16843174; // 0x10101a6
     field public static final int data = 16842798; // 0x101002e
+    field public static final int dataExtractionRules = 16844350; // 0x101063e
     field public static final int datePickerDialogTheme = 16843948; // 0x10104ac
     field public static final int datePickerMode = 16843955; // 0x10104b3
     field public static final int datePickerStyle = 16843612; // 0x101035c
@@ -5610,6 +5611,7 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final String EXTRA_ANSWER_COLOR = "android.answerColor";
     field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
     field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
     field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
@@ -5621,6 +5623,7 @@
     field public static final String EXTRA_COLORIZED = "android.colorized";
     field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
     field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final String EXTRA_DECLINE_COLOR = "android.declineColor";
     field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
     field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
     field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
@@ -5908,6 +5911,8 @@
     method @NonNull public static android.app.Notification.CallStyle forIncomingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
     method @NonNull public static android.app.Notification.CallStyle forOngoingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent);
     method @NonNull public static android.app.Notification.CallStyle forScreeningCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
+    method @NonNull public android.app.Notification.CallStyle setAnswerButtonColorHint(@ColorInt int);
+    method @NonNull public android.app.Notification.CallStyle setDeclineButtonColorHint(@ColorInt int);
     method @NonNull public android.app.Notification.CallStyle setVerificationIcon(@Nullable android.graphics.drawable.Icon);
     method @NonNull public android.app.Notification.CallStyle setVerificationText(@Nullable CharSequence);
   }
@@ -6265,14 +6270,14 @@
     method public static android.app.PendingIntent getActivities(android.content.Context, int, @NonNull android.content.Intent[], int, @Nullable android.os.Bundle);
     method public static android.app.PendingIntent getActivity(android.content.Context, int, android.content.Intent, int);
     method public static android.app.PendingIntent getActivity(android.content.Context, int, @NonNull android.content.Intent, int, @Nullable android.os.Bundle);
-    method public static android.app.PendingIntent getBroadcast(android.content.Context, int, android.content.Intent, int);
-    method @Nullable public String getCreatorPackage();
+    method public static android.app.PendingIntent getBroadcast(android.content.Context, int, @NonNull android.content.Intent, int);
+    method @NonNull public String getCreatorPackage();
     method public int getCreatorUid();
-    method @Nullable public android.os.UserHandle getCreatorUserHandle();
+    method @NonNull public android.os.UserHandle getCreatorUserHandle();
     method public static android.app.PendingIntent getForegroundService(android.content.Context, int, @NonNull android.content.Intent, int);
-    method public android.content.IntentSender getIntentSender();
+    method @NonNull public android.content.IntentSender getIntentSender();
     method public static android.app.PendingIntent getService(android.content.Context, int, @NonNull android.content.Intent, int);
-    method @Deprecated public String getTargetPackage();
+    method @Deprecated @NonNull public String getTargetPackage();
     method public boolean isActivity();
     method public boolean isBroadcast();
     method public boolean isForegroundService();
@@ -8371,7 +8376,9 @@
     method protected android.view.View getDefaultView();
     method protected android.view.View getErrorView();
     method protected void prepareView(android.view.View);
+    method public void resetColorResources();
     method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
+    method public void setColorResources(@NonNull android.util.SparseIntArray);
     method public void setCurrentSize(@NonNull android.graphics.PointF);
     method public void setExecutor(java.util.concurrent.Executor);
     method public void setOnLightBackground(boolean);
@@ -8452,7 +8459,7 @@
     method public int describeContents();
     method public final android.os.UserHandle getProfile();
     method @NonNull public android.content.pm.ActivityInfo getProviderInfo();
-    method @Nullable public final String loadDescription(@NonNull android.content.Context);
+    method @Nullable public final CharSequence loadDescription(@NonNull android.content.Context);
     method public final android.graphics.drawable.Drawable loadIcon(@NonNull android.content.Context, int);
     method public final String loadLabel(android.content.pm.PackageManager);
     method public final android.graphics.drawable.Drawable loadPreviewImage(@NonNull android.content.Context, int);
@@ -8470,7 +8477,7 @@
     field public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; // 0x1
     field public int autoAdvanceViewId;
     field public android.content.ComponentName configure;
-    field @IdRes public int descriptionResource;
+    field @IdRes public int descriptionRes;
     field public int icon;
     field public int initialKeyguardLayout;
     field public int initialLayout;
@@ -9734,7 +9741,7 @@
   public interface DeviceFilter<D extends android.os.Parcelable> extends android.os.Parcelable {
   }
 
-  public class DeviceNotAssociatedException extends java.lang.Exception {
+  public class DeviceNotAssociatedException extends java.lang.RuntimeException {
   }
 
   public final class WifiDeviceFilter implements android.companion.DeviceFilter<android.net.wifi.ScanResult> {
@@ -10238,6 +10245,7 @@
     field public static final String SYNC_EXTRAS_MANUAL = "force";
     field public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
     field public static final String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging";
+    field public static final String SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB = "schedule_as_expedited_job";
     field public static final String SYNC_EXTRAS_UPLOAD = "upload";
     field public static final int SYNC_OBSERVER_TYPE_ACTIVE = 4; // 0x4
     field public static final int SYNC_OBSERVER_TYPE_PENDING = 2; // 0x2
@@ -10381,7 +10389,7 @@
     method public abstract void grantUriPermission(String, android.net.Uri, int);
     method public abstract boolean isDeviceProtectedStorage();
     method public boolean isRestricted();
-    method public static boolean isUiContext(@NonNull android.content.Context);
+    method public boolean isUiContext();
     method public abstract boolean moveDatabaseFrom(android.content.Context, String);
     method public abstract boolean moveSharedPreferencesFrom(android.content.Context, String);
     method @NonNull public final android.content.res.TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[]);
@@ -11560,6 +11568,7 @@
     method public android.content.SyncRequest.Builder setManual(boolean);
     method public android.content.SyncRequest.Builder setNoRetry(boolean);
     method public android.content.SyncRequest.Builder setRequiresCharging(boolean);
+    method @NonNull public android.content.SyncRequest.Builder setScheduleAsExpeditedJob(boolean);
     method public android.content.SyncRequest.Builder setSyncAdapter(android.accounts.Account, String);
     method public android.content.SyncRequest.Builder syncOnce();
     method public android.content.SyncRequest.Builder syncPeriodic(long, long);
@@ -18559,23 +18568,6 @@
 
 package android.hardware.display {
 
-  public final class DeviceProductInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getConnectionToSinkType();
-    method public int getManufactureWeek();
-    method public int getManufactureYear();
-    method @NonNull public String getManufacturerPnpId();
-    method public int getModelYear();
-    method @Nullable public String getName();
-    method @NonNull public String getProductId();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final int CONNECTION_TO_SINK_BUILT_IN = 1; // 0x1
-    field public static final int CONNECTION_TO_SINK_DIRECT = 2; // 0x2
-    field public static final int CONNECTION_TO_SINK_TRANSITIVE = 3; // 0x3
-    field public static final int CONNECTION_TO_SINK_UNKNOWN = 0; // 0x0
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.DeviceProductInfo> CREATOR;
-  }
-
   public final class DisplayManager {
     method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, int, int, int, @Nullable android.view.Surface, int);
     method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, int, int, int, @Nullable android.view.Surface, int, @Nullable android.hardware.display.VirtualDisplay.Callback, @Nullable android.os.Handler);
@@ -18904,6 +18896,7 @@
   public abstract class AbstractInputMethodService extends android.app.Service implements android.view.KeyEvent.Callback {
     ctor public AbstractInputMethodService();
     method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
+    method public final boolean isUiContext();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface();
     method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
@@ -19669,6 +19662,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void addProximityAlert(double, double, float, long, @NonNull android.app.PendingIntent);
     method public void addTestProvider(@NonNull String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
     method public void addTestProvider(@NonNull String, @NonNull android.location.provider.ProviderProperties);
+    method public void addTestProvider(@NonNull String, @NonNull android.location.provider.ProviderProperties, @NonNull java.util.Set<java.lang.String>);
     method @Deprecated public void clearTestProviderEnabled(@NonNull String);
     method @Deprecated public void clearTestProviderLocation(@NonNull String);
     method @Deprecated public void clearTestProviderStatus(@NonNull String);
@@ -22157,6 +22151,14 @@
     field public static final String KEY_TILE_HEIGHT = "tile-height";
     field public static final String KEY_TILE_WIDTH = "tile-width";
     field public static final String KEY_TRACK_ID = "track-id";
+    field public static final String KEY_VIDEO_QP_B_MAX = "video-qp-b-max";
+    field public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min";
+    field public static final String KEY_VIDEO_QP_I_MAX = "video-qp-i-max";
+    field public static final String KEY_VIDEO_QP_I_MIN = "video-qp-i-min";
+    field public static final String KEY_VIDEO_QP_MAX = "video-qp-max";
+    field public static final String KEY_VIDEO_QP_MIN = "video-qp-min";
+    field public static final String KEY_VIDEO_QP_P_MAX = "video-qp-p-max";
+    field public static final String KEY_VIDEO_QP_P_MIN = "video-qp-p-min";
     field public static final String KEY_WIDTH = "width";
     field public static final String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
     field public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3";
@@ -25825,161 +25827,6 @@
 
 package android.net {
 
-  public class CaptivePortal implements android.os.Parcelable {
-    method public int describeContents();
-    method public void ignoreNetwork();
-    method public void reportCaptivePortalDismissed();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR;
-  }
-
-  public class ConnectivityDiagnosticsManager {
-    method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
-    method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
-  }
-
-  public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
-    ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback();
-    method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
-    method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport);
-    method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
-  }
-
-  public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable {
-    ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
-    method public int describeContents();
-    method @NonNull public android.os.PersistableBundle getAdditionalInfo();
-    method @NonNull public android.net.LinkProperties getLinkProperties();
-    method @NonNull public android.net.Network getNetwork();
-    method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
-    method public long getReportTimestamp();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR;
-    field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted";
-    field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded";
-    field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
-    field public static final int NETWORK_PROBE_DNS = 4; // 0x4
-    field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20
-    field public static final int NETWORK_PROBE_HTTP = 8; // 0x8
-    field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10
-    field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40
-    field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0
-    field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2
-    field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3
-    field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1
-  }
-
-  public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
-    ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
-    method public int describeContents();
-    method public int getDetectionMethod();
-    method @NonNull public android.net.LinkProperties getLinkProperties();
-    method @NonNull public android.net.Network getNetwork();
-    method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
-    method public long getReportTimestamp();
-    method @NonNull public android.os.PersistableBundle getStallDetails();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR;
-    field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
-    field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
-    field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
-    field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis";
-    field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
-  }
-
-  public class ConnectivityManager {
-    method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
-    method public boolean bindProcessToNetwork(@Nullable android.net.Network);
-    method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
-    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork();
-    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo();
-    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo();
-    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks();
-    method @Deprecated public boolean getBackgroundDataSetting();
-    method @Nullable public android.net.Network getBoundNetworkForProcess();
-    method public int getConnectionOwnerUid(int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress);
-    method @Nullable public android.net.ProxyInfo getDefaultProxy();
-    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.LinkProperties getLinkProperties(@Nullable android.net.Network);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getMultipathPreference(@Nullable android.net.Network);
-    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkCapabilities getNetworkCapabilities(@Nullable android.net.Network);
-    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(int);
-    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(@Nullable android.net.Network);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getNetworkPreference();
-    method @Nullable public byte[] getNetworkWatchlistConfigHash();
-    method @Deprecated @Nullable public static android.net.Network getProcessDefaultNetwork();
-    method public int getRestrictBackgroundStatus();
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered();
-    method public boolean isDefaultNetworkActive();
-    method @Deprecated public static boolean isNetworkTypeValid(int);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent);
-    method public void releaseNetworkRequest(@NonNull android.app.PendingIntent);
-    method public void removeDefaultNetworkActiveListener(@NonNull android.net.ConnectivityManager.OnNetworkActiveListener);
-    method @Deprecated public void reportBadNetwork(@Nullable android.net.Network);
-    method public void reportNetworkConnectivity(@Nullable android.net.Network, boolean);
-    method public boolean requestBandwidthUpdate(@NonNull android.net.Network);
-    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
-    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
-    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int);
-    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler, int);
-    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent);
-    method @Deprecated public void setNetworkPreference(int);
-    method @Deprecated public static boolean setProcessDefaultNetwork(@Nullable android.net.Network);
-    method public void unregisterNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
-    method public void unregisterNetworkCallback(@NonNull android.app.PendingIntent);
-    field @Deprecated public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
-    field public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
-    field public static final String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED";
-    field @Deprecated public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
-    field @Deprecated public static final int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
-    field public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
-    field public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
-    field @Deprecated public static final String EXTRA_EXTRA_INFO = "extraInfo";
-    field @Deprecated public static final String EXTRA_IS_FAILOVER = "isFailover";
-    field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
-    field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo";
-    field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
-    field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType";
-    field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity";
-    field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
-    field public static final String EXTRA_REASON = "reason";
-    field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1
-    field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4
-    field public static final int MULTIPATH_PREFERENCE_RELIABILITY = 2; // 0x2
-    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
-    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
-    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
-    field @Deprecated public static final int TYPE_BLUETOOTH = 7; // 0x7
-    field @Deprecated public static final int TYPE_DUMMY = 8; // 0x8
-    field @Deprecated public static final int TYPE_ETHERNET = 9; // 0x9
-    field @Deprecated public static final int TYPE_MOBILE = 0; // 0x0
-    field @Deprecated public static final int TYPE_MOBILE_DUN = 4; // 0x4
-    field @Deprecated public static final int TYPE_MOBILE_HIPRI = 5; // 0x5
-    field @Deprecated public static final int TYPE_MOBILE_MMS = 2; // 0x2
-    field @Deprecated public static final int TYPE_MOBILE_SUPL = 3; // 0x3
-    field @Deprecated public static final int TYPE_VPN = 17; // 0x11
-    field @Deprecated public static final int TYPE_WIFI = 1; // 0x1
-    field @Deprecated public static final int TYPE_WIMAX = 6; // 0x6
-  }
-
-  public static class ConnectivityManager.NetworkCallback {
-    ctor public ConnectivityManager.NetworkCallback();
-    method public void onAvailable(@NonNull android.net.Network);
-    method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean);
-    method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities);
-    method public void onLinkPropertiesChanged(@NonNull android.net.Network, @NonNull android.net.LinkProperties);
-    method public void onLosing(@NonNull android.net.Network, int);
-    method public void onLost(@NonNull android.net.Network);
-    method public void onUnavailable();
-  }
-
-  public static interface ConnectivityManager.OnNetworkActiveListener {
-    method public void onNetworkActive();
-  }
-
   public class Credentials {
     ctor public Credentials(int, int, int);
     method public int getGid();
@@ -25987,46 +25834,6 @@
     method public int getUid();
   }
 
-  public class DhcpInfo implements android.os.Parcelable {
-    ctor public DhcpInfo();
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpInfo> CREATOR;
-    field public int dns1;
-    field public int dns2;
-    field public int gateway;
-    field public int ipAddress;
-    field public int leaseDuration;
-    field public int netmask;
-    field public int serverAddress;
-  }
-
-  public final class DnsResolver {
-    method @NonNull public static android.net.DnsResolver getInstance();
-    method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>);
-    method public void query(@Nullable android.net.Network, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>);
-    method public void rawQuery(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>);
-    method public void rawQuery(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>);
-    field public static final int CLASS_IN = 1; // 0x1
-    field public static final int ERROR_PARSE = 0; // 0x0
-    field public static final int ERROR_SYSTEM = 1; // 0x1
-    field public static final int FLAG_EMPTY = 0; // 0x0
-    field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4
-    field public static final int FLAG_NO_CACHE_STORE = 2; // 0x2
-    field public static final int FLAG_NO_RETRY = 1; // 0x1
-    field public static final int TYPE_A = 1; // 0x1
-    field public static final int TYPE_AAAA = 28; // 0x1c
-  }
-
-  public static interface DnsResolver.Callback<T> {
-    method public void onAnswer(@NonNull T, int);
-    method public void onError(@NonNull android.net.DnsResolver.DnsException);
-  }
-
-  public static class DnsResolver.DnsException extends java.lang.Exception {
-    field public final int code;
-  }
-
   public final class Ikev2VpnProfile extends android.net.PlatformVpnProfile {
     method @NonNull public java.util.List<java.lang.String> getAllowedAlgorithms();
     method public int getMaxMtu();
@@ -26056,21 +25863,6 @@
     method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo);
   }
 
-  public class InetAddresses {
-    method public static boolean isNumericAddress(@NonNull String);
-    method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String);
-  }
-
-  public final class IpPrefix implements android.os.Parcelable {
-    method public boolean contains(@NonNull java.net.InetAddress);
-    method public int describeContents();
-    method @NonNull public java.net.InetAddress getAddress();
-    method @IntRange(from=0, to=128) public int getPrefixLength();
-    method @NonNull public byte[] getRawAddress();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
-  }
-
   public final class IpSecAlgorithm implements android.os.Parcelable {
     ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[]);
     ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[], int);
@@ -26139,45 +25931,6 @@
     method @NonNull public android.net.IpSecTransform.Builder setIpv4Encapsulation(@NonNull android.net.IpSecManager.UdpEncapsulationSocket, int);
   }
 
-  public class LinkAddress implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.net.InetAddress getAddress();
-    method public int getFlags();
-    method @IntRange(from=0, to=128) public int getPrefixLength();
-    method public int getScope();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkAddress> CREATOR;
-  }
-
-  public final class LinkProperties implements android.os.Parcelable {
-    ctor public LinkProperties();
-    method public boolean addRoute(@NonNull android.net.RouteInfo);
-    method public void clear();
-    method public int describeContents();
-    method @Nullable public java.net.Inet4Address getDhcpServerAddress();
-    method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
-    method @Nullable public String getDomains();
-    method @Nullable public android.net.ProxyInfo getHttpProxy();
-    method @Nullable public String getInterfaceName();
-    method @NonNull public java.util.List<android.net.LinkAddress> getLinkAddresses();
-    method public int getMtu();
-    method @Nullable public android.net.IpPrefix getNat64Prefix();
-    method @Nullable public String getPrivateDnsServerName();
-    method @NonNull public java.util.List<android.net.RouteInfo> getRoutes();
-    method public boolean isPrivateDnsActive();
-    method public boolean isWakeOnLanSupported();
-    method public void setDhcpServerAddress(@Nullable java.net.Inet4Address);
-    method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
-    method public void setDomains(@Nullable String);
-    method public void setHttpProxy(@Nullable android.net.ProxyInfo);
-    method public void setInterfaceName(@Nullable String);
-    method public void setLinkAddresses(@NonNull java.util.Collection<android.net.LinkAddress>);
-    method public void setMtu(int);
-    method public void setNat64Prefix(@Nullable android.net.IpPrefix);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
-  }
-
   public class LocalServerSocket implements java.io.Closeable {
     ctor public LocalServerSocket(String) throws java.io.IOException;
     ctor public LocalServerSocket(java.io.FileDescriptor) throws java.io.IOException;
@@ -26233,24 +25986,6 @@
     enum_constant public static final android.net.LocalSocketAddress.Namespace RESERVED;
   }
 
-  public final class MacAddress implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public static android.net.MacAddress fromBytes(@NonNull byte[]);
-    method @NonNull public static android.net.MacAddress fromString(@NonNull String);
-    method public int getAddressType();
-    method @Nullable public java.net.Inet6Address getLinkLocalIpv6FromEui48Mac();
-    method public boolean isLocallyAssigned();
-    method public boolean matches(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress);
-    method @NonNull public byte[] toByteArray();
-    method @NonNull public String toOuiString();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.net.MacAddress BROADCAST_ADDRESS;
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR;
-    field public static final int TYPE_BROADCAST = 3; // 0x3
-    field public static final int TYPE_MULTICAST = 2; // 0x2
-    field public static final int TYPE_UNICAST = 1; // 0x1
-  }
-
   public class MailTo {
     method public String getBody();
     method public String getCc();
@@ -26262,138 +25997,6 @@
     field public static final String MAILTO_SCHEME = "mailto:";
   }
 
-  public class Network implements android.os.Parcelable {
-    method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException;
-    method public void bindSocket(java.net.Socket) throws java.io.IOException;
-    method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException;
-    method public int describeContents();
-    method public static android.net.Network fromNetworkHandle(long);
-    method public java.net.InetAddress[] getAllByName(String) throws java.net.UnknownHostException;
-    method public java.net.InetAddress getByName(String) throws java.net.UnknownHostException;
-    method public long getNetworkHandle();
-    method public javax.net.SocketFactory getSocketFactory();
-    method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
-    method public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException;
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.Network> CREATOR;
-  }
-
-  public final class NetworkCapabilities implements android.os.Parcelable {
-    ctor public NetworkCapabilities();
-    ctor public NetworkCapabilities(android.net.NetworkCapabilities);
-    method public int describeContents();
-    method public int getLinkDownstreamBandwidthKbps();
-    method public int getLinkUpstreamBandwidthKbps();
-    method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
-    method public int getOwnerUid();
-    method public int getSignalStrength();
-    method @Nullable public android.net.TransportInfo getTransportInfo();
-    method public boolean hasCapability(int);
-    method public boolean hasTransport(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR;
-    field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11
-    field public static final int NET_CAPABILITY_CBS = 5; // 0x5
-    field public static final int NET_CAPABILITY_DUN = 2; // 0x2
-    field public static final int NET_CAPABILITY_EIMS = 10; // 0xa
-    field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
-    field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
-    field public static final int NET_CAPABILITY_IA = 7; // 0x7
-    field public static final int NET_CAPABILITY_IMS = 4; // 0x4
-    field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
-    field public static final int NET_CAPABILITY_MCX = 23; // 0x17
-    field public static final int NET_CAPABILITY_MMS = 0; // 0x0
-    field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14
-    field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
-    field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
-    field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
-    field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15
-    field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
-    field public static final int NET_CAPABILITY_RCS = 8; // 0x8
-    field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
-    field public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; // 0x19
-    field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe
-    field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10
-    field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6
-    field public static final int NET_CAPABILITY_XCAP = 9; // 0x9
-    field public static final int SIGNAL_STRENGTH_UNSPECIFIED = -2147483648; // 0x80000000
-    field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2
-    field public static final int TRANSPORT_CELLULAR = 0; // 0x0
-    field public static final int TRANSPORT_ETHERNET = 3; // 0x3
-    field public static final int TRANSPORT_LOWPAN = 6; // 0x6
-    field public static final int TRANSPORT_VPN = 4; // 0x4
-    field public static final int TRANSPORT_WIFI = 1; // 0x1
-    field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
-  }
-
-  @Deprecated public class NetworkInfo implements android.os.Parcelable {
-    ctor @Deprecated public NetworkInfo(int, int, @Nullable String, @Nullable String);
-    method @Deprecated public int describeContents();
-    method @Deprecated @NonNull public android.net.NetworkInfo.DetailedState getDetailedState();
-    method @Deprecated public String getExtraInfo();
-    method @Deprecated public String getReason();
-    method @Deprecated public android.net.NetworkInfo.State getState();
-    method @Deprecated public int getSubtype();
-    method @Deprecated public String getSubtypeName();
-    method @Deprecated public int getType();
-    method @Deprecated public String getTypeName();
-    method @Deprecated public boolean isAvailable();
-    method @Deprecated public boolean isConnected();
-    method @Deprecated public boolean isConnectedOrConnecting();
-    method @Deprecated public boolean isFailover();
-    method @Deprecated public boolean isRoaming();
-    method @Deprecated public void setDetailedState(@NonNull android.net.NetworkInfo.DetailedState, @Nullable String, @Nullable String);
-    method @Deprecated public void writeToParcel(android.os.Parcel, int);
-    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR;
-  }
-
-  @Deprecated public enum NetworkInfo.DetailedState {
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState AUTHENTICATING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState BLOCKED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CAPTIVE_PORTAL_CHECK;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState FAILED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState IDLE;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SCANNING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SUSPENDED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK;
-  }
-
-  @Deprecated public enum NetworkInfo.State {
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State SUSPENDED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State UNKNOWN;
-  }
-
-  public class NetworkRequest implements android.os.Parcelable {
-    method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkCapabilities);
-    method public int describeContents();
-    method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
-    method public boolean hasCapability(int);
-    method public boolean hasTransport(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR;
-  }
-
-  public static class NetworkRequest.Builder {
-    ctor public NetworkRequest.Builder();
-    method public android.net.NetworkRequest.Builder addCapability(int);
-    method public android.net.NetworkRequest.Builder addTransportType(int);
-    method public android.net.NetworkRequest build();
-    method @NonNull public android.net.NetworkRequest.Builder clearCapabilities();
-    method public android.net.NetworkRequest.Builder removeCapability(int);
-    method public android.net.NetworkRequest.Builder removeTransportType(int);
-    method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
-    method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
-  }
-
   public abstract class NetworkSpecifier {
     ctor public NetworkSpecifier();
   }
@@ -26410,44 +26013,6 @@
     field public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; // 0x6
   }
 
-  public final class Proxy {
-    ctor public Proxy();
-    method @Deprecated public static String getDefaultHost();
-    method @Deprecated public static int getDefaultPort();
-    method @Deprecated public static String getHost(android.content.Context);
-    method @Deprecated public static int getPort(android.content.Context);
-    field @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
-    field public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
-  }
-
-  public class ProxyInfo implements android.os.Parcelable {
-    ctor public ProxyInfo(@Nullable android.net.ProxyInfo);
-    method public static android.net.ProxyInfo buildDirectProxy(String, int);
-    method public static android.net.ProxyInfo buildDirectProxy(String, int, java.util.List<java.lang.String>);
-    method public static android.net.ProxyInfo buildPacProxy(android.net.Uri);
-    method @NonNull public static android.net.ProxyInfo buildPacProxy(@NonNull android.net.Uri, int);
-    method public int describeContents();
-    method public String[] getExclusionList();
-    method public String getHost();
-    method public android.net.Uri getPacFileUrl();
-    method public int getPort();
-    method public boolean isValid();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.ProxyInfo> CREATOR;
-  }
-
-  public final class RouteInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public android.net.IpPrefix getDestination();
-    method @Nullable public java.net.InetAddress getGateway();
-    method @Nullable public String getInterface();
-    method public boolean hasGateway();
-    method public boolean isDefaultRoute();
-    method public boolean matches(java.net.InetAddress);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.RouteInfo> CREATOR;
-  }
-
   @Deprecated public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
     ctor @Deprecated public SSLCertificateSocketFactory(int);
     method @Deprecated public java.net.Socket createSocket(java.net.Socket, String, int, boolean) throws java.io.IOException;
@@ -26473,30 +26038,6 @@
     ctor public SSLSessionCache(android.content.Context);
   }
 
-  public abstract class SocketKeepalive implements java.lang.AutoCloseable {
-    method public final void close();
-    method public final void start(@IntRange(from=0xa, to=0xe10) int);
-    method public final void stop();
-    field public static final int ERROR_HARDWARE_ERROR = -31; // 0xffffffe1
-    field public static final int ERROR_INSUFFICIENT_RESOURCES = -32; // 0xffffffe0
-    field public static final int ERROR_INVALID_INTERVAL = -24; // 0xffffffe8
-    field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
-    field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
-    field public static final int ERROR_INVALID_NETWORK = -20; // 0xffffffec
-    field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
-    field public static final int ERROR_INVALID_SOCKET = -25; // 0xffffffe7
-    field public static final int ERROR_SOCKET_NOT_IDLE = -26; // 0xffffffe6
-    field public static final int ERROR_UNSUPPORTED = -30; // 0xffffffe2
-  }
-
-  public static class SocketKeepalive.Callback {
-    ctor public SocketKeepalive.Callback();
-    method public void onDataReceived();
-    method public void onError(int);
-    method public void onStarted();
-    method public void onStopped();
-  }
-
   public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
     method public int describeContents();
     method public int getSubscriptionId();
@@ -26554,9 +26095,6 @@
     field public static final int UNSUPPORTED = -1; // 0xffffffff
   }
 
-  public interface TransportInfo {
-  }
-
   public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
     method public abstract android.net.Uri.Builder buildUpon();
     method public int compareTo(android.net.Uri);
@@ -31929,10 +31467,10 @@
     method public static android.content.Intent createUserCreationIntent(@Nullable String, @Nullable String, @Nullable String, @Nullable android.os.PersistableBundle);
     method @WorkerThread public android.os.Bundle getApplicationRestrictions(String);
     method public long getSerialNumberForUser(android.os.UserHandle);
-    method @RequiresPermission("android.permission.MANAGE_USERS") public int getUserCount();
+    method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS"}) public int getUserCount();
     method public long getUserCreationTime(android.os.UserHandle);
     method public android.os.UserHandle getUserForSerialNumber(long);
-    method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}, conditional=true) public String getUserName();
+    method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, "android.permission.CREATE_USERS"}, conditional=true) public String getUserName();
     method public java.util.List<android.os.UserHandle> getUserProfiles();
     method public android.os.Bundle getUserRestrictions();
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
@@ -35126,6 +34664,7 @@
     field public static final String ACTION_QUICK_ACCESS_WALLET_SETTINGS = "android.settings.QUICK_ACCESS_WALLET_SETTINGS";
     field public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS";
     field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
+    field public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM = "android.settings.REQUEST_SCHEDULE_EXACT_ALARM";
     field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE";
     field public static final String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS";
     field public static final String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS";
@@ -37170,8 +36709,10 @@
     method @NonNull public static android.content.Intent createInstallIntent();
     method @NonNull public static android.content.Intent createManageCredentialsIntent(@NonNull android.security.AppUriAuthenticationPolicy);
     method @Nullable @WorkerThread public static java.security.cert.X509Certificate[] getCertificateChain(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException;
+    method @NonNull public static android.security.AppUriAuthenticationPolicy getCredentialManagementAppPolicy(@NonNull android.content.Context) throws java.lang.SecurityException;
     method @Nullable @WorkerThread public static java.security.PrivateKey getPrivateKey(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException;
     method @Deprecated public static boolean isBoundKeyAlgorithm(@NonNull String);
+    method public static boolean isCredentialManagementApp(@NonNull android.content.Context);
     method public static boolean isKeyAlgorithmSupported(@NonNull String);
     field public static final String ACTION_KEYCHAIN_CHANGED = "android.security.action.KEYCHAIN_CHANGED";
     field public static final String ACTION_KEY_ACCESS_CHANGED = "android.security.action.KEY_ACCESS_CHANGED";
@@ -40878,6 +40419,7 @@
   }
 
   public static final class CarrierConfigManager.Apn {
+    field @Deprecated public static final String KEY_PREFIX = "apn.";
     field public static final String KEY_SETTINGS_DEFAULT_PROTOCOL_STRING = "apn.settings_default_protocol_string";
     field public static final String KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING = "apn.settings_default_roaming_protocol_string";
     field public static final String PROTOCOL_IPV4 = "IP";
@@ -41242,6 +40784,7 @@
     field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e
     field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f
     field public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 65; // 0x41
+    field public static final int ALL_MATCHING_RULES_FAILED = 2254; // 0x8ce
     field public static final int APN_DISABLED = 2045; // 0x7fd
     field public static final int APN_DISALLOWED_ON_ROAMING = 2059; // 0x80b
     field public static final int APN_MISMATCH = 2054; // 0x806
@@ -41391,6 +40934,7 @@
     field public static final int LTE_NAS_SERVICE_REQUEST_FAILED = 2117; // 0x845
     field public static final int LTE_THROTTLING_NOT_REQUIRED = 2127; // 0x84f
     field public static final int MAC_FAILURE = 2183; // 0x887
+    field public static final int MATCH_ALL_RULE_NOT_ALLOWED = 2253; // 0x8cd
     field public static final int MAXIMIUM_NSAPIS_EXCEEDED = 2157; // 0x86d
     field public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 2166; // 0x876
     field public static final int MAX_ACCESS_PROBE = 2079; // 0x81f
@@ -42176,6 +41720,7 @@
     method @NonNull public android.telephony.SmsManager createForSubscriptionId(int);
     method public java.util.ArrayList<java.lang.String> divideMessage(String);
     method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
+    method public void downloadMultimediaMessage(@NonNull android.content.Context, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
     method @NonNull public android.os.Bundle getCarrierConfigValues();
     method @Deprecated public static android.telephony.SmsManager getDefault();
     method public static int getDefaultSmsSubscriptionId();
@@ -42187,6 +41732,7 @@
     method public void injectSmsPdu(byte[], String, android.app.PendingIntent);
     method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
     method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
+    method public void sendMultimediaMessage(@NonNull android.content.Context, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
     method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
     method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, long);
     method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String, @Nullable String);
@@ -46704,7 +46250,6 @@
     method public long getAppVsyncOffsetNanos();
     method public void getCurrentSizeRange(android.graphics.Point, android.graphics.Point);
     method @Nullable public android.view.DisplayCutout getCutout();
-    method @Nullable public android.hardware.display.DeviceProductInfo getDeviceProductInfo();
     method public int getDisplayId();
     method public int getFlags();
     method public android.view.Display.HdrCapabilities getHdrCapabilities();
@@ -48028,16 +47573,47 @@
     method public void onScaleEnd(android.view.ScaleGestureDetector);
   }
 
+  @UiThread public interface ScrollCaptureCallback {
+    method public void onScrollCaptureEnd(@NonNull Runnable);
+    method public void onScrollCaptureImageRequest(@NonNull android.view.ScrollCaptureSession, @NonNull android.os.CancellationSignal, @NonNull android.graphics.Rect, @NonNull java.util.function.Consumer<android.graphics.Rect>);
+    method public void onScrollCaptureSearch(@NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.graphics.Rect>);
+    method public void onScrollCaptureStart(@NonNull android.view.ScrollCaptureSession, @NonNull android.os.CancellationSignal, @NonNull Runnable);
+  }
+
+  public class ScrollCaptureSession {
+    ctor public ScrollCaptureSession(@NonNull android.view.Surface, @NonNull android.graphics.Rect, @NonNull android.graphics.Point);
+    method @NonNull public android.graphics.Point getPositionInWindow();
+    method @NonNull public android.graphics.Rect getScrollBounds();
+    method @NonNull public android.view.Surface getSurface();
+  }
+
+  public final class ScrollCaptureTarget {
+    ctor public ScrollCaptureTarget(@NonNull android.view.View, @NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull android.view.ScrollCaptureCallback);
+    method @NonNull public android.view.ScrollCaptureCallback getCallback();
+    method @NonNull public android.view.View getContainingView();
+    method public int getHint();
+    method @NonNull public android.graphics.Rect getLocalVisibleRect();
+    method @NonNull public android.graphics.Point getPositionInWindow();
+    method @Nullable public android.graphics.Rect getScrollBounds();
+    method public void setScrollBounds(@Nullable android.graphics.Rect);
+    method @UiThread public void updatePositionInWindow();
+  }
+
   public class SearchEvent {
     ctor public SearchEvent(android.view.InputDevice);
     method public android.view.InputDevice getInputDevice();
   }
 
   public class SoundEffectConstants {
+    method public static int getConstantForFocusDirection(int, boolean);
     method public static int getContantForFocusDirection(int);
     field public static final int CLICK = 0; // 0x0
     field public static final int NAVIGATION_DOWN = 4; // 0x4
     field public static final int NAVIGATION_LEFT = 1; // 0x1
+    field public static final int NAVIGATION_REPEAT_DOWN = 8; // 0x8
+    field public static final int NAVIGATION_REPEAT_LEFT = 5; // 0x5
+    field public static final int NAVIGATION_REPEAT_RIGHT = 7; // 0x7
+    field public static final int NAVIGATION_REPEAT_UP = 6; // 0x6
     field public static final int NAVIGATION_RIGHT = 3; // 0x3
     field public static final int NAVIGATION_UP = 2; // 0x2
   }
@@ -48349,6 +47925,7 @@
     method public void dispatchProvideStructure(android.view.ViewStructure);
     method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
+    method public void dispatchScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>);
     method protected void dispatchSetActivated(boolean);
     method protected void dispatchSetPressed(boolean);
     method protected void dispatchSetSelected(boolean);
@@ -48508,6 +48085,7 @@
     method public int getScrollBarFadeDuration();
     method public int getScrollBarSize();
     method public int getScrollBarStyle();
+    method public int getScrollCaptureHint();
     method public int getScrollIndicators();
     method public final int getScrollX();
     method public final int getScrollY();
@@ -48676,6 +48254,7 @@
     method public void onRtlPropertiesChanged(int);
     method @CallSuper @Nullable protected android.os.Parcelable onSaveInstanceState();
     method public void onScreenStateChanged(int);
+    method public void onScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>);
     method protected void onScrollChanged(int, int, int, int);
     method protected boolean onSetAlpha(int);
     method protected void onSizeChanged(int, int, int, int);
@@ -48858,6 +48437,8 @@
     method public void setScrollBarFadeDuration(int);
     method public void setScrollBarSize(int);
     method public void setScrollBarStyle(int);
+    method public final void setScrollCaptureCallback(@Nullable android.view.ScrollCaptureCallback);
+    method public void setScrollCaptureHint(int);
     method public void setScrollContainer(boolean);
     method public void setScrollIndicators(int);
     method public void setScrollIndicators(int, int);
@@ -49036,6 +48617,10 @@
     field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
     field public static final int SCROLL_AXIS_NONE = 0; // 0x0
     field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_CAPTURE_HINT_AUTO = 0; // 0x0
+    field public static final int SCROLL_CAPTURE_HINT_EXCLUDE = 1; // 0x1
+    field public static final int SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS = 4; // 0x4
+    field public static final int SCROLL_CAPTURE_HINT_INCLUDE = 2; // 0x2
     field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
     field public static final int SCROLL_INDICATOR_END = 32; // 0x20
     field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
@@ -49820,6 +49405,7 @@
     method public abstract boolean performContextMenuIdentifierAction(int, int);
     method public abstract boolean performPanelIdentifierAction(int, int, int);
     method public abstract boolean performPanelShortcut(int, int, android.view.KeyEvent, int);
+    method public void registerScrollCaptureCallback(@NonNull android.view.ScrollCaptureCallback);
     method public final void removeOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener);
     method public boolean requestFeature(int);
     method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
@@ -49899,6 +49485,7 @@
     method public abstract void takeKeyEvents(boolean);
     method public abstract void takeSurface(android.view.SurfaceHolder.Callback2);
     method public abstract void togglePanel(int, android.view.KeyEvent);
+    method public void unregisterScrollCaptureCallback(@NonNull android.view.ScrollCaptureCallback);
     field public static final int DECOR_CAPTION_SHADE_AUTO = 0; // 0x0
     field public static final int DECOR_CAPTION_SHADE_DARK = 2; // 0x2
     field public static final int DECOR_CAPTION_SHADE_LIGHT = 1; // 0x1
@@ -53391,6 +52978,7 @@
     method public int getCheckedItemPosition();
     method public android.util.SparseBooleanArray getCheckedItemPositions();
     method public int getChoiceMode();
+    method public int getEdgeEffectType();
     method public int getListPaddingBottom();
     method public int getListPaddingLeft();
     method public int getListPaddingRight();
@@ -53432,6 +53020,7 @@
     method public void setChoiceMode(int);
     method public void setDrawSelectorOnTop(boolean);
     method public void setEdgeEffectColor(@ColorInt int);
+    method public void setEdgeEffectType(int);
     method public void setFastScrollAlwaysVisible(boolean);
     method public void setFastScrollEnabled(boolean);
     method public void setFastScrollStyle(int);
@@ -54440,6 +54029,7 @@
     method public boolean executeKeyEvent(android.view.KeyEvent);
     method public void fling(int);
     method public boolean fullScroll(int);
+    method public int getEdgeEffectType();
     method @ColorInt public int getLeftEdgeEffectColor();
     method public int getMaxScrollAmount();
     method @ColorInt public int getRightEdgeEffectColor();
@@ -54447,6 +54037,7 @@
     method public boolean isSmoothScrollingEnabled();
     method public boolean pageScroll(int);
     method public void setEdgeEffectColor(@ColorInt int);
+    method public void setEdgeEffectType(int);
     method public void setFillViewport(boolean);
     method public void setLeftEdgeEffectColor(@ColorInt int);
     method public void setRightEdgeEffectColor(@ColorInt int);
@@ -55300,6 +54891,7 @@
     method public void fling(int);
     method public boolean fullScroll(int);
     method @ColorInt public int getBottomEdgeEffectColor();
+    method public int getEdgeEffectType();
     method public int getMaxScrollAmount();
     method @ColorInt public int getTopEdgeEffectColor();
     method public boolean isFillViewport();
@@ -55308,6 +54900,7 @@
     method public void scrollToDescendant(@NonNull android.view.View);
     method public void setBottomEdgeEffectColor(@ColorInt int);
     method public void setEdgeEffectColor(@ColorInt int);
+    method public void setEdgeEffectType(int);
     method public void setFillViewport(boolean);
     method public void setSmoothScrollingEnabled(boolean);
     method public void setTopEdgeEffectColor(@ColorInt int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index dd9582f..e6f0e48 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -163,67 +163,14 @@
 
 package android.net {
 
-  public final class ConnectivityFrameworkInitializer {
-    method public static void registerServiceWrappers();
-  }
-
-  public class ConnectivityManager {
-    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
-    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
-    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
-  }
-
   public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
     method public int getResourceId();
   }
 
-  public final class NetworkAgentConfig implements android.os.Parcelable {
-    method @Nullable public String getSubscriberId();
-  }
-
-  public static final class NetworkAgentConfig.Builder {
-    method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
-  }
-
-  public final class NetworkCapabilities implements android.os.Parcelable {
-    field public static final int TRANSPORT_TEST = 7; // 0x7
-  }
-
   public class NetworkWatchlistManager {
     method @Nullable public byte[] getWatchlistConfigHash();
   }
 
-  public final class Proxy {
-    method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
-  }
-
-  public final class TcpRepairWindow {
-    ctor public TcpRepairWindow(int, int, int, int, int, int);
-    field public final int maxWindow;
-    field public final int rcvWnd;
-    field public final int rcvWndScale;
-    field public final int rcvWup;
-    field public final int sndWl1;
-    field public final int sndWnd;
-  }
-
-  public final class TestNetworkInterface implements android.os.Parcelable {
-    ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String);
-    method public int describeContents();
-    method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor();
-    method @NonNull public String getInterfaceName();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR;
-  }
-
-  public class TestNetworkManager {
-    method @NonNull public android.net.TestNetworkInterface createTapInterface();
-    method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>);
-    method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
-    method public void teardownTestNetwork(@NonNull android.net.Network);
-    field public static final String TEST_TAP_PREFIX = "testtap";
-  }
-
   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();
@@ -234,14 +181,6 @@
     field @NonNull public final java.util.List<java.lang.String> underlyingIfaces;
   }
 
-  public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
-    ctor public VpnTransportInfo(int);
-    method public int describeContents();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
-    field public final int type;
-  }
-
 }
 
 package android.os {
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 990388a..a99178d 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -241,12 +241,6 @@
 
 package android.net {
 
-  public class ConnectivityManager {
-    method @Deprecated public boolean requestRouteToHost(int, int);
-    method @Deprecated public int startUsingNetworkFeature(int, String);
-    method @Deprecated public int stopUsingNetworkFeature(int, String);
-  }
-
   @Deprecated public class NetworkBadging {
     method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme);
     field public static final int BADGING_4K = 30; // 0x1e
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index e5a36d1..c21c965 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -176,6 +176,7 @@
     field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
     field public static final String OBSERVE_NETWORK_POLICY = "android.permission.OBSERVE_NETWORK_POLICY";
     field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
+    field public static final String OBSERVE_SENSOR_PRIVACY = "android.permission.OBSERVE_SENSOR_PRIVACY";
     field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS";
     field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
     field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
@@ -352,6 +353,7 @@
     field public static final int config_systemContacts = 17039403; // 0x104002b
     field public static final int config_systemGallery = 17039399; // 0x1040027
     field public static final int config_systemShell = 17039402; // 0x104002a
+    field public static final int config_systemSpeechRecognizer = 17039406; // 0x104002e
   }
 
   public static final class R.style {
@@ -641,8 +643,9 @@
     method public static android.app.BroadcastOptions makeBasic();
     method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
     method public void setDontSendToRestrictedApps(boolean);
-    method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long);
-    method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(int, long);
+    method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppAllowlist(long, int, int, @Nullable String);
+    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long);
+    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(int, long);
     method public android.os.Bundle toBundle();
   }
 
@@ -884,7 +887,7 @@
   public class DevicePolicyManager {
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser();
     method @Nullable public CharSequence getDeviceOwnerOrganizationName();
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser();
@@ -1826,7 +1829,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public android.bluetooth.BufferConstraints getBufferConstraints();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getDynamicBufferSupport();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferMillis(int, int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferLengthMillis(int, int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; // 0x1
     field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; // 0x2
@@ -2032,7 +2035,7 @@
   public final class BufferConstraints implements android.os.Parcelable {
     ctor public BufferConstraints(@NonNull java.util.List<android.bluetooth.BufferConstraint>);
     method public int describeContents();
-    method @Nullable public android.bluetooth.BufferConstraint getCodec(int);
+    method @Nullable public android.bluetooth.BufferConstraint forCodec(int);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final int BUFFER_CODEC_MAX_NUM = 32; // 0x20
     field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraints> CREATOR;
@@ -2461,10 +2464,10 @@
   }
 
   public static class PackageInstaller.Session implements java.io.Closeable {
-    method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]);
+    method public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void commitTransferred(@NonNull android.content.IntentSender);
-    method @Nullable @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public android.content.pm.DataLoaderParams getDataLoaderParams();
-    method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void removeFile(int, @NonNull String);
+    method @Nullable public android.content.pm.DataLoaderParams getDataLoaderParams();
+    method public void removeFile(int, @NonNull String);
   }
 
   public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
@@ -2485,7 +2488,7 @@
   public static class PackageInstaller.SessionParams implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean);
     method @Deprecated public void setAllowDowngrade(boolean);
-    method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.USE_INSTALLER_V2"}) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
+    method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
     method public void setDontKillApp(boolean);
     method public void setEnableRollback(boolean);
     method public void setEnableRollback(boolean, int);
@@ -2550,10 +2553,12 @@
     field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
     field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
     field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
+    field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
     field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
     field public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur";
     field @Deprecated public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
     field public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION = "android.software.incremental_delivery_version";
+    field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
     field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
     field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
     field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
@@ -2874,7 +2879,7 @@
   }
 
   public class FontManager {
-    method @Nullable public android.text.FontConfig getFontConfig();
+    method @NonNull public android.text.FontConfig getFontConfig();
     method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int);
     method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.graphics.fonts.FontFileUpdateRequest, @IntRange(from=0) int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @IntRange(from=0) int);
@@ -2908,6 +2913,22 @@
     method public boolean injectSensorData(android.hardware.Sensor, float[], int, long);
   }
 
+  public final class SensorPrivacyManager {
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+  }
+
+  public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
+    method public void onSensorPrivacyChanged(boolean);
+  }
+
+  public static class SensorPrivacyManager.Sensors {
+    field public static final int CAMERA = 2; // 0x2
+    field public static final int MICROPHONE = 1; // 0x1
+  }
+
 }
 
 package android.hardware.biometrics {
@@ -4688,6 +4709,7 @@
   }
 
   public class LocationManager {
+    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener);
     method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
     method @Nullable public String getExtraLocationControllerPackage();
@@ -4702,7 +4724,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String);
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
-    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerProviderRequestListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.Listener);
+    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void removeProviderRequestChangedListener(@NonNull android.location.provider.ProviderRequest.ChangedListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
@@ -4711,7 +4733,6 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setProviderEnabledForUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterGnssBatchedLocationCallback(@NonNull android.location.BatchedLocationCallback);
-    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void unregisterProviderRequestListener(@NonNull android.location.provider.ProviderRequest.Listener);
   }
 
   public final class LocationRequest implements android.os.Parcelable {
@@ -4861,7 +4882,7 @@
     method @NonNull public android.location.provider.ProviderRequest.Builder setWorkSource(@NonNull android.os.WorkSource);
   }
 
-  public static interface ProviderRequest.Listener {
+  public static interface ProviderRequest.ChangedListener {
     method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest);
   }
 
@@ -5072,7 +5093,7 @@
   }
 
   public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
-    method @RequiresPermission(android.Manifest.permission.BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener, @Nullable android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener);
   }
 
   public static interface MediaPlayer.OnRtpRxNoticeListener {
@@ -7063,102 +7084,6 @@
 
 package android.net {
 
-  public class CaptivePortal implements android.os.Parcelable {
-    method public void logEvent(int, @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork();
-    method public void useNetwork();
-    field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
-    field public static final int APP_RETURN_DISMISSED = 0; // 0x0
-    field public static final int APP_RETURN_UNWANTED = 1; // 0x1
-    field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
-  }
-
-  public final class CaptivePortalData implements android.os.Parcelable {
-    method public int describeContents();
-    method public long getByteLimit();
-    method public long getExpiryTimeMillis();
-    method public long getRefreshTimeMillis();
-    method @Nullable public android.net.Uri getUserPortalUrl();
-    method public int getUserPortalUrlSource();
-    method @Nullable public String getVenueFriendlyName();
-    method @Nullable public android.net.Uri getVenueInfoUrl();
-    method public int getVenueInfoUrlSource();
-    method public boolean isCaptive();
-    method public boolean isSessionExtendable();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0
-    field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR;
-  }
-
-  public static class CaptivePortalData.Builder {
-    ctor public CaptivePortalData.Builder();
-    ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData);
-    method @NonNull public android.net.CaptivePortalData build();
-    method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long);
-    method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean);
-    method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long);
-    method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
-    method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
-    method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
-    method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int);
-    method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String);
-    method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
-    method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int);
-  }
-
-  public class ConnectivityManager {
-    method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
-    method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
-    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
-    method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
-    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
-    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
-    method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener);
-    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
-    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
-    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
-    method public void unregisterQosCallback(@NonNull android.net.QosCallback);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
-    field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
-    field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
-    field public static final int TETHERING_BLUETOOTH = 2; // 0x2
-    field public static final int TETHERING_USB = 1; // 0x1
-    field public static final int TETHERING_WIFI = 0; // 0x0
-    field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd
-    field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
-    field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
-    field public static final int TYPE_NONE = -1; // 0xffffffff
-    field @Deprecated public static final int TYPE_PROXY = 16; // 0x10
-    field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
-  }
-
-  public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener {
-    method public void onComplete();
-  }
-
-  @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback {
-    ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback();
-    method @Deprecated public void onTetheringFailed();
-    method @Deprecated public void onTetheringStarted();
-  }
-
-  @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener {
-    method @Deprecated public void onTetheringEntitlementResult(int);
-  }
-
-  @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback {
-    ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback();
-    method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network);
-  }
-
   public class DnsResolverServiceManager {
     method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public static android.os.IBinder getService(@NonNull android.content.Context);
   }
@@ -7176,48 +7101,6 @@
     method public void release();
   }
 
-  public final class InvalidPacketException extends java.lang.Exception {
-    ctor public InvalidPacketException(int);
-    method public int getError();
-    field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
-    field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
-    field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
-  }
-
-  public final class IpConfiguration implements android.os.Parcelable {
-    ctor public IpConfiguration();
-    ctor public IpConfiguration(@NonNull android.net.IpConfiguration);
-    method public int describeContents();
-    method @Nullable public android.net.ProxyInfo getHttpProxy();
-    method @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment();
-    method @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings();
-    method @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration();
-    method public void setHttpProxy(@Nullable android.net.ProxyInfo);
-    method public void setIpAssignment(@NonNull android.net.IpConfiguration.IpAssignment);
-    method public void setProxySettings(@NonNull android.net.IpConfiguration.ProxySettings);
-    method public void setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.IpConfiguration> CREATOR;
-  }
-
-  public enum IpConfiguration.IpAssignment {
-    enum_constant public static final android.net.IpConfiguration.IpAssignment DHCP;
-    enum_constant public static final android.net.IpConfiguration.IpAssignment STATIC;
-    enum_constant public static final android.net.IpConfiguration.IpAssignment UNASSIGNED;
-  }
-
-  public enum IpConfiguration.ProxySettings {
-    enum_constant public static final android.net.IpConfiguration.ProxySettings NONE;
-    enum_constant public static final android.net.IpConfiguration.ProxySettings PAC;
-    enum_constant public static final android.net.IpConfiguration.ProxySettings STATIC;
-    enum_constant public static final android.net.IpConfiguration.ProxySettings UNASSIGNED;
-  }
-
-  public final class IpPrefix implements android.os.Parcelable {
-    ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
-    ctor public IpPrefix(@NonNull String);
-  }
-
   public final class IpSecManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
@@ -7235,68 +7118,6 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecTransform buildTunnelModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
   }
 
-  public class KeepalivePacketData {
-    ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull byte[]) throws android.net.InvalidPacketException;
-    method @NonNull public java.net.InetAddress getDstAddress();
-    method public int getDstPort();
-    method @NonNull public byte[] getPacket();
-    method @NonNull public java.net.InetAddress getSrcAddress();
-    method public int getSrcPort();
-  }
-
-  public class LinkAddress implements android.os.Parcelable {
-    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
-    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long);
-    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
-    ctor public LinkAddress(@NonNull String);
-    ctor public LinkAddress(@NonNull String, int, int);
-    method public long getDeprecationTime();
-    method public long getExpirationTime();
-    method public boolean isGlobalPreferred();
-    method public boolean isIpv4();
-    method public boolean isIpv6();
-    method public boolean isSameAddressAs(@Nullable android.net.LinkAddress);
-    field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL
-    field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL
-  }
-
-  public final class LinkProperties implements android.os.Parcelable {
-    ctor public LinkProperties(@Nullable android.net.LinkProperties);
-    ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean);
-    method public boolean addDnsServer(@NonNull java.net.InetAddress);
-    method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
-    method public boolean addPcscfServer(@NonNull java.net.InetAddress);
-    method @NonNull public java.util.List<java.net.InetAddress> getAddresses();
-    method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames();
-    method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses();
-    method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes();
-    method @Nullable public android.net.Uri getCaptivePortalApiUrl();
-    method @Nullable public android.net.CaptivePortalData getCaptivePortalData();
-    method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
-    method @Nullable public String getTcpBufferSizes();
-    method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
-    method public boolean hasGlobalIpv6Address();
-    method public boolean hasIpv4Address();
-    method public boolean hasIpv4DefaultRoute();
-    method public boolean hasIpv4DnsServer();
-    method public boolean hasIpv6DefaultRoute();
-    method public boolean hasIpv6DnsServer();
-    method public boolean isIpv4Provisioned();
-    method public boolean isIpv6Provisioned();
-    method public boolean isProvisioned();
-    method public boolean isReachable(@NonNull java.net.InetAddress);
-    method public boolean removeDnsServer(@NonNull java.net.InetAddress);
-    method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
-    method public boolean removeRoute(@NonNull android.net.RouteInfo);
-    method public void setCaptivePortalApiUrl(@Nullable android.net.Uri);
-    method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData);
-    method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
-    method public void setPrivateDnsServerName(@Nullable String);
-    method public void setTcpBufferSizes(@Nullable String);
-    method public void setUsePrivateDns(boolean);
-    method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
-  }
-
   public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
     ctor public MatchAllNetworkSpecifier();
     method public int describeContents();
@@ -7304,104 +7125,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.MatchAllNetworkSpecifier> CREATOR;
   }
 
-  public final class NattKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
-    ctor public NattKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException;
-    method public int describeContents();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.NattKeepalivePacketData> CREATOR;
-  }
-
-  public class Network implements android.os.Parcelable {
-    ctor public Network(@NonNull android.net.Network);
-    method public int getNetId();
-    method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
-  }
-
-  public abstract class NetworkAgent {
-    ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
-    method @Nullable public android.net.Network getNetwork();
-    method public void markConnected();
-    method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
-    method public void onAutomaticReconnectDisabled();
-    method public void onNetworkUnwanted();
-    method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
-    method public void onQosCallbackUnregistered(int);
-    method public void onRemoveKeepalivePacketFilter(int);
-    method public void onSaveAcceptUnvalidated(boolean);
-    method public void onSignalStrengthThresholdsUpdated(@NonNull int[]);
-    method public void onStartSocketKeepalive(int, @NonNull java.time.Duration, @NonNull android.net.KeepalivePacketData);
-    method public void onStopSocketKeepalive(int);
-    method public void onValidationStatus(int, @Nullable android.net.Uri);
-    method @NonNull public android.net.Network register();
-    method public final void sendLinkProperties(@NonNull android.net.LinkProperties);
-    method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
-    method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
-    method public final void sendQosCallbackError(int, int);
-    method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes);
-    method public final void sendQosSessionLost(int, int);
-    method public final void sendSocketKeepaliveEvent(int, int);
-    method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
-    method public void unregister();
-    field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
-    field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
-  }
-
-  public final class NetworkAgentConfig implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getLegacyType();
-    method @NonNull public String getLegacyTypeName();
-    method public boolean isExplicitlySelected();
-    method public boolean isPartialConnectivityAcceptable();
-    method public boolean isUnvalidatedConnectivityAcceptable();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
-  }
-
-  public static final class NetworkAgentConfig.Builder {
-    ctor public NetworkAgentConfig.Builder();
-    method @NonNull public android.net.NetworkAgentConfig build();
-    method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean);
-    method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int);
-    method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String);
-    method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean);
-    method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean);
-  }
-
-  public final class NetworkCapabilities implements android.os.Parcelable {
-    ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean);
-    method @NonNull public int[] getAdministratorUids();
-    method @Nullable public String getSsid();
-    method @NonNull public int[] getTransportTypes();
-    method public boolean isPrivateDnsBroken();
-    method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
-    field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c
-    field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
-    field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
-    field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
-    field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b
-  }
-
-  public static final class NetworkCapabilities.Builder {
-    ctor public NetworkCapabilities.Builder();
-    ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
-    method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
-    method @NonNull public android.net.NetworkCapabilities build();
-    method @NonNull public android.net.NetworkCapabilities.Builder clearAll();
-    method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
-    method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
-    method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
-  }
-
   public class NetworkKey implements android.os.Parcelable {
     ctor public NetworkKey(android.net.WifiKey);
     method @Nullable public static android.net.NetworkKey createFromScanResult(@NonNull android.net.wifi.ScanResult);
@@ -7413,15 +7136,6 @@
     field public final android.net.WifiKey wifiKey;
   }
 
-  public class NetworkProvider {
-    ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
-    method public int getProviderId();
-    method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest);
-    method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int);
-    field public static final int ID_NONE = -1; // 0xffffffff
-  }
-
   public abstract class NetworkRecommendationProvider {
     ctor public NetworkRecommendationProvider(android.content.Context, java.util.concurrent.Executor);
     method public final android.os.IBinder getBinder();
@@ -7431,15 +7145,6 @@
   public class NetworkReleasedException extends java.lang.Exception {
   }
 
-  public class NetworkRequest implements android.os.Parcelable {
-    method @Nullable public String getRequestorPackageName();
-    method public int getRequestorUid();
-  }
-
-  public static class NetworkRequest.Builder {
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
-  }
-
   public class NetworkScoreManager {
     method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean clearScores() throws java.lang.SecurityException;
     method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public void disableScoring() throws java.lang.SecurityException;
@@ -7564,16 +7269,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR;
   }
 
-  public final class RouteInfo implements android.os.Parcelable {
-    ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int);
-    ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int);
-    method public int getMtu();
-    method public int getType();
-    field public static final int RTN_THROW = 9; // 0x9
-    field public static final int RTN_UNICAST = 1; // 0x1
-    field public static final int RTN_UNREACHABLE = 7; // 0x7
-  }
-
   public class RssiCurve implements android.os.Parcelable {
     ctor public RssiCurve(int, int, byte[]);
     ctor public RssiCurve(int, int, byte[], int);
@@ -7605,53 +7300,12 @@
     field public final android.net.RssiCurve rssiCurve;
   }
 
-  public abstract class SocketKeepalive implements java.lang.AutoCloseable {
-    field public static final int SUCCESS = 0; // 0x0
-  }
-
   public class SocketLocalAddressChangedException extends java.lang.Exception {
   }
 
   public class SocketNotBoundException extends java.lang.Exception {
   }
 
-  public final class StaticIpConfiguration implements android.os.Parcelable {
-    ctor public StaticIpConfiguration();
-    ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
-    method public void addDnsServer(@NonNull java.net.InetAddress);
-    method public void clear();
-    method public int describeContents();
-    method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
-    method @Nullable public String getDomains();
-    method @Nullable public java.net.InetAddress getGateway();
-    method @Nullable public android.net.LinkAddress getIpAddress();
-    method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR;
-  }
-
-  public static final class StaticIpConfiguration.Builder {
-    ctor public StaticIpConfiguration.Builder();
-    method @NonNull public android.net.StaticIpConfiguration build();
-    method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>);
-    method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String);
-    method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress);
-    method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
-  }
-
-  public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
-    ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException;
-    method public int describeContents();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR;
-    field public final int ipTos;
-    field public final int ipTtl;
-    field public final int tcpAck;
-    field public final int tcpSeq;
-    field public final int tcpWindow;
-    field public final int tcpWindowScale;
-  }
-
   public class TrafficStats {
     method public static void setThreadStatsTagApp();
     method public static void setThreadStatsTagBackup();
@@ -7664,11 +7318,6 @@
     field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00
   }
 
-  public interface TransportInfo {
-    method public default boolean hasLocationSensitiveFields();
-    method @NonNull public default android.net.TransportInfo makeCopy(boolean);
-  }
-
   public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
     method @NonNull public String toSafeString();
   }
@@ -7692,23 +7341,6 @@
 
 }
 
-package android.net.apf {
-
-  public final class ApfCapabilities implements android.os.Parcelable {
-    ctor public ApfCapabilities(int, int, int);
-    method public int describeContents();
-    method public static boolean getApfDrop8023Frames();
-    method @NonNull public static int[] getApfEtherTypeBlackList();
-    method public boolean hasDataAccess();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR;
-    field public final int apfPacketFormat;
-    field public final int apfVersionSupported;
-    field public final int maximumApfProgramSize;
-  }
-
-}
-
 package android.net.metrics {
 
   @Deprecated public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
@@ -7903,19 +7535,6 @@
 
 }
 
-package android.net.util {
-
-  public final class SocketUtils {
-    method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException;
-    method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException;
-    method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int);
-    method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int);
-    method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]);
-    method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]);
-  }
-
-}
-
 package android.net.vcn {
 
   public class VcnManager {
@@ -8607,11 +8226,18 @@
     method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
     method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long);
-    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @Nullable String);
+    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String);
     field public static final int EVENT_MMS = 2; // 0x2
     field public static final int EVENT_SMS = 1; // 0x1
     field public static final int EVENT_UNSPECIFIED = 0; // 0x0
+    field public static final int REASON_ACTIVITY_RECOGNITION = 102; // 0x66
+    field public static final int REASON_GEOFENCING = 100; // 0x64
+    field public static final int REASON_OTHER = 1; // 0x1
+    field public static final int REASON_PUSH_MESSAGING = 101; // 0x65
+    field public static final int REASON_UNKNOWN = 0; // 0x0
     field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
     field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
   }
@@ -8760,13 +8386,13 @@
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set<java.lang.String>) throws android.os.UserManager.UserOperationException;
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getAllProfiles();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getEnabledProfiles();
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getRestrictedProfileParent();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType();
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public long[] getSerialNumbersOfUsers(boolean);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
     method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
@@ -9204,6 +8830,7 @@
     field public static final String NAMESPACE_BIOMETRICS = "biometrics";
     field public static final String NAMESPACE_BLOBSTORE = "blobstore";
     field public static final String NAMESPACE_BLUETOOTH = "bluetooth";
+    field public static final String NAMESPACE_CLIPBOARD = "clipboard";
     field public static final String NAMESPACE_CONNECTIVITY = "connectivity";
     field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
     field @Deprecated public static final String NAMESPACE_DEX_BOOT = "dex_boot";
@@ -10763,7 +10390,7 @@
     method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
     method @Nullable public final String getTelecomCallId();
     method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
-    method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean);
+    method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(@NonNull android.telecom.Connection.CallFilteringCompletionInfo);
     method public final void resetConnectionTime();
     method public void setCallDirection(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
@@ -10779,6 +10406,16 @@
     field public static final int PROPERTY_REMOTELY_HOSTED = 2048; // 0x800
   }
 
+  public static final class Connection.CallFilteringCompletionInfo implements android.os.Parcelable {
+    ctor public Connection.CallFilteringCompletionInfo(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, @Nullable android.content.ComponentName);
+    method public int describeContents();
+    method @Nullable public android.telecom.CallScreeningService.CallResponse getCallResponse();
+    method @Nullable public android.content.ComponentName getCallScreeningComponent();
+    method public boolean isBlocked();
+    method public boolean isInContacts();
+    field @NonNull public static final android.os.Parcelable.Creator<android.telecom.Connection.CallFilteringCompletionInfo> CREATOR;
+  }
+
   public final class ConnectionRequest implements android.os.Parcelable {
     method @Nullable public String getTelecomCallId();
   }
@@ -10940,7 +10577,7 @@
   }
 
   public final class RemoteConnection {
-    method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean);
+    method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(@NonNull android.telecom.Connection.CallFilteringCompletionInfo);
     method @Deprecated public void setAudioState(android.telecom.AudioState);
   }
 
@@ -12026,7 +11663,6 @@
     field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
     field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
     field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
-    field public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED = "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
     field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
@@ -12251,6 +11887,7 @@
     method public long getRetryDurationMillis();
     method @Nullable public android.telephony.data.SliceInfo getSliceInfo();
     method @Deprecated public int getSuggestedRetryTime();
+    method @NonNull public java.util.List<android.telephony.data.TrafficDescriptor> getTrafficDescriptors();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
     field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1
@@ -12286,6 +11923,7 @@
     method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo);
     method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int);
+    method @NonNull public android.telephony.data.DataCallResponse.Builder setTrafficDescriptors(@NonNull java.util.List<android.telephony.data.TrafficDescriptor>);
   }
 
   public final class DataProfile implements android.os.Parcelable {
@@ -12356,7 +11994,7 @@
     method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
     method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
     method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
-    method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @NonNull android.telephony.data.DataServiceCallback);
+    method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @Nullable android.telephony.data.TrafficDescriptor, boolean, @NonNull android.telephony.data.DataServiceCallback);
   }
 
   public class DataServiceCallback {
@@ -12455,6 +12093,15 @@
     method @NonNull public android.telephony.data.ThrottleStatus.Builder setTransportType(int);
   }
 
+  public final class TrafficDescriptor implements android.os.Parcelable {
+    ctor public TrafficDescriptor(@Nullable String, @Nullable String);
+    method public int describeContents();
+    method @Nullable public String getDnn();
+    method @Nullable public String getOsAppId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.TrafficDescriptor> CREATOR;
+  }
+
 }
 
 package android.telephony.euicc {
@@ -14274,21 +13921,10 @@
 
   public final class UwbManager {
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos();
-    method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getAngleOfArrivalSupport();
-    method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerInitiatorSession();
-    method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerResponderSession();
-    method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxSimultaneousSessions();
     method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo();
-    method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.List<java.lang.Integer> getSupportedChannelNumbers();
-    method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.Set<java.lang.Integer> getSupportedPreambleCodeIndices();
-    method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public boolean isRangingSupported();
     method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
-    field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2; // 0x2
-    field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3; // 0x3
-    field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4; // 0x4
-    field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1; // 0x1
   }
 
   public static interface UwbManager.AdapterStateCallback {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a02320d..28724f2 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -10,9 +10,11 @@
     field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
     field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
     field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
+    field public static final String CLEAR_FREEZE_PERIOD = "android.permission.CLEAR_FREEZE_PERIOD";
     field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
     field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
     field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE";
+    field public static final String FORCE_DEVICE_POLICY_MANAGER_LOGS = "android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS";
     field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final String KEEP_UNINSTALLED_PACKAGES = "android.permission.KEEP_UNINSTALLED_PACKAGES";
     field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
@@ -91,6 +93,8 @@
     method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
     method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
     method public long getTotalRam();
+    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidProcessCapabilities(int);
+    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidProcessState(int);
     method public void holdLock(android.os.IBinder, int);
     method public static boolean isHighEndGfx();
     method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
@@ -106,7 +110,10 @@
     field public static final int PROCESS_CAPABILITY_FOREGROUND_CAMERA = 2; // 0x2
     field public static final int PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1; // 0x1
     field public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 4; // 0x4
+    field public static final int PROCESS_CAPABILITY_NETWORK = 8; // 0x8
     field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0
+    field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4
+    field public static final int PROCESS_STATE_TOP = 2; // 0x2
   }
 
   public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
@@ -384,7 +391,11 @@
 
   public class DevicePolicyManager {
     method public int checkProvisioningPreCondition(@Nullable String, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
     method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
+    method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
+    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
+    method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
     method public void forceUpdateUserSetupComplete();
     method public long getLastBugReportRequestTime();
     method public long getLastNetworkLogRetrievalTime();
@@ -392,10 +403,13 @@
     method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
     method public boolean isCurrentInputMethodSetByOwner();
     method public boolean isFactoryResetProtectionPolicySupported();
+    method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
     method @NonNull public static String operationSafetyReasonToString(int);
     method @NonNull public static String operationToString(int);
     method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
     method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
+    method @RequiresPermission(allOf={"android.permission.MANAGE_DEVICE_ADMINS", android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
+    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
     method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
     field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
     field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
@@ -580,6 +594,14 @@
 
 }
 
+package android.appwidget {
+
+  public class AppWidgetManager {
+    method public void setBindAppWidgetPermission(@NonNull String, int, boolean);
+  }
+
+}
+
 package android.bluetooth {
 
   public final class BluetoothClass implements android.os.Parcelable {
@@ -696,8 +718,10 @@
     method public void holdLock(android.os.IBinder, int);
     method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>);
     field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
+    field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
     field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
     field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
+    field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
     field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
     field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
     field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
@@ -724,6 +748,65 @@
     ctor public ShortcutManager(android.content.Context);
   }
 
+  public class UserInfo implements android.os.Parcelable {
+    ctor public UserInfo(int, String, int);
+    ctor public UserInfo(int, String, String, int);
+    ctor public UserInfo(int, String, String, int, String);
+    ctor @Deprecated public UserInfo();
+    ctor public UserInfo(android.content.pm.UserInfo);
+    method public boolean canHaveProfile();
+    method public int describeContents();
+    method public android.os.UserHandle getUserHandle();
+    method public boolean isAdmin();
+    method public boolean isDemo();
+    method public boolean isEnabled();
+    method public boolean isEphemeral();
+    method public boolean isFull();
+    method public boolean isGuest();
+    method public boolean isInitialized();
+    method public boolean isManagedProfile();
+    method public boolean isPrimary();
+    method public boolean isProfile();
+    method public boolean isQuietModeEnabled();
+    method public boolean isRestricted();
+    method public boolean isSystemOnly();
+    method public static boolean isSystemOnly(int);
+    method public boolean supportsSwitchTo();
+    method public boolean supportsSwitchToByUser();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserInfo> CREATOR;
+    field public static final int FLAG_ADMIN = 2; // 0x2
+    field @Deprecated public static final int FLAG_DEMO = 512; // 0x200
+    field public static final int FLAG_DISABLED = 64; // 0x40
+    field public static final int FLAG_EPHEMERAL = 256; // 0x100
+    field public static final int FLAG_FULL = 1024; // 0x400
+    field @Deprecated public static final int FLAG_GUEST = 4; // 0x4
+    field public static final int FLAG_INITIALIZED = 16; // 0x10
+    field @Deprecated public static final int FLAG_MANAGED_PROFILE = 32; // 0x20
+    field public static final int FLAG_PRIMARY = 1; // 0x1
+    field public static final int FLAG_PROFILE = 4096; // 0x1000
+    field public static final int FLAG_QUIET_MODE = 128; // 0x80
+    field @Deprecated public static final int FLAG_RESTRICTED = 8; // 0x8
+    field public static final int FLAG_SYSTEM = 2048; // 0x800
+    field public static final int NO_PROFILE_GROUP_ID = -10000; // 0xffffd8f0
+    field public boolean convertedFromPreCreated;
+    field public long creationTime;
+    field public int flags;
+    field public boolean guestToRemove;
+    field public String iconPath;
+    field public int id;
+    field public String lastLoggedInFingerprint;
+    field public long lastLoggedInTime;
+    field public String name;
+    field public boolean partial;
+    field public boolean preCreated;
+    field public int profileBadge;
+    field public int profileGroupId;
+    field public int restrictedProfileParentId;
+    field public int serialNumber;
+    field public String userType;
+  }
+
 }
 
 package android.content.res {
@@ -837,7 +920,7 @@
 package android.graphics.fonts {
 
   public class FontManager {
-    method @Nullable public android.text.FontConfig getFontConfig();
+    method @NonNull public android.text.FontConfig getFontConfig();
     method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int);
     method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.graphics.fonts.FontFileUpdateRequest, @IntRange(from=0) int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @IntRange(from=0) int);
@@ -858,11 +941,21 @@
 package android.hardware {
 
   public final class SensorPrivacyManager {
-    method public boolean isIndividualSensorPrivacyEnabled(int);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacy(int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacyForProfileGroup(int, boolean);
-    field public static final int INDIVIDUAL_SENSOR_CAMERA = 2; // 0x2
-    field public static final int INDIVIDUAL_SENSOR_MICROPHONE = 1; // 0x1
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyForProfileGroup(int, boolean);
+  }
+
+  public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
+    method public void onSensorPrivacyChanged(boolean);
+  }
+
+  public static class SensorPrivacyManager.Sensors {
+    field public static final int CAMERA = 2; // 0x2
+    field public static final int MICROPHONE = 1; // 0x1
   }
 
 }
@@ -1477,8 +1570,15 @@
   }
 
   public class UserManager {
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createProfileForUser(@Nullable String, @NonNull String, int, int, @Nullable String[]);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public String getUserType();
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
+    method public static boolean isGuestUserEphemeral();
     method public static boolean isSplitSystemUser();
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException;
   }
 
   public final class VibrationAttributes implements android.os.Parcelable {
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 87fb5b1..a536efb 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -468,7 +468,7 @@
 GetterSetterNames: android.location.GnssMeasurement#setCarrierFrequencyHz(float):
     
 GetterSetterNames: android.location.GnssMeasurement#setCodeType(String):
-
+    
 GetterSetterNames: android.location.GnssMeasurement#setCorrelationVectors(java.util.Collection<android.location.CorrelationVector>):
     
 GetterSetterNames: android.location.GnssMeasurement#setFullInterSignalBiasNanos(double):
@@ -480,7 +480,7 @@
 GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasUncertaintyNanos(double):
     
 GetterSetterNames: android.location.GnssMeasurement#setSatellitePvt(android.location.SatellitePvt):
-
+    
 GetterSetterNames: android.location.GnssMeasurement#setSnrInDb(double):
     
 GetterSetterNames: android.location.LocationRequest#isLocationSettingsIgnored():
@@ -953,6 +953,32 @@
     
 MissingNullability: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context) parameter #0:
     
+MissingNullability: android.content.pm.UserInfo#UserInfo(android.content.pm.UserInfo) parameter #0:
+    Missing nullability on parameter `orig` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #1:
+    Missing nullability on parameter `name` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #2:
+    Missing nullability on parameter `iconPath` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #1:
+    Missing nullability on parameter `name` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #2:
+    Missing nullability on parameter `iconPath` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #4:
+    Missing nullability on parameter `userType` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, int) parameter #1:
+    Missing nullability on parameter `name` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#getUserHandle():
+    Missing nullability on method `getUserHandle` return
+MissingNullability: android.content.pm.UserInfo#iconPath:
+    Missing nullability on field `iconPath` in class `class android.content.pm.UserInfo`
+MissingNullability: android.content.pm.UserInfo#lastLoggedInFingerprint:
+    Missing nullability on field `lastLoggedInFingerprint` in class `class android.content.pm.UserInfo`
+MissingNullability: android.content.pm.UserInfo#name:
+    Missing nullability on field `name` in class `class android.content.pm.UserInfo`
+MissingNullability: android.content.pm.UserInfo#userType:
+    Missing nullability on field `userType` in class `class android.content.pm.UserInfo`
+MissingNullability: android.content.pm.UserInfo#writeToParcel(android.os.Parcel, int) parameter #0:
+    Missing nullability on parameter `dest` in method `writeToParcel`
 MissingNullability: android.content.res.AssetManager#getOverlayablesToString(String) parameter #0:
     
 MissingNullability: android.content.res.Configuration#windowConfiguration:
@@ -2433,6 +2459,38 @@
     
 MutableBareField: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill:
     
+MutableBareField: android.content.pm.UserInfo#convertedFromPreCreated:
+    Bare field convertedFromPreCreated must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#creationTime:
+    Bare field creationTime must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#flags:
+    Bare field flags must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#guestToRemove:
+    Bare field guestToRemove must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#iconPath:
+    Bare field iconPath must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#id:
+    Bare field id must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#lastLoggedInFingerprint:
+    Bare field lastLoggedInFingerprint must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#lastLoggedInTime:
+    Bare field lastLoggedInTime must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#name:
+    Bare field name must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#partial:
+    Bare field partial must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#preCreated:
+    Bare field preCreated must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#profileBadge:
+    Bare field profileBadge must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#profileGroupId:
+    Bare field profileGroupId must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#restrictedProfileParentId:
+    Bare field restrictedProfileParentId must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#serialNumber:
+    Bare field serialNumber must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#userType:
+    Bare field userType must be marked final, or moved behind accessors if mutable
 MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#cache:
     
 MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbName:
@@ -2548,15 +2606,15 @@
 NoSettingsProvider: android.provider.Settings.Global#USE_OPEN_WIFI_PACKAGE:
     
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
-
+    
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY:
-
+    
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE:
-
+    
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_ALL:
-
+    
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN:
-
+    
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW:
     
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE:
@@ -2619,6 +2677,10 @@
     
 
 
+NullableCollection: android.os.UserManager#createProfileForUser(String, String, int, int, String[]) parameter #4:
+    Type of parameter disallowedPackages in android.os.UserManager.createProfileForUser(String name, String userType, int flags, int userId, String[] disallowedPackages) is a nullable collection (`java.lang.String[]`); must be non-null
+
+
 OnNameExpected: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.PrintWriter, String[]):
     
 OnNameExpected: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]):
@@ -2729,6 +2791,8 @@
 
 ParcelNotFinal: android.app.WindowConfiguration:
     
+ParcelNotFinal: android.content.pm.UserInfo:
+    Parcelable classes must be final: android.content.pm.UserInfo is not final
 ParcelNotFinal: android.net.metrics.IpConnectivityLog.Event:
     
 ParcelNotFinal: android.os.IncidentManager.IncidentReport:
diff --git a/core/java/android/app/ActivityManager.aidl b/core/java/android/app/ActivityManager.aidl
index 341393c..eb7cac07 100644
--- a/core/java/android/app/ActivityManager.aidl
+++ b/core/java/android/app/ActivityManager.aidl
@@ -17,6 +17,7 @@
 package android.app;
 
 parcelable ActivityManager.MemoryInfo;
+parcelable ActivityManager.PendingIntentInfo;
 parcelable ActivityManager.ProcessErrorStateInfo;
 parcelable ActivityManager.RecentTaskInfo;
 parcelable ActivityManager.TaskDescription;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 220c332..f905ec8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -511,6 +511,7 @@
     /** @hide Process is hosting the current top activities.  Note that this covers
      * all activities that are visible to the user. */
     @UnsupportedAppUsage
+    @TestApi
     public static final int PROCESS_STATE_TOP = ProcessStateEnum.TOP;
 
     /** @hide Process is bound to a TOP app. */
@@ -518,6 +519,7 @@
 
     /** @hide Process is hosting a foreground service. */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @TestApi
     public static final int PROCESS_STATE_FOREGROUND_SERVICE = ProcessStateEnum.FOREGROUND_SERVICE;
 
     /** @hide Process is hosting a foreground service due to a system binding. */
@@ -617,6 +619,7 @@
     public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 2;
 
     /** @hide Process can access network despite any power saving resrictions */
+    @TestApi
     public static final int PROCESS_CAPABILITY_NETWORK = 1 << 3;
 
     /** @hide all capabilities, the ORing of all flags in {@link ProcessCapability}*/
@@ -3432,6 +3435,36 @@
     }
 
     /**
+     * Returns the process state of this uid.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+    public int getUidProcessState(int uid) {
+        try {
+            return getService().getUidProcessState(uid, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the process capability of this uid.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+    public @ProcessCapability int getUidProcessCapabilities(int uid) {
+        try {
+            return getService().getUidProcessCapabilities(uid, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Return the importance of a given package name, based on the processes that are
      * currently running.  The return value is one of the importance constants defined
      * in {@link RunningAppProcessInfo}, giving you the highest importance of all the
@@ -4776,4 +4809,71 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * A subset of immutable pending intent information suitable for caching on the client side.
+     *
+     * @hide
+     */
+    public static final class PendingIntentInfo implements Parcelable {
+
+        private final String mCreatorPackage;
+        private final int mCreatorUid;
+        private final boolean mImmutable;
+        private final int mIntentSenderType;
+
+        public PendingIntentInfo(String creatorPackage, int creatorUid, boolean immutable,
+                int intentSenderType) {
+            mCreatorPackage = creatorPackage;
+            mCreatorUid = creatorUid;
+            mImmutable = immutable;
+            mIntentSenderType = intentSenderType;
+        }
+
+        public String getCreatorPackage() {
+            return mCreatorPackage;
+        }
+
+        public int getCreatorUid() {
+            return mCreatorUid;
+        }
+
+        public boolean isImmutable() {
+            return mImmutable;
+        }
+
+        public int getIntentSenderType() {
+            return mIntentSenderType;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel parcel, int flags) {
+            parcel.writeString(mCreatorPackage);
+            parcel.writeInt(mCreatorUid);
+            parcel.writeBoolean(mImmutable);
+            parcel.writeInt(mIntentSenderType);
+        }
+
+        public static final @NonNull Creator<PendingIntentInfo> CREATOR =
+                new Creator<PendingIntentInfo>() {
+                    @Override
+                    public PendingIntentInfo createFromParcel(Parcel in) {
+                        return new PendingIntentInfo(
+                                /* creatorPackage= */ in.readString(),
+                                /* creatorUid= */ in.readInt(),
+                                /* immutable= */ in.readBoolean(),
+                                /* intentSenderType= */ in.readInt());
+                    }
+
+                    @Override
+                    public PendingIntentInfo[] newArray(int size) {
+                        return new PendingIntentInfo[size];
+                    }
+                };
+    }
 }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index c31c22c..71f164f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -30,6 +30,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.PowerWhitelistManager.ReasonCode;
 import android.os.PowerWhitelistManager.TempAllowListType;
 import android.os.TransactionTooLargeException;
 import android.os.WorkSource;
@@ -102,12 +103,19 @@
      * Sets how long a {@link PendingIntent} can be temporarily allowlisted to bypass restrictions
      * such as Power Save mode.
      * @param target
-     * @param whitelistToken
+     * @param allowlistToken
      * @param duration temp allowlist duration in milliseconds.
      * @param type temp allowlist type defined at {@link TempAllowListType}
+     * @param reasonCode one of {@link ReasonCode}
+     * @param reason A human-readable reason for logging purposes.
      */
+    public abstract void setPendingIntentAllowlistDuration(IIntentSender target,
+            IBinder allowlistToken, long duration, @TempAllowListType int type,
+            @ReasonCode int reasonCode, @Nullable String reason);
+
+    @Deprecated
     public abstract void setPendingIntentWhitelistDuration(IIntentSender target,
-            IBinder whitelistToken, long duration, int type);
+            IBinder allowlistToken, long duration, @TempAllowListType int type);
 
     /**
      * Returns the flags set for a {@link PendingIntent}.
@@ -127,20 +135,26 @@
             IBinder allowlistToken);
 
     /**
-     * Allow DeviceIdleController to tell us about what apps are whitelisted.
+     * Allow DeviceIdleController to tell us about what apps are allowlisted.
      */
-    public abstract void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids);
+    public abstract void setDeviceIdleAllowlist(int[] allAppids, int[] exceptIdleAppids);
 
     /**
-     * Update information about which app IDs are on the temp whitelist.
+     * Update information about which app IDs are on the temp allowlist.
      * @param appids the updated list of appIds in temp allowlist.
      * @param changingUid uid to add or remove to temp allowlist.
      * @param adding true to add to temp allowlist, false to remove from temp allowlist.
      * @param durationMs when adding is true, the duration to be in temp allowlist.
      * @param type temp allowlist type defined at {@link TempAllowListType}.
+     * @param reasonCode one of {@link ReasonCode}
+     * @param reason A human-readable reason for logging purposes.
+     * @param callingUid the callingUid that setup this temp allowlist, only valid when param adding
+     *                   is true.
      */
-    public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingUid,
-            boolean adding, long durationMs, @TempAllowListType int type);
+    public abstract void updateDeviceIdleTempAllowlist(int[] appids, int changingUid,
+            boolean adding, long durationMs, @TempAllowListType int type,
+            @ReasonCode int reasonCode,
+            @Nullable String reason, int callingUid);
 
     /**
      * Get the procstate for the UID.  The return value will be between
@@ -335,10 +349,11 @@
      * @param targetUid the UID that is been temp allowlisted.
      * @param duration temp allowlist duration in milliseconds.
      * @param type temp allowlist type defined at {@link TempAllowListType}
-     * @param tag
+     * @param reasonCode one of {@link ReasonCode}
+     * @param reason
      */
-    public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
-            long duration, int type, String tag);
+    public abstract void tempAllowlistForPendingIntent(int callerPid, int callerUid, int targetUid,
+            long duration, int type, @ReasonCode int reasonCode, String reason);
 
     public abstract int broadcastIntentInPackage(String packageName, @Nullable String featureId,
             int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
@@ -495,9 +510,9 @@
 
     /**
      * Sends a broadcast, assuming the caller to be the system and allowing the inclusion of an
-     * approved whitelist of app Ids >= {@link android.os.Process#FIRST_APPLICATION_UID} that the
+     * approved allowlist of app Ids >= {@link android.os.Process#FIRST_APPLICATION_UID} that the
      * broadcast my be sent to; any app Ids < {@link android.os.Process#FIRST_APPLICATION_UID} are
-     * automatically whitelisted.
+     * automatically allowlisted.
      *
      * @see com.android.server.am.ActivityManagerService#broadcastIntentWithFeature(
      *      IApplicationThread, String, Intent, String, IIntentReceiver, int, String, Bundle,
diff --git a/core/java/android/app/AnrController.java b/core/java/android/app/AnrController.java
index cfc9d27..a0d4b3a 100644
--- a/core/java/android/app/AnrController.java
+++ b/core/java/android/app/AnrController.java
@@ -23,7 +23,29 @@
 public interface AnrController {
     /**
      * Returns the delay in milliseconds for an ANR dialog that is about to be shown for
-     * {@code packageName}.
+     * {@code packageName} with {@code uid}.
+     *
+     * Implementations should only return a positive value if they actually expect the
+     * {@code packageName} to be delayed due to them.
+
+     * If there are multiple controllers registered, the controller with the max delay will
+     * be selected and will receive an {@link #onAnrDelayStarted} callback at the start of the
+     * delay and an {@link #onAnrDelayCompleted} at the end of the delay.
      */
     long getAnrDelayMillis(String packageName, int uid);
+
+    /**
+     * Notifies the controller at the start of the ANR dialog delay for {@code packageName} with
+     * {@code uid}. The controller can decide to show a progress UI after this notification.
+     */
+    void onAnrDelayStarted(String packageName, int uid);
+
+    /**
+     * Notifies the controller at the end of the ANR dialog delay for {@code packageName} with
+     * {@code uid}.
+     *
+     * @return whether the ANR dialog should be shown or cancelled. {@code true} if the
+     * ANR dialog should be shown, {@code false} if it should be cancelled.
+     */
+    boolean onAnrDelayCompleted(String packageName, int uid);
 }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2f3b50b..160844a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1141,23 +1141,20 @@
      *
      * @hide
      */
-    // TODO: Add as AppProtoEnums
-    public static final int OP_PHONE_CALL_MICROPHONE = 100;
+    public static final int OP_PHONE_CALL_MICROPHONE = AppProtoEnums.APP_OP_PHONE_CALL_MICROPHONE;
     /**
      * Phone call is using camera
      *
      * @hide
      */
-    // TODO: Add as AppProtoEnums
-    public static final int OP_PHONE_CALL_CAMERA = 101;
+    public static final int OP_PHONE_CALL_CAMERA = AppProtoEnums.APP_OP_PHONE_CALL_CAMERA;
 
     /**
      * Audio is being recorded for hotword detection.
      *
      * @hide
      */
-    // TODO: Add as AppProtoEnums
-    public static final int OP_RECORD_AUDIO_HOTWORD = 102;
+    public static final int OP_RECORD_AUDIO_HOTWORD = AppProtoEnums.APP_OP_RECORD_AUDIO_HOTWORD;
 
     /**
      * Manage credentials in the system KeyChain.
@@ -1184,10 +1181,29 @@
      */
     public static final int OP_SCHEDULE_EXACT_ALARM = AppProtoEnums.APP_OP_SCHEDULE_EXACT_ALARM;
 
+    /**
+     * Fine location being accessed by a location source, which is
+     * a component that already has location data since it is the one
+     * that produces location, which is it is a data source for
+     * location data.
+     *
+     * @hide
+     */
+    public static final int OP_FINE_LOCATION_SOURCE = AppProtoEnums.APP_OP_FINE_LOCATION_SOURCE;
+
+    /**
+     * Coarse location being accessed by a location source, which is
+     * a component that already has location data since it is the one
+     * that produces location, which is it is a data source for
+     * location data.
+     *
+     * @hide
+     */
+    public static final int OP_COARSE_LOCATION_SOURCE = AppProtoEnums.APP_OP_COARSE_LOCATION_SOURCE;
 
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 108;
+    public static final int _NUM_OP = 110;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1567,6 +1583,24 @@
      */
     public static final String OPSTR_SCHEDULE_EXACT_ALARM = "android:schedule_exact_alarm";
 
+    /**
+     * Fine location being accessed by a location source, which is
+     * a component that already has location since it is the one that
+     * produces location.
+     *
+     * @hide
+     */
+    public static final String OPSTR_FINE_LOCATION_SOURCE = "android:fine_location_source";
+
+    /**
+     * Coarse location being accessed by a location source, which is
+     * a component that already has location since it is the one that
+     * produces location.
+     *
+     * @hide
+     */
+    public static final String OPSTR_COARSE_LOCATION_SOURCE = "android:coarse_location_source";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1767,6 +1801,8 @@
             OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
             OP_RECORD_AUDIO_OUTPUT,             // RECORD_AUDIO_OUTPUT
             OP_SCHEDULE_EXACT_ALARM,            // SCHEDULE_EXACT_ALARM
+            OP_FINE_LOCATION,                   // OP_FINE_LOCATION_SOURCE
+            OP_COARSE_LOCATION,                 // OP_COARSE_LOCATION_SOURCE
     };
 
     /**
@@ -1881,6 +1917,8 @@
             OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
             OPSTR_RECORD_AUDIO_OUTPUT,
             OPSTR_SCHEDULE_EXACT_ALARM,
+            OPSTR_FINE_LOCATION_SOURCE,
+            OPSTR_COARSE_LOCATION_SOURCE,
     };
 
     /**
@@ -1996,6 +2034,8 @@
             "USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER",
             "RECORD_AUDIO_OUTPUT",
             "SCHEDULE_EXACT_ALARM",
+            "FINE_LOCATION_SOURCE",
+            "COARSE_LOCATION_SOURCE",
     };
 
     /**
@@ -2112,6 +2152,8 @@
             Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
             null, // no permission for OP_RECORD_AUDIO_OUTPUT
             Manifest.permission.SCHEDULE_EXACT_ALARM,
+            null, // no permission for OP_ACCESS_FINE_LOCATION_SOURCE,
+            null, // no permission for OP_ACCESS_COARSE_LOCATION_SOURCE,
     };
 
     /**
@@ -2228,6 +2270,8 @@
             null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
             null, // RECORD_AUDIO_OUTPUT
             null, // SCHEDULE_EXACT_ALARM
+            null, // ACCESS_FINE_LOCATION_SOURCE
+            null, // ACCESS_COARSE_LOCATION_SOURCE
     };
 
     /**
@@ -2343,6 +2387,8 @@
             null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
             null, // RECORD_AUDIO_OUTPUT
             null, // SCHEDULE_EXACT_ALARM
+            null, // ACCESS_FINE_LOCATION_SOURCE
+            null, // ACCESS_COARSE_LOCATION_SOURCE
     };
 
     /**
@@ -2457,6 +2503,8 @@
             AppOpsManager.MODE_DEFAULT, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
             AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_OUTPUT
             AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM
+            AppOpsManager.MODE_ALLOWED, // ACCESS_FINE_LOCATION_SOURCE
+            AppOpsManager.MODE_ALLOWED, // ACCESS_COARSE_LOCATION_SOURCE
     };
 
     /**
@@ -2575,6 +2623,8 @@
             true, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
             false, // RECORD_AUDIO_OUTPUT
             false, // SCHEDULE_EXACT_ALARM
+            false, // ACCESS_FINE_LOCATION_SOURCE
+            false, // ACCESS_COARSE_LOCATION_SOURCE
     };
 
     /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 062cab4..a6260d6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1797,6 +1797,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) {
         mContext = context;
         mPM = pm;
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 445fdd8..2e06e9b 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -16,11 +16,13 @@
 
 package android.app;
 
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.PowerWhitelistManager;
+import android.os.PowerWhitelistManager.ReasonCode;
 import android.os.PowerWhitelistManager.TempAllowListType;
 
 /**
@@ -31,8 +33,11 @@
  */
 @SystemApi
 public class BroadcastOptions {
-    private long mTemporaryAppWhitelistDuration;
-    private @TempAllowListType int mTemporaryAppWhitelistType;
+    private long mTemporaryAppAllowlistDuration;
+    private @TempAllowListType int mTemporaryAppAllowlistType;
+    private @ReasonCode int mTemporaryAppAllowlistReasonCode =
+            PowerWhitelistManager.REASON_UNKNOWN;
+    private @Nullable String mTemporaryAppAllowlistReason;
     private int mMinManifestReceiverApiLevel = 0;
     private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
     private boolean mDontSendToRestrictedApps = false;
@@ -42,11 +47,17 @@
      * How long to temporarily put an app on the power allowlist when executing this broadcast
      * to it.
      */
-    static final String KEY_TEMPORARY_APP_WHITELIST_DURATION
-            = "android:broadcast.temporaryAppWhitelistDuration";
+    static final String KEY_TEMPORARY_APP_ALLOWLIST_DURATION
+            = "android:broadcast.temporaryAppAllowlistDuration";
 
-    static final String KEY_TEMPORARY_APP_WHITELIST_TYPE
-            = "android:broadcast.temporaryAppWhitelistType";
+    static final String KEY_TEMPORARY_APP_ALLOWLIST_TYPE
+            = "android:broadcast.temporaryAppAllowlistType";
+
+    static final String KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE =
+            "android:broadcast.temporaryAppAllowlistReasonCode";
+
+    static final String KEY_TEMPORARY_APP_ALLOWLIST_REASON =
+            "android:broadcast.temporaryAppAllowlistReason";
 
     /**
      * Corresponds to {@link #setMinManifestReceiverApiLevel}.
@@ -80,6 +91,7 @@
     @Deprecated
     public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED =
             PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+
     /**
      * @hide
      * @deprecated Use {@link android.os.PowerWhitelistManager#
@@ -99,8 +111,11 @@
 
     /** @hide */
     public BroadcastOptions(Bundle opts) {
-        mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION);
-        mTemporaryAppWhitelistType = opts.getInt(KEY_TEMPORARY_APP_WHITELIST_TYPE);
+        mTemporaryAppAllowlistDuration = opts.getLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION);
+        mTemporaryAppAllowlistType = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE);
+        mTemporaryAppAllowlistReasonCode = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE,
+                PowerWhitelistManager.REASON_UNKNOWN);
+        mTemporaryAppAllowlistReason = opts.getString(KEY_TEMPORARY_APP_ALLOWLIST_REASON);
         mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0);
         mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL,
                 Build.VERSION_CODES.CUR_DEVELOPMENT);
@@ -113,14 +128,16 @@
      * Set a duration for which the system should temporary place an application on the
      * power allowlist when this broadcast is being delivered to it.
      * @param duration The duration in milliseconds; 0 means to not place on allowlist.
+     * @deprecated use {@link #setTemporaryAppAllowlist(long, int, int,  String)} instead.
      */
+    @Deprecated
     @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
             android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
             android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
     public void setTemporaryAppWhitelistDuration(long duration) {
-        mTemporaryAppWhitelistDuration = duration;
-        mTemporaryAppWhitelistType =
-                PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+        setTemporaryAppAllowlist(duration,
+                PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                PowerWhitelistManager.REASON_UNKNOWN, null);
     }
 
     /**
@@ -129,29 +146,69 @@
      * type.
      * @param type one of {@link TempAllowListType}
      * @param duration the duration in milliseconds; 0 means to not place on allowlist.
+     * @deprecated use {@link #setTemporaryAppAllowlist(long, int, int,  String)} instead.
      */
+    @Deprecated
     @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
             android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
             android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
     public void setTemporaryAppWhitelistDuration(@TempAllowListType int type, long duration) {
-        mTemporaryAppWhitelistDuration = duration;
-        mTemporaryAppWhitelistType = type;
+        setTemporaryAppAllowlist(duration, type,
+                PowerWhitelistManager.REASON_UNKNOWN, null);
     }
 
     /**
-     * Return {@link #setTemporaryAppWhitelistDuration}.
-     * @hide
+     * Set a duration for which the system should temporary place an application on the
+     * power allowlist when this broadcast is being delivered to it, specify the temp allowlist
+     * type.
+     * @param duration the duration in milliseconds; 0 means to not place on allowlist.
+     * @param type one of {@link TempAllowListType}
+     * @param reasonCode one of {@link ReasonCode}, use
+     *                  {@link PowerWhitelistManager#REASON_UNKNOWN} if not sure.
+     * @param reason A human-readable reason explaining why the app is temp allowlisted. Only
+     *               used for logging purposes. Could be null or empty string.
      */
-    public long getTemporaryAppWhitelistDuration() {
-        return mTemporaryAppWhitelistDuration;
+    @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
+            android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
+            android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
+    public void setTemporaryAppAllowlist(long duration, @TempAllowListType int type,
+            @ReasonCode int reasonCode, @Nullable String reason) {
+        mTemporaryAppAllowlistDuration = duration;
+        mTemporaryAppAllowlistType = type;
+        mTemporaryAppAllowlistReasonCode = reasonCode;
+        mTemporaryAppAllowlistReason = reason;
     }
 
     /**
-     * Return {@link #mTemporaryAppWhitelistType}.
+     * Return {@link #setTemporaryAppAllowlist}.
      * @hide
      */
-    public @TempAllowListType int getTemporaryAppWhitelistType() {
-        return mTemporaryAppWhitelistType;
+    public long getTemporaryAppAllowlistDuration() {
+        return mTemporaryAppAllowlistDuration;
+    }
+
+    /**
+     * Return {@link #mTemporaryAppAllowlistType}.
+     * @hide
+     */
+    public @TempAllowListType int getTemporaryAppAllowlistType() {
+        return mTemporaryAppAllowlistType;
+    }
+
+    /**
+     * Return {@link #mTemporaryAppAllowlistReasonCode}.
+     * @hide
+     */
+    public @ReasonCode int getTemporaryAppAllowlistReasonCode() {
+        return mTemporaryAppAllowlistReasonCode;
+    }
+
+    /**
+     * Return {@link #mTemporaryAppAllowlistReason}.
+     * @hide
+     */
+    public @Nullable String getTemporaryAppAllowlistReason() {
+        return mTemporaryAppAllowlistReason;
     }
 
     /**
@@ -236,11 +293,17 @@
      */
     public Bundle toBundle() {
         Bundle b = new Bundle();
-        if (mTemporaryAppWhitelistDuration > 0) {
-            b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration);
+        if (mTemporaryAppAllowlistDuration > 0) {
+            b.putLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION, mTemporaryAppAllowlistDuration);
         }
-        if (mTemporaryAppWhitelistType != 0) {
-            b.putInt(KEY_TEMPORARY_APP_WHITELIST_TYPE, mTemporaryAppWhitelistType);
+        if (mTemporaryAppAllowlistType != 0) {
+            b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE, mTemporaryAppAllowlistType);
+        }
+        if (mTemporaryAppAllowlistReasonCode != PowerWhitelistManager.REASON_UNKNOWN) {
+            b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE, mTemporaryAppAllowlistReasonCode);
+        }
+        if (mTemporaryAppAllowlistReason != null) {
+            b.putString(KEY_TEMPORARY_APP_ALLOWLIST_REASON, mTemporaryAppAllowlistReason);
         }
         if (mMinManifestReceiverApiLevel != 0) {
             b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 4ad13e1..3a8172e 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.app.ActivityManager;
+import android.app.ActivityManager.PendingIntentInfo;
 import android.app.ActivityTaskManager;
 import android.app.ApplicationErrorReport;
 import android.app.ApplicationExitInfo;
@@ -248,7 +249,7 @@
             in IBinder token, in String resultWho, int requestCode, in Intent[] intents,
             in String[] resolvedTypes, int flags, in Bundle options, int userId);
     void cancelIntentSender(in IIntentSender sender);
-    String getPackageForIntentSender(in IIntentSender sender);
+    ActivityManager.PendingIntentInfo getInfoForIntentSender(in IIntentSender sender);
     void registerIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
     void unregisterIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
     void enterSafeMode();
@@ -293,7 +294,6 @@
             int operationType);
     void backupAgentCreated(in String packageName, in IBinder agent, int userId);
     void unbindBackupAgent(in ApplicationInfo appInfo);
-    int getUidForIntentSender(in IIntentSender sender);
     int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
             boolean requireFull, in String name, in String callerPackage);
     void addPackageDependency(in String packageName);
@@ -345,7 +345,6 @@
     @UnsupportedAppUsage
     void unregisterProcessObserver(in IProcessObserver observer);
     boolean isIntentSenderTargetedToPackage(in IIntentSender sender);
-    boolean isIntentSenderImmutable(in IIntentSender sender);
     @UnsupportedAppUsage
     void updatePersistentConfiguration(in Configuration values);
     void updatePersistentConfigurationWithAttribution(in Configuration values,
@@ -375,8 +374,6 @@
     void unstableProviderDied(in IBinder connection);
     @UnsupportedAppUsage
     boolean isIntentSenderAnActivity(in IIntentSender sender);
-    boolean isIntentSenderAForegroundService(in IIntentSender sender);
-    boolean isIntentSenderABroadcast(in IIntentSender sender);
     /** @deprecated Use {@link startActivityAsUserWithFeature} instead */
     @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@code android.content.Context#createContextAsUser(android.os.UserHandle, int)} and {@link android.content.Context#startActivity(android.content.Intent)} instead")
     int startActivityAsUser(in IApplicationThread caller, in String callingPackage,
@@ -552,7 +549,7 @@
     /**
      * Add a bare uid to the background restrictions whitelist.  Only the system uid may call this.
      */
-    void backgroundWhitelistUid(int uid);
+    void backgroundAllowlistUid(int uid);
 
     // Start of P transactions
     /**
@@ -711,5 +708,5 @@
     /** Called by PendingIntent.queryIntentComponents() */
     List<ResolveInfo> queryIntentComponentsForIntentSender(in IIntentSender sender, int matchFlags);
 
-    boolean isIntentSenderAService(in IIntentSender sender);
+    int getUidProcessCapabilities(int uid, in String callingPackage);
 }
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index b1c39d3..7bb5d9a 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -60,9 +60,9 @@
     void onActivityForcedResizable(String packageName, int taskId, int reason);
 
     /**
-     * Called when we launched an activity that dismissed the docked stack.
+     * Called when we launched an activity that dismissed the docked task.
      */
-    void onActivityDismissingDockedStack();
+    void onActivityDismissingDockedTask();
 
     /**
      * Called when an activity was requested to be launched on a secondary display but was not
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 899cdb5..f7304fb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1371,6 +1371,18 @@
     public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
 
     /**
+     * {@link #extras} key: the color used as a hint for the Answer action button of a
+     * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}.
+     */
+    public static final String EXTRA_ANSWER_COLOR = "android.answerColor";
+
+    /**
+     * {@link #extras} key: the color used as a hint for the Decline or Hang Up action button of a
+     * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}.
+     */
+    public static final String EXTRA_DECLINE_COLOR = "android.declineColor";
+
+    /**
      * {@link #extras} key: whether the notification should be colorized as
      * supplied to {@link Builder#setColorized(boolean)}.
      */
@@ -5444,6 +5456,11 @@
             return p.allowColorization && mN.isColorized();
         }
 
+        private boolean isCallActionColorCustomizable(StandardTemplateParams p) {
+            return isColorized(p) && mContext.getResources().getBoolean(
+                    R.bool.config_callNotificationActionColorsRequireColorized);
+        }
+
         private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) {
             if (mN.mSmallIcon == null && mN.icon != 0) {
                 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
@@ -9066,6 +9083,8 @@
         private PendingIntent mAnswerIntent;
         private PendingIntent mDeclineIntent;
         private PendingIntent mHangUpIntent;
+        private Integer mAnswerButtonColor;
+        private Integer mDeclineButtonColor;
         private Icon mVerificationIcon;
         private CharSequence mVerificationText;
 
@@ -9176,6 +9195,28 @@
         }
 
         /**
+         * Optional color to be used as a hint for the Answer action button's color.
+         * The system may change this color to ensure sufficient contrast with the background.
+         * The system may choose to disregard this hint if the notification is not colorized.
+         */
+        @NonNull
+        public CallStyle setAnswerButtonColorHint(@ColorInt int color) {
+            mAnswerButtonColor = color;
+            return this;
+        }
+
+        /**
+         * Optional color to be used as a hint for the Decline or Hang Up action button's color.
+         * The system may change this color to ensure sufficient contrast with the background.
+         * The system may choose to disregard this hint if the notification is not colorized.
+         */
+        @NonNull
+        public CallStyle setDeclineButtonColorHint(@ColorInt int color) {
+            mDeclineButtonColor = color;
+            return this;
+        }
+
+        /**
          * @hide
          */
         public boolean displayCustomViewInline() {
@@ -9234,40 +9275,47 @@
         }
 
         @NonNull
-        private Action makeNegativeAction() {
+        private Action makeNegativeAction(@NonNull StandardTemplateParams p) {
             if (mDeclineIntent == null) {
-                return makeAction(R.drawable.ic_call_decline,
+                return makeAction(p, R.drawable.ic_call_decline,
                         R.string.call_notification_hang_up_action,
-                        R.color.call_notification_decline_color, mHangUpIntent);
+                        mDeclineButtonColor, R.color.call_notification_decline_color,
+                        mHangUpIntent);
             } else {
-                return makeAction(R.drawable.ic_call_decline,
+                return makeAction(p, R.drawable.ic_call_decline,
                         R.string.call_notification_decline_action,
-                        R.color.call_notification_decline_color, mDeclineIntent);
+                        mDeclineButtonColor, R.color.call_notification_decline_color,
+                        mDeclineIntent);
             }
         }
 
         @Nullable
-        private Action makeAnswerAction() {
-            return mAnswerIntent == null ? null : makeAction(R.drawable.ic_call_answer,
+        private Action makeAnswerAction(@NonNull StandardTemplateParams p) {
+            return mAnswerIntent == null ? null : makeAction(p, R.drawable.ic_call_answer,
                     R.string.call_notification_answer_action,
-                    R.color.call_notification_answer_color, mAnswerIntent);
+                    mAnswerButtonColor, R.color.call_notification_answer_color,
+                    mAnswerIntent);
         }
 
         @NonNull
-        private Action makeAction(@DrawableRes int icon, @StringRes int title,
-                @ColorRes int colorRes, PendingIntent intent) {
+        private Action makeAction(@NonNull StandardTemplateParams p,
+                @DrawableRes int icon, @StringRes int title,
+                @ColorInt Integer colorInt, @ColorRes int defaultColorRes, PendingIntent intent) {
+            if (colorInt == null || !mBuilder.isCallActionColorCustomizable(p)) {
+                colorInt = mBuilder.mContext.getColor(defaultColorRes);
+            }
             Action action = new Action.Builder(Icon.createWithResource("", icon),
                     new SpannableStringBuilder().append(mBuilder.mContext.getString(title),
-                            new ForegroundColorSpan(mBuilder.mContext.getColor(colorRes)),
+                            new ForegroundColorSpan(colorInt),
                             SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE),
                     intent).build();
             action.getExtras().putBoolean(KEY_ACTION_PRIORITY, true);
             return action;
         }
 
-        private ArrayList<Action> makeActionsList() {
-            final Action negativeAction = makeNegativeAction();
-            final Action answerAction = makeAnswerAction();
+        private ArrayList<Action> makeActionsList(@NonNull StandardTemplateParams p) {
+            final Action negativeAction = makeNegativeAction(p);
+            final Action answerAction = makeAnswerAction(p);
 
             ArrayList<Action> actions = new ArrayList<>(MAX_ACTION_BUTTONS);
             final Action lastAction;
@@ -9356,7 +9404,7 @@
 
             // Create the buttons for the generated actions list.
             int i = 0;
-            for (Action action : makeActionsList()) {
+            for (Action action : makeActionsList(p)) {
                 final RemoteViews button = mBuilder.generateActionButton(action, emphasizedMode, p);
                 if (i > 0) {
                     // Clear start margin from non-first buttons to reduce the gap between buttons.
@@ -9421,6 +9469,12 @@
             if (mHangUpIntent != null) {
                 extras.putParcelable(EXTRA_HANG_UP_INTENT, mHangUpIntent);
             }
+            if (mAnswerButtonColor != null) {
+                extras.putInt(EXTRA_ANSWER_COLOR, mAnswerButtonColor);
+            }
+            if (mDeclineButtonColor != null) {
+                extras.putInt(EXTRA_DECLINE_COLOR, mDeclineButtonColor);
+            }
             fixTitleAndTextExtras(extras);
         }
 
@@ -9447,6 +9501,10 @@
             mAnswerIntent = extras.getParcelable(EXTRA_ANSWER_INTENT);
             mDeclineIntent = extras.getParcelable(EXTRA_DECLINE_INTENT);
             mHangUpIntent = extras.getParcelable(EXTRA_HANG_UP_INTENT);
+            mAnswerButtonColor = extras.containsKey(EXTRA_ANSWER_COLOR)
+                    ? extras.getInt(EXTRA_ANSWER_COLOR) : null;
+            mDeclineButtonColor = extras.containsKey(EXTRA_DECLINE_COLOR)
+                    ? extras.getInt(EXTRA_DECLINE_COLOR) : null;
         }
 
         /**
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 671315f..549bd4b 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -16,6 +16,11 @@
 
 package android.app;
 
+import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
+import static android.app.ActivityManager.INTENT_SENDER_BROADCAST;
+import static android.app.ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.INTENT_SENDER_SERVICE;
+
 import android.Manifest.permission;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -25,6 +30,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemApi.Client;
 import android.annotation.TestApi;
+import android.app.ActivityManager.PendingIntentInfo;
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
@@ -55,6 +61,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * A description of an Intent and target action to perform with it.  Instances
@@ -122,6 +129,9 @@
     private IBinder mWhitelistToken;
     private ArraySet<CancelListener> mCancelListeners;
 
+    // cached pending intent information
+    private @Nullable PendingIntentInfo mCachedInfo;
+
     /**
      * It is now required to specify either {@link #FLAG_IMMUTABLE}
      * or {@link #FLAG_MUTABLE} when creating a PendingIntent.
@@ -455,15 +465,14 @@
     public static PendingIntent getActivityAsUser(Context context, int requestCode,
             @NonNull Intent intent, int flags, Bundle options, UserHandle user) {
         String packageName = context.getPackageName();
-        String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
-                context.getContentResolver()) : null;
+        String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
         checkFlags(flags, packageName);
         try {
             intent.migrateExtraStreamToClipData(context);
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
                 ActivityManager.getService().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                    INTENT_SENDER_ACTIVITY, packageName,
                     context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, options, user.getIdentifier());
@@ -596,7 +605,7 @@
         try {
             IIntentSender target =
                 ActivityManager.getService().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                    INTENT_SENDER_ACTIVITY, packageName,
                     context.getAttributionTag(), null, null, requestCode, intents, resolvedTypes,
                     flags, options, user.getIdentifier());
             return target != null ? new PendingIntent(target) : null;
@@ -630,7 +639,7 @@
      */
     @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
     public static PendingIntent getBroadcast(Context context, int requestCode,
-            Intent intent, @Flags int flags) {
+            @NonNull Intent intent, @Flags int flags) {
         return getBroadcastAsUser(context, requestCode, intent, flags, context.getUser());
     }
 
@@ -643,14 +652,13 @@
     public static PendingIntent getBroadcastAsUser(Context context, int requestCode,
             Intent intent, int flags, UserHandle userHandle) {
         String packageName = context.getPackageName();
-        String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
-                context.getContentResolver()) : null;
+        String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
         checkFlags(flags, packageName);
         try {
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
                 ActivityManager.getService().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_BROADCAST, packageName,
+                    INTENT_SENDER_BROADCAST, packageName,
                     context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, null, userHandle.getIdentifier());
@@ -687,7 +695,7 @@
     public static PendingIntent getService(Context context, int requestCode,
             @NonNull Intent intent, @Flags int flags) {
         return buildServicePendingIntent(context, requestCode, intent, flags,
-                ActivityManager.INTENT_SENDER_SERVICE);
+                INTENT_SENDER_SERVICE);
     }
 
     /**
@@ -717,14 +725,13 @@
     public static PendingIntent getForegroundService(Context context, int requestCode,
             @NonNull Intent intent, @Flags int flags) {
         return buildServicePendingIntent(context, requestCode, intent, flags,
-                ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE);
+                INTENT_SENDER_FOREGROUND_SERVICE);
     }
 
     private static PendingIntent buildServicePendingIntent(Context context, int requestCode,
             Intent intent, int flags, int serviceKind) {
         String packageName = context.getPackageName();
-        String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
-                context.getContentResolver()) : null;
+        String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
         checkFlags(flags, packageName);
         try {
             intent.prepareToLeaveProcess(context);
@@ -746,6 +753,7 @@
      * @return Returns a IntentSender object that wraps the sender of PendingIntent
      *
      */
+    @NonNull
     public IntentSender getIntentSender() {
         return new IntentSender(mTarget, mWhitelistToken);
     }
@@ -758,6 +766,7 @@
         try {
             ActivityManager.getService().cancelIntentSender(mTarget);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -1000,13 +1009,9 @@
      * @deprecated Renamed to {@link #getCreatorPackage()}.
      */
     @Deprecated
+    @NonNull
     public String getTargetPackage() {
-        try {
-            return ActivityManager.getService()
-                .getPackageForIntentSender(mTarget);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getCreatorPackage();
     }
 
     /**
@@ -1024,17 +1029,11 @@
      * only use this information to identify who you expect to be interacting with
      * through a {@link #send} call, not who gave you the PendingIntent.</p>
      *
-     * @return The package name of the PendingIntent, or null if there is
-     * none associated with it.
+     * @return The package name of the PendingIntent.
      */
-    @Nullable
+    @NonNull
     public String getCreatorPackage() {
-        try {
-            return ActivityManager.getService()
-                .getPackageForIntentSender(mTarget);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getCachedInfo().getCreatorPackage();
     }
 
     /**
@@ -1056,12 +1055,7 @@
      * none associated with it.
      */
     public int getCreatorUid() {
-        try {
-            return ActivityManager.getService()
-                .getUidForIntentSender(mTarget);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getCachedInfo().getCreatorUid();
     }
 
     /**
@@ -1149,18 +1143,12 @@
      * only use this information to identify who you expect to be interacting with
      * through a {@link #send} call, not who gave you the PendingIntent.</p>
      *
-     * @return The user handle of the PendingIntent, or null if there is
-     * none associated with it.
+     * @return The user handle of the PendingIntent
      */
-    @Nullable
+    @NonNull
     public UserHandle getCreatorUserHandle() {
-        try {
-            int uid = ActivityManager.getService()
-                .getUidForIntentSender(mTarget);
-            return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        int uid = getCachedInfo().getCreatorUid();
+        return UserHandle.getUserHandleForUid(uid);
     }
 
     /**
@@ -1180,12 +1168,7 @@
      * Check if this PendingIntent is marked with {@link #FLAG_IMMUTABLE}.
      */
     public boolean isImmutable() {
-        try {
-            return ActivityManager.getService()
-                    .isIntentSenderImmutable(mTarget);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getCachedInfo().isImmutable();
     }
 
     /**
@@ -1193,48 +1176,28 @@
      * {@link #getActivity} or {@link #getActivities}.
      */
     public boolean isActivity() {
-        try {
-            return ActivityManager.getService()
-                .isIntentSenderAnActivity(mTarget);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getCachedInfo().getIntentSenderType() == INTENT_SENDER_ACTIVITY;
     }
 
     /**
      * @return TRUE if this {@link PendingIntent} was created with {@link #getForegroundService}.
      */
     public boolean isForegroundService() {
-        try {
-            return ActivityManager.getService()
-                    .isIntentSenderAForegroundService(mTarget);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getCachedInfo().getIntentSenderType() == INTENT_SENDER_FOREGROUND_SERVICE;
     }
 
     /**
      * @return TRUE if this {@link PendingIntent} was created with {@link #getService}.
      */
     public boolean isService() {
-        try {
-            return ActivityManager.getService()
-                    .isIntentSenderAService(mTarget);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getCachedInfo().getIntentSenderType() == INTENT_SENDER_SERVICE;
     }
 
     /**
      * @return TRUE if this {@link PendingIntent} was created with {@link #getBroadcast}.
      */
     public boolean isBroadcast() {
-        try {
-            return ActivityManager.getService()
-                .isIntentSenderABroadcast(mTarget);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getCachedInfo().getIntentSenderType() == INTENT_SENDER_BROADCAST;
     }
 
     /**
@@ -1318,7 +1281,7 @@
         sb.append("PendingIntent{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
         sb.append(": ");
-        sb.append(mTarget != null ? mTarget.asBinder() : null);
+        sb.append(mTarget.asBinder());
         sb.append('}');
         return sb.toString();
     }
@@ -1326,9 +1289,7 @@
     /** @hide */
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        if (mTarget != null) {
-            proto.write(PendingIntentProto.TARGET, mTarget.asBinder().toString());
-        }
+        proto.write(PendingIntentProto.TARGET, mTarget.asBinder().toString());
         proto.end(token);
     }
 
@@ -1345,8 +1306,7 @@
 
     }
 
-    public static final @android.annotation.NonNull Parcelable.Creator<PendingIntent> CREATOR
-            = new Parcelable.Creator<PendingIntent>() {
+    public static final @NonNull Creator<PendingIntent> CREATOR = new Creator<PendingIntent>() {
         public PendingIntent createFromParcel(Parcel in) {
             IBinder target = in.readStrongBinder();
             return target != null
@@ -1400,11 +1360,11 @@
      * @hide
      */
     public PendingIntent(IIntentSender target) {
-        mTarget = target;
+        mTarget = Objects.requireNonNull(target);
     }
 
     /*package*/ PendingIntent(IBinder target, Object cookie) {
-        mTarget = IIntentSender.Stub.asInterface(target);
+        mTarget = Objects.requireNonNull(IIntentSender.Stub.asInterface(target));
         if (cookie != null) {
             mWhitelistToken = (IBinder)cookie;
         }
@@ -1433,4 +1393,16 @@
          */
         void onCancelled(PendingIntent intent);
     }
+
+    private PendingIntentInfo getCachedInfo() {
+        if (mCachedInfo == null) {
+            try {
+                mCachedInfo = ActivityManager.getService().getInfoForIntentSender(mTarget);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        return mCachedInfo;
+    }
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index dd5a9a8..e16e40b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -47,6 +47,7 @@
 import android.app.usage.NetworkStatsManager;
 import android.app.usage.StorageStatsManager;
 import android.app.usage.UsageStatsManager;
+import android.apphibernation.AppHibernationManager;
 import android.appwidget.AppWidgetManager;
 import android.bluetooth.BluetoothManager;
 import android.companion.CompanionDeviceManager;
@@ -1378,6 +1379,13 @@
                         IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE);
                         return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b));
                     }});
+        registerService(Context.APP_HIBERNATION_SERVICE, AppHibernationManager.class,
+                new CachedServiceFetcher<AppHibernationManager>() {
+                    @Override
+                    public AppHibernationManager createService(ContextImpl ctx) {
+                        IBinder b = ServiceManager.getService(Context.APP_HIBERNATION_SERVICE);
+                        return b == null ? null : new AppHibernationManager(ctx);
+                    }});
         registerService(Context.DREAM_SERVICE, DreamManager.class,
                 new CachedServiceFetcher<DreamManager>() {
                     @Override
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 1e38230..f523a7d 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -43,7 +43,7 @@
 
     @Override
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public void onActivityPinned(String packageName, int userId, int taskId, int stackId)
+    public void onActivityPinned(String packageName, int userId, int taskId, int rootTaskId)
             throws RemoteException {
     }
 
@@ -66,7 +66,7 @@
 
     @Override
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public void onActivityDismissingDockedStack() throws RemoteException {
+    public void onActivityDismissingDockedTask() throws RemoteException {
     }
 
     @Override
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index 2d203f57..3abba43 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -30,14 +30,17 @@
 import android.util.Size;
 
 import com.android.internal.graphics.ColorUtils;
+import com.android.internal.graphics.palette.CelebiQuantizer;
 import com.android.internal.graphics.palette.Palette;
-import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
 import com.android.internal.util.ContrastColorUtil;
 
 import java.io.FileOutputStream;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * Provides information about the colors of a wallpaper.
@@ -94,16 +97,21 @@
     private static final float DARK_PIXEL_CONTRAST = 6f;
     private static final float MAX_DARK_AREA = 0.025f;
 
-    private final ArrayList<Color> mMainColors;
+    private final List<Color> mMainColors;
+    private final Map<Integer, Integer> mAllColors;
     private int mColorHints;
 
     public WallpaperColors(Parcel parcel) {
         mMainColors = new ArrayList<>();
+        mAllColors = new HashMap<>();
         final int count = parcel.readInt();
         for (int i = 0; i < count; i++) {
             final int colorInt = parcel.readInt();
             Color color = Color.valueOf(colorInt);
             mMainColors.add(color);
+
+            final int population = parcel.readInt();
+            mAllColors.put(colorInt, population);
         }
         mColorHints = parcel.readInt();
     }
@@ -166,39 +174,22 @@
         }
 
         final Palette palette = Palette
-                .from(bitmap)
-                .setQuantizer(new VariationalKMeansQuantizer())
-                .maximumColorCount(5)
-                .clearFilters()
+                .from(bitmap, new CelebiQuantizer())
+                .maximumColorCount(256)
                 .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA)
                 .generate();
-
         // Remove insignificant colors and sort swatches by population
         final ArrayList<Palette.Swatch> swatches = new ArrayList<>(palette.getSwatches());
-        final float minColorArea = bitmap.getWidth() * bitmap.getHeight() * MIN_COLOR_OCCURRENCE;
-        swatches.removeIf(s -> s.getPopulation() < minColorArea);
         swatches.sort((a, b) -> b.getPopulation() - a.getPopulation());
 
         final int swatchesSize = swatches.size();
-        Color primary = null, secondary = null, tertiary = null;
 
-        swatchLoop:
+        final Map<Integer, Integer> populationByColor = new HashMap<>();
         for (int i = 0; i < swatchesSize; i++) {
-            Color color = Color.valueOf(swatches.get(i).getRgb());
-            switch (i) {
-                case 0:
-                    primary = color;
-                    break;
-                case 1:
-                    secondary = color;
-                    break;
-                case 2:
-                    tertiary = color;
-                    break;
-                default:
-                    // out of bounds
-                    break swatchLoop;
-            }
+            Palette.Swatch swatch = swatches.get(i);
+            int colorInt = swatch.getInt();
+            populationByColor.put(colorInt, swatch.getPopulation());
+
         }
 
         int hints = calculateDarkHints(bitmap);
@@ -207,7 +198,7 @@
             bitmap.recycle();
         }
 
-        return new WallpaperColors(primary, secondary, tertiary, HINT_FROM_BITMAP | hints);
+        return new WallpaperColors(populationByColor, HINT_FROM_BITMAP | hints);
     }
 
     /**
@@ -253,9 +244,13 @@
         }
 
         mMainColors = new ArrayList<>(3);
+        mAllColors = new HashMap<>();
+
         mMainColors.add(primaryColor);
+        mAllColors.put(primaryColor.toArgb(), 0);
         if (secondaryColor != null) {
             mMainColors.add(secondaryColor);
+            mAllColors.put(secondaryColor.toArgb(), 0);
         }
         if (tertiaryColor != null) {
             if (secondaryColor == null) {
@@ -263,8 +258,32 @@
                         + "secondaryColor is null");
             }
             mMainColors.add(tertiaryColor);
+            mAllColors.put(tertiaryColor.toArgb(), 0);
         }
+        mColorHints = colorHints;
+    }
 
+    /**
+     * Constructs a new object from a set of colors, where hints can be specified.
+     *
+     * @param populationByColor Map with keys of colors, and value representing the number of
+     *                          occurrences of color in the wallpaper.
+     * @param colorHints        A combination of WallpaperColor hints.
+     * @hide
+     * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
+     * @see WallpaperColors#fromBitmap(Bitmap)
+     * @see WallpaperColors#fromDrawable(Drawable)
+     */
+    public WallpaperColors(@NonNull Map<Integer, Integer> populationByColor, int colorHints) {
+        mAllColors = populationByColor;
+
+        ArrayList<Map.Entry<Integer, Integer>> mapEntries = new ArrayList(
+                populationByColor.entrySet());
+        mapEntries.sort((a, b) ->
+                a.getValue().compareTo(b.getValue())
+        );
+        mMainColors = mapEntries.stream().map(entry -> Color.valueOf(entry.getKey())).collect(
+                Collectors.toList());
         mColorHints = colorHints;
     }
 
@@ -293,6 +312,9 @@
         for (int i = 0; i < count; i++) {
             Color color = mainColors.get(i);
             dest.writeInt(color.toArgb());
+            Integer population = mAllColors.get(color.toArgb());
+            int populationInt = (population != null) ? population : 0;
+            dest.writeInt(populationInt);
         }
         dest.writeInt(mColorHints);
     }
@@ -336,6 +358,17 @@
         return Collections.unmodifiableList(mMainColors);
     }
 
+    /**
+     * Map of all colors. Key is rgb integer, value is importance of color.
+     *
+     * @return List of colors.
+     * @hide
+     */
+    public @NonNull Map<Integer, Integer> getAllColors() {
+        return Collections.unmodifiableMap(mAllColors);
+    }
+
+
     @Override
     public boolean equals(@Nullable Object o) {
         if (o == null || getClass() != o.getClass()) {
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 4ae1670..d04ca1d9 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -57,7 +57,7 @@
      * TODO: Investigate combining with {@link #mAppBounds}. Can the latter be a product of the
      * former?
      */
-    private Rect mBounds = new Rect();
+    private final Rect mBounds = new Rect();
 
     /**
      * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of
@@ -71,7 +71,7 @@
      * The maximum {@link Rect} bounds that an app can expect. It is used to report value of
      * {@link WindowManager#getMaximumWindowMetrics()}.
      */
-    private Rect mMaxBounds = new Rect();
+    private final Rect mMaxBounds = new Rect();
 
     /**
      * The current rotation of this window container relative to the default
@@ -240,9 +240,9 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mBounds, flags);
-        dest.writeParcelable(mAppBounds, flags);
-        dest.writeParcelable(mMaxBounds, flags);
+        mBounds.writeToParcel(dest, flags);
+        dest.writeTypedObject(mAppBounds, flags);
+        mMaxBounds.writeToParcel(dest, flags);
         dest.writeInt(mWindowingMode);
         dest.writeInt(mActivityType);
         dest.writeInt(mAlwaysOnTop);
@@ -250,10 +250,11 @@
         dest.writeInt(mDisplayWindowingMode);
     }
 
-    private void readFromParcel(Parcel source) {
-        mBounds = source.readParcelable(Rect.class.getClassLoader());
-        mAppBounds = source.readParcelable(Rect.class.getClassLoader());
-        mMaxBounds = source.readParcelable(Rect.class.getClassLoader());
+    /** @hide */
+    public void readFromParcel(@NonNull Parcel source) {
+        mBounds.readFromParcel(source);
+        mAppBounds = source.readTypedObject(Rect.CREATOR);
+        mMaxBounds.readFromParcel(source);
         mWindowingMode = source.readInt();
         mActivityType = source.readInt();
         mAlwaysOnTop = source.readInt();
@@ -693,9 +694,7 @@
         }
         protoOutputStream.write(WINDOWING_MODE, mWindowingMode);
         protoOutputStream.write(ACTIVITY_TYPE, mActivityType);
-        if (mBounds != null) {
-            mBounds.dumpDebug(protoOutputStream, BOUNDS);
-        }
+        mBounds.dumpDebug(protoOutputStream, BOUNDS);
         mMaxBounds.dumpDebug(protoOutputStream, MAX_BOUNDS);
         protoOutputStream.end(token);
     }
@@ -719,11 +718,9 @@
                         mAppBounds.readFromProto(proto, APP_BOUNDS);
                         break;
                     case (int) BOUNDS:
-                        mBounds = new Rect();
                         mBounds.readFromProto(proto, BOUNDS);
                         break;
                     case (int) MAX_BOUNDS:
-                        mMaxBounds = new Rect();
                         mMaxBounds.readFromProto(proto, MAX_BOUNDS);
                         break;
                     case (int) WINDOWING_MODE:
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 59e5144..bb1ff60 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2746,6 +2746,32 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface PersonalAppsSuspensionReason {}
 
+    /**
+     * The default device owner type for a managed device.
+     *
+     * @hide
+     */
+    public static final int DEVICE_OWNER_TYPE_DEFAULT = 0;
+
+    /**
+     * The device owner type for a financed device.
+     *
+     * @hide
+     */
+    public static final int DEVICE_OWNER_TYPE_FINANCED = 1;
+
+    /**
+     * Different device owner types for a managed device.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "DEVICE_OWNER_TYPE_" }, value = {
+            DEVICE_OWNER_TYPE_DEFAULT,
+            DEVICE_OWNER_TYPE_FINANCED
+    })
+    public @interface DeviceOwnerType {}
+
     /** @hide */
     @TestApi
     public static final int OPERATION_LOCK_NOW = 1;
@@ -7276,7 +7302,12 @@
     /**
      * @hide
      */
+    @TestApi
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MANAGE_DEVICE_ADMINS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
+    })
     public void setActiveAdmin(@NonNull ComponentName policyReceiver, boolean refreshing,
             int userHandle) {
         if (mService != null) {
@@ -7453,8 +7484,10 @@
      * @throws IllegalArgumentException if the package name is null or invalid
      * @throws IllegalStateException If the preconditions mentioned are not met.
      */
-    public boolean setDeviceOwner(ComponentName who, String ownerName, int userId)
-            throws IllegalArgumentException, IllegalStateException {
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+    public boolean setDeviceOwner(
+            @NonNull ComponentName who, @Nullable String ownerName, @UserIdInt int userId) {
         if (mService != null) {
             try {
                 return mService.setDeviceOwner(who, ownerName, userId);
@@ -7521,7 +7554,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
+    })
     public ComponentName getDeviceOwnerComponentOnAnyUser() {
         return getDeviceOwnerComponentInner(/* callingUserOnly =*/ false);
     }
@@ -10477,9 +10513,10 @@
 
     /**
      * Reset record of previous system update freeze period the device went through.
-     * Only callable by ADB.
      * @hide
      */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD)
     public void clearSystemUpdatePolicyFreezePeriodRecord() {
         throwIfParentInstance("clearSystemUpdatePolicyFreezePeriodRecord");
         if (mService == null) {
@@ -11207,9 +11244,11 @@
 
     /**
      * Makes all accumulated network logs available to DPC in a new batch.
-     * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
+     * If throttled, returns time to wait in milliseconds, otherwise 0.
      * @hide
      */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS)
     public long forceNetworkLogs() {
         if (mService == null) {
             return -1;
@@ -11223,9 +11262,11 @@
 
     /**
      * Forces a batch of security logs to be fetched from logd and makes it available for DPC.
-     * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
+     * If throttled, returns time to wait in milliseconds, otherwise 0.
      * @hide
      */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS)
     public long forceSecurityLogs() {
         if (mService == null) {
             return 0;
@@ -11657,7 +11698,10 @@
      * @throws SecurityException if the caller is not shell / root or the admin package
      *         isn't a test application see {@link ApplicationInfo#FLAG_TEST_APP}.
      */
-    public void forceRemoveActiveAdmin(ComponentName adminReceiver, int userHandle) {
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+    public void forceRemoveActiveAdmin(
+            @NonNull ComponentName adminReceiver, @UserIdInt int userHandle) {
         try {
             mService.forceRemoveActiveAdmin(adminReceiver, userHandle);
         } catch (RemoteException re) {
@@ -12727,8 +12771,11 @@
      *
      * @hide
      */
-    @RequiresPermission(value = android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED,
-            conditional = true)
+    @TestApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED,
+            android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
+    }, conditional = true)
     public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull ComponentName who) {
         if (mService == null) {
             return;
@@ -13460,6 +13507,57 @@
     }
 
     /**
+     * Sets the device owner type for a managed device (e.g. financed device).
+     *
+     * @param admin The {@link DeviceAdminReceiver} that is the device owner.
+     * @param deviceOwnerType The device owner type is set to. Use
+     * {@link #DEVICE_OWNER_TYPE_DEFAULT} for the default device owner type. Use
+     * {@link #DEVICE_OWNER_TYPE_FINANCED} for the financed device owner type.
+     *
+     * @throws IllegalStateException When admin is not the device owner, or there is no device
+     *     owner, or attempting to set the device owner type again for the same admin.
+     * @throws SecurityException If the caller does not have the permission
+     *     {@link permission#MANAGE_PROFILE_AND_DEVICE_OWNERS}.
+     *
+     * @hide
+     */
+    public void setDeviceOwnerType(@NonNull ComponentName admin,
+            @DeviceOwnerType int deviceOwnerType) {
+        throwIfParentInstance("setDeviceOwnerType");
+        if (mService != null) {
+            try {
+                mService.setDeviceOwnerType(admin, deviceOwnerType);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the device owner type for the admin used in
+     * {@link #setDeviceOwnerType(ComponentName, int)}. {@link #DEVICE_OWNER_TYPE_DEFAULT}
+     * would be returned when the device owner type is not set for the device owner admin.
+     *
+     * @param admin The {@link DeviceAdminReceiver} that is the device owner.
+     *
+     * @throws IllegalStateException When admin is not the device owner or there is no device owner.
+     *
+     * @hide
+     */
+    @DeviceOwnerType
+    public int getDeviceOwnerType(@NonNull ComponentName admin) {
+        throwIfParentInstance("getDeviceOwnerType");
+        if (mService != null) {
+            try {
+                return mService.getDeviceOwnerType(admin);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        return DEVICE_OWNER_TYPE_DEFAULT;
+    }
+
+    /**
      * Called by device owner or profile owner of an organization-owned managed profile to
      * enable or disable USB data signaling for the device. When disabled, USB data connections
      * (except from charging functions) are prohibited.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 8a87b16..ac1592d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -503,6 +503,9 @@
     UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
     void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage);
 
+    void setDeviceOwnerType(in ComponentName admin, in int deviceOwnerType);
+    int getDeviceOwnerType(in ComponentName admin);
+
     void resetDefaultCrossProfileIntentFilters(int userId);
     boolean canAdminGrantSensorsPermissionsForUser(int userId);
 
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 673de8f..dae565e 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -361,36 +361,7 @@
             try {
                 // All packages, current transport
                 IRestoreSession binder =
-                        sService.beginRestoreSessionForUser(mContext.getUserId(), null, null,
-                                OperationType.BACKUP);
-                if (binder != null) {
-                    session = new RestoreSession(mContext, binder);
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "beginRestoreSession() couldn't connect");
-            }
-        }
-        return session;
-    }
-
-    /**
-     * Begin the process of restoring data from backup.  See the
-     * {@link android.app.backup.RestoreSession} class for documentation on that process.
-     *
-     * @param operationType Type of the operation, see {@link OperationType}
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BACKUP)
-    public RestoreSession beginRestoreSession(@OperationType int operationType) {
-        RestoreSession session = null;
-        checkServiceBinder();
-        if (sService != null) {
-            try {
-                // All packages, current transport
-                IRestoreSession binder =
-                        sService.beginRestoreSessionForUser(mContext.getUserId(), null, null,
-                                operationType);
+                        sService.beginRestoreSessionForUser(mContext.getUserId(), null, null);
                 if (binder != null) {
                     session = new RestoreSession(mContext, binder);
                 }
@@ -801,7 +772,7 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.BACKUP)
     public int requestBackup(String[] packages, BackupObserver observer) {
-        return requestBackup(packages, observer, null, 0, OperationType.BACKUP);
+        return requestBackup(packages, observer, null, 0);
     }
 
     /**
@@ -826,31 +797,6 @@
     @RequiresPermission(android.Manifest.permission.BACKUP)
     public int requestBackup(String[] packages, BackupObserver observer,
             BackupManagerMonitor monitor, int flags) {
-        return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP);
-    }
-
-    /**
-     * Request an immediate backup, providing an observer to which results of the backup operation
-     * will be published. The Android backup system will decide for each package whether it will
-     * be full app data backup or key/value-pair-based backup.
-     *
-     * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all
-     * provided packages using the remote transport.
-     *
-     * @param packages List of package names to backup.
-     * @param observer The {@link BackupObserver} to receive callbacks during the backup
-     *                 operation. Could be {@code null}.
-     * @param monitor  The {@link BackupManagerMonitorWrapper} to receive callbacks of important
-     *                 events during the backup operation. Could be {@code null}.
-     * @param flags    {@link #FLAG_NON_INCREMENTAL_BACKUP}.
-     * @param operationType {@link OperationType}
-     * @return {@link BackupManager#SUCCESS} on success; nonzero on error.
-     * @throws IllegalArgumentException on null or empty {@code packages} param.
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BACKUP)
-    public int requestBackup(String[] packages, BackupObserver observer,
-            BackupManagerMonitor monitor, int flags, @OperationType int operationType) {
         checkServiceBinder();
         if (sService != null) {
             try {
@@ -860,8 +806,7 @@
                 BackupManagerMonitorWrapper monitorWrapper = monitor == null
                         ? null
                         : new BackupManagerMonitorWrapper(monitor);
-                return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags,
-                        operationType);
+                return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags);
             } catch (RemoteException e) {
                 Log.e(TAG, "requestBackup() couldn't connect");
             }
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index f7ed6f1f..3701ea8 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -19,8 +19,10 @@
 import static android.app.backup.BackupManager.OperationType;
 
 import android.annotation.Nullable;
+import android.annotation.StringDef;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.XmlResourceParser;
 import android.os.ParcelFileDescriptor;
@@ -33,6 +35,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -93,6 +96,15 @@
     public static final String FLAG_REQUIRED_FAKE_CLIENT_SIDE_ENCRYPTION =
             "fakeClientSideEncryption";
 
+    @StringDef({
+        ConfigSection.CLOUD_BACKUP,
+        ConfigSection.DEVICE_TRANSFER
+    })
+    private @interface ConfigSection {
+        String CLOUD_BACKUP = "cloud-backup";
+        String DEVICE_TRANSFER = "device-transfer";
+    }
+
     /**
      * Identify {@link BackupScheme} object by package and operation type
      * (see {@link OperationType}) it corresponds to.
@@ -273,6 +285,7 @@
         private final static String TAG_INCLUDE = "include";
         private final static String TAG_EXCLUDE = "exclude";
 
+        final int mDataExtractionRules;
         final int mFullBackupContent;
         @OperationType final int mOperationType;
         final PackageManager mPackageManager;
@@ -394,7 +407,10 @@
         ArraySet<PathWithRequiredFlags> mExcludes;
 
         BackupScheme(Context context, @OperationType int operationType) {
-            mFullBackupContent = context.getApplicationInfo().fullBackupContent;
+            ApplicationInfo applicationInfo = context.getApplicationInfo();
+
+            mDataExtractionRules = applicationInfo.dataExtractionRulesRes;
+            mFullBackupContent = applicationInfo.fullBackupContent;
             mOperationType = operationType;
             mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
             mPackageManager = context.getPackageManager();
@@ -468,34 +484,98 @@
             mIncludes = new ArrayMap<String, Set<PathWithRequiredFlags>>();
             mExcludes = new ArraySet<PathWithRequiredFlags>();
 
-            if (mFullBackupContent == 0) {
-                // android:fullBackupContent="true" which means that we'll do everything.
+            if (mFullBackupContent == 0 && mDataExtractionRules == 0) {
+                // No scheme specified via either new or legacy config, will copy everything.
                 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
                     Log.v(FullBackup.TAG_XML_PARSER, "android:fullBackupContent - \"true\"");
                 }
             } else {
-                // android:fullBackupContent="@xml/some_resource".
+                // Scheme is present.
                 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
-                    Log.v(FullBackup.TAG_XML_PARSER,
-                            "android:fullBackupContent - found xml resource");
+                    Log.v(FullBackup.TAG_XML_PARSER, "Found xml scheme: "
+                            + "android:fullBackupContent=" + mFullBackupContent
+                            + "; android:dataExtractionRules=" + mDataExtractionRules);
                 }
-                XmlResourceParser parser = null;
+
                 try {
-                    parser = mPackageManager
-                            .getResourcesForApplication(mPackageName)
-                            .getXml(mFullBackupContent);
-                    parseBackupSchemeFromXmlLocked(parser, mExcludes, mIncludes);
+                    parseSchemeForOperationType(mOperationType);
                 } catch (PackageManager.NameNotFoundException e) {
                     // Throw it as an IOException
                     throw new IOException(e);
-                } finally {
-                    if (parser != null) {
-                        parser.close();
-                    }
                 }
             }
         }
 
+        private void parseSchemeForOperationType(@OperationType int operationType)
+                throws PackageManager.NameNotFoundException, IOException, XmlPullParserException {
+            String configSection = getConfigSectionForOperationType(operationType);
+            if (configSection == null) {
+                Slog.w(TAG, "Given operation type isn't supported by backup scheme: "
+                        + operationType);
+                return;
+            }
+
+            if (mDataExtractionRules != 0) {
+                // New config is present. Use it if it has configuration for this operation
+                // type.
+                try (XmlResourceParser parser = getParserForResource(mDataExtractionRules)) {
+                    parseNewBackupSchemeFromXmlLocked(parser, configSection, mExcludes, mIncludes);
+                }
+                if (!mExcludes.isEmpty() || !mIncludes.isEmpty()) {
+                    // Found configuration in the new config, we will use it.
+                    return;
+                }
+            }
+
+            // TODO(b/180523564): Ignore the old config for apps targeting Android S+ during D2D.
+
+            if (mFullBackupContent != 0) {
+                // Fall back to the old config.
+                try (XmlResourceParser parser = getParserForResource(mFullBackupContent)) {
+                    parseBackupSchemeFromXmlLocked(parser, mExcludes, mIncludes);
+                }
+            }
+        }
+
+        @Nullable
+        private String getConfigSectionForOperationType(@OperationType int operationType)  {
+            switch (operationType) {
+                case OperationType.BACKUP:
+                    return ConfigSection.CLOUD_BACKUP;
+                case OperationType.MIGRATION:
+                    return ConfigSection.DEVICE_TRANSFER;
+                default:
+                    return null;
+            }
+        }
+
+        private XmlResourceParser getParserForResource(int resourceId)
+                throws PackageManager.NameNotFoundException {
+            return mPackageManager
+                    .getResourcesForApplication(mPackageName)
+                    .getXml(resourceId);
+        }
+
+        private void parseNewBackupSchemeFromXmlLocked(XmlPullParser parser,
+                @ConfigSection  String configSection,
+                Set<PathWithRequiredFlags> excludes,
+                Map<String, Set<PathWithRequiredFlags>> includes)
+                throws IOException, XmlPullParserException {
+            verifyTopLevelTag(parser, "data-extraction-rules");
+
+            int event;
+            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                if (event != XmlPullParser.START_TAG || !configSection.equals(parser.getName())) {
+                    continue;
+                }
+
+                // TODO(b/180523028): Parse required attributes for rules (e.g. encryption).
+                parseRules(parser, excludes, includes, Optional.of(0), configSection);
+            }
+
+            logParsingResults(excludes, includes);
+        }
+
         @VisibleForTesting
         public void parseBackupSchemeFromXmlLocked(XmlPullParser parser,
                                                    Set<PathWithRequiredFlags> excludes,
@@ -503,7 +583,7 @@
                 throws IOException, XmlPullParserException {
             verifyTopLevelTag(parser, "full-backup-content");
 
-            parseRules(parser, excludes, includes, Optional.empty());
+            parseRules(parser, excludes, includes, Optional.empty(), "full-backup-content");
 
             logParsingResults(excludes, includes);
         }
@@ -532,10 +612,12 @@
         private void parseRules(XmlPullParser parser,
                 Set<PathWithRequiredFlags> excludes,
                 Map<String, Set<PathWithRequiredFlags>> includes,
-                Optional<Integer> maybeRequiredFlags)
+                Optional<Integer> maybeRequiredFlags,
+                String endingTag)
                 throws IOException, XmlPullParserException {
             int event;
-            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && !parser.getName().equals(endingTag)) {
                 switch (event) {
                     case XmlPullParser.START_TAG:
                         validateInnerTagContents(parser);
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index e1bbc08..bf5be95 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -547,11 +547,9 @@
      *        set can be restored.
      * @param transportID The name of the transport to use for the restore operation.
      *        May be null, in which case the current active transport is used.
-     * @param operationType Type of the operation, see {@link BackupManager#OperationType}
      * @return An interface to the restore session, or null on error.
      */
-    IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID,
-            int operationType);
+    IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID);
 
     /**
      * Notify the backup manager that a BackupAgent has completed the operation
@@ -680,7 +678,7 @@
      * {@link android.app.backup.IBackupManager.requestBackupForUser} for the calling user id.
      */
     int requestBackup(in String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor,
-        int flags, int operationType);
+            int flags);
 
     /**
      * Cancel all running backups. After this call returns, no currently running backups will
diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java
index 132af4b..dd2ba7d 100644
--- a/core/java/android/app/people/PeopleSpaceTile.java
+++ b/core/java/android/app/people/PeopleSpaceTile.java
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -45,7 +46,7 @@
     private String mId;
     private CharSequence mUserName;
     private Icon mUserIcon;
-    private int mUid;
+    private UserHandle mUserHandle;
     private Uri mContactUri;
     private String mPackageName;
     private String mBirthdayText;
@@ -64,7 +65,7 @@
         mUserName = b.mUserName;
         mUserIcon = b.mUserIcon;
         mContactUri = b.mContactUri;
-        mUid = b.mUid;
+        mUserHandle = b.mUserHandle;
         mPackageName = b.mPackageName;
         mBirthdayText = b.mBirthdayText;
         mLastInteractionTimestamp = b.mLastInteractionTimestamp;
@@ -95,8 +96,8 @@
         return mContactUri;
     }
 
-    public int getUid() {
-        return mUid;
+    public UserHandle getUserHandle() {
+        return mUserHandle;
     }
 
     public String getPackageName() {
@@ -165,7 +166,7 @@
         Builder builder =
                 new Builder(mId, mUserName.toString(), mUserIcon, mIntent);
         builder.setContactUri(mContactUri);
-        builder.setUid(mUid);
+        builder.setUserHandle(mUserHandle);
         builder.setPackageName(mPackageName);
         builder.setBirthdayText(mBirthdayText);
         builder.setLastInteractionTimestamp(mLastInteractionTimestamp);
@@ -186,7 +187,7 @@
         private CharSequence mUserName;
         private Icon mUserIcon;
         private Uri mContactUri;
-        private int mUid;
+        private UserHandle mUserHandle;
         private String mPackageName;
         private String mBirthdayText;
         private long mLastInteractionTimestamp;
@@ -212,7 +213,7 @@
             mId = info.getId();
             mUserName = info.getLabel();
             mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0));
-            mUid = info.getUserId();
+            mUserHandle = info.getUserHandle();
             mPackageName = info.getPackage();
             mContactUri = getContactUri(info);
         }
@@ -222,7 +223,7 @@
             mId = info.getId();
             mUserName = info.getLabel();
             mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0));
-            mUid = info.getUserId();
+            mUserHandle = info.getUserHandle();
             mPackageName = info.getPackage();
             mContactUri = getContactUri(info);
             mStatuses = channel.getStatuses();
@@ -265,9 +266,9 @@
             return this;
         }
 
-        /** Sets the associated uid. */
-        public Builder setUid(int uid) {
-            mUid = uid;
+        /** Sets the associated {@code userHandle}. */
+        public Builder setUserHandle(UserHandle userHandle) {
+            mUserHandle = userHandle;
             return this;
         }
 
@@ -349,7 +350,7 @@
         mUserName = in.readCharSequence();
         mUserIcon = in.readParcelable(Icon.class.getClassLoader());
         mContactUri = in.readParcelable(Uri.class.getClassLoader());
-        mUid = in.readInt();
+        mUserHandle = in.readParcelable(UserHandle.class.getClassLoader());
         mPackageName = in.readString();
         mBirthdayText = in.readString();
         mLastInteractionTimestamp = in.readLong();
@@ -375,7 +376,7 @@
         dest.writeCharSequence(mUserName);
         dest.writeParcelable(mUserIcon, flags);
         dest.writeParcelable(mContactUri, flags);
-        dest.writeInt(mUid);
+        dest.writeParcelable(mUserHandle, flags);
         dest.writeString(mPackageName);
         dest.writeString(mBirthdayText);
         dest.writeLong(mLastInteractionTimestamp);
diff --git a/core/java/android/app/time/LocationTimeZoneManager.java b/core/java/android/app/time/LocationTimeZoneManager.java
index 71a800f..066aada 100644
--- a/core/java/android/app/time/LocationTimeZoneManager.java
+++ b/core/java/android/app/time/LocationTimeZoneManager.java
@@ -37,7 +37,7 @@
     /**
      * The name of the service for shell commands
      */
-    public static final String SHELL_COMMAND_SERVICE_NAME = "location_time_zone_manager";
+    public static final String SERVICE_NAME = "location_time_zone_manager";
 
     /**
      * A shell command that starts the service (after stop).
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 4277292..fc54c71 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -40,6 +40,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -92,7 +93,10 @@
     int mLayoutId = -1;
     private InteractionHandler mInteractionHandler;
     private boolean mOnLightBackground;
-    PointF mCurrentSize = null;
+    private PointF mCurrentSize = null;
+    private RemoteViews.ColorResources mColorResources = null;
+    // Stores the last remote views last inflated.
+    private RemoteViews mLastInflatedRemoteViews = null;
 
     private Executor mAsyncExecutor;
     private CancellationSignal mLastExecutionSignal;
@@ -358,7 +362,7 @@
         PointF newSize = new PointF(size.x - xPaddingDips, size.y - yPaddingDips);
         if (!newSize.equals(mCurrentSize)) {
             mCurrentSize = newSize;
-            mLayoutId = -1; // Prevents recycling the view.
+            reapplyLastRemoteViews();
         }
     }
 
@@ -368,7 +372,7 @@
     public void clearCurrentSize() {
         if (mCurrentSize != null) {
             mCurrentSize = null;
-            mLayoutId = -1;
+            reapplyLastRemoteViews();
         }
     }
 
@@ -477,10 +481,18 @@
      * AppWidget provider. Will animate into these new views as needed
      */
     public void updateAppWidget(RemoteViews remoteViews) {
+        this.mLastInflatedRemoteViews = remoteViews;
         applyRemoteViews(remoteViews, true);
     }
 
     /**
+     * Reapply the last inflated remote views, or the default view is none was inflated.
+     */
+    private void reapplyLastRemoteViews() {
+        applyRemoteViews(mLastInflatedRemoteViews, true);
+    }
+
+    /**
      * @hide
      */
     protected void applyRemoteViews(RemoteViews remoteViews, boolean useAsyncIfPossible) {
@@ -518,7 +530,8 @@
             // layout matches, try recycling it
             if (content == null && layoutId == mLayoutId) {
                 try {
-                    remoteViews.reapply(mContext, mView, mInteractionHandler);
+                    remoteViews.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
+                            mColorResources);
                     content = mView;
                     recycled = true;
                     if (LOGD) Log.d(TAG, "was able to recycle existing layout");
@@ -530,7 +543,8 @@
             // Try normal RemoteView inflation
             if (content == null) {
                 try {
-                    content = remoteViews.apply(mContext, this, mInteractionHandler, mCurrentSize);
+                    content = remoteViews.apply(mContext, this, mInteractionHandler,
+                            mCurrentSize, mColorResources);
                     if (LOGD) Log.d(TAG, "had to inflate new layout");
                 } catch (RuntimeException e) {
                     exception = e;
@@ -583,7 +597,8 @@
                         mAsyncExecutor,
                         new ViewApplyListener(remoteViews, layoutId, true),
                         mInteractionHandler,
-                        mCurrentSize);
+                        mCurrentSize,
+                        mColorResources);
             } catch (Exception e) {
                 // Reapply failed. Try apply
             }
@@ -594,7 +609,8 @@
                     mAsyncExecutor,
                     new ViewApplyListener(remoteViews, layoutId, false),
                     mInteractionHandler,
-                    mCurrentSize);
+                    mCurrentSize,
+                    mColorResources);
         }
     }
 
@@ -662,9 +678,13 @@
     protected Context getRemoteContext() {
         try {
             // Return if cloned successfully, otherwise default
-            return mContext.createApplicationContext(
+            Context newContext = mContext.createApplicationContext(
                     mInfo.providerInfo.applicationInfo,
                     Context.CONTEXT_RESTRICTED);
+            if (mColorResources != null) {
+                mColorResources.apply(newContext);
+            }
+            return newContext;
         } catch (NameNotFoundException e) {
             Log.e(TAG, "Package name " +  mInfo.providerInfo.packageName + " not found");
             return mContext;
@@ -819,4 +839,37 @@
             }
         };
     }
+
+    /**
+     * Set the dynamically overloaded color resources.
+     *
+     * {@code colorMapping} maps a predefined set of color resources to their ARGB
+     * representation. Any entry not in the predefined set of colors will be ignored.
+     *
+     * Calling this method will trigger a full re-inflation of the App Widget.
+     *
+     * The color resources that can be overloaded are the ones whose name is prefixed with
+     * {@code system_primary_}, {@code system_secondary_} or {@code system_neutral_}, for example
+     * {@link android.R.color#system_primary_500}.
+     */
+    public void setColorResources(@NonNull SparseIntArray colorMapping) {
+        mColorResources = RemoteViews.ColorResources.create(mContext, colorMapping);
+        mLayoutId = -1;
+        reapplyLastRemoteViews();
+    }
+
+    /**
+     * Reset the dynamically overloaded resources, reverting to the default values for
+     * all the colors.
+     *
+     * If colors were defined before, calling this method will trigger a full re-inflation of the
+     * App Widget.
+     */
+    public void resetColorResources() {
+        if (mColorResources != null) {
+            mColorResources = null;
+            mLayoutId = -1;
+            reapplyLastRemoteViews();
+        }
+    }
 }
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index aac8710..38919f6 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -23,6 +23,8 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
 import android.app.IServiceConnection;
 import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -1095,7 +1097,9 @@
      *
      * @hide
      */
-    public void setBindAppWidgetPermission(String packageName, int userId, boolean permission) {
+    @TestApi
+    public void setBindAppWidgetPermission(
+            @NonNull String packageName, @UserIdInt int userId, boolean permission) {
         if (mService == null) {
             return;
         }
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index d893a5e..6ac1c1a 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -332,12 +332,13 @@
 
     /**
      * Resource id for the description of the AppWidget.
+     *
      * <p>This field corresponds to the <code>android:description</code> attribute in the AppWidget
      * meta-data file.
      */
     @SuppressLint("MutableBareField")
     @IdRes
-    public int descriptionResource;
+    public int descriptionRes;
 
     /**
      * Flags indicating various features supported by the widget. These are hints to the widget
@@ -385,7 +386,7 @@
         this.widgetCategory = in.readInt();
         this.providerInfo = in.readTypedObject(ActivityInfo.CREATOR);
         this.widgetFeatures = in.readInt();
-        this.descriptionResource = in.readInt();
+        this.descriptionRes = in.readInt();
     }
 
     /**
@@ -442,14 +443,22 @@
         return loadDrawable(context, density, previewImage, false);
     }
 
-    /** Loads localized description for the app widget. */
+    /**
+     * Loads localized description for the app widget.
+     *
+     * <p>Description is intended to be displayed in the UI of the widget picker.
+     *
+     * @param context Context for accessing resources.
+     *
+     * @return CharSequence for app widget description for the current locale.
+     */
     @Nullable
-    public final String loadDescription(@NonNull Context context) {
-        if (ResourceId.isValid(descriptionResource)) {
+    public final CharSequence loadDescription(@NonNull Context context) {
+        if (ResourceId.isValid(descriptionRes)) {
             return context.getPackageManager()
                     .getText(
                             providerInfo.packageName,
-                            descriptionResource,
+                            descriptionRes,
                             providerInfo.applicationInfo)
                     .toString()
                     .trim();
@@ -499,7 +508,7 @@
         out.writeInt(this.widgetCategory);
         out.writeTypedObject(this.providerInfo, flags);
         out.writeInt(this.widgetFeatures);
-        out.writeInt(this.descriptionResource);
+        out.writeInt(this.descriptionRes);
     }
 
     @Override
@@ -528,7 +537,7 @@
         that.widgetCategory = this.widgetCategory;
         that.providerInfo = this.providerInfo;
         that.widgetFeatures = this.widgetFeatures;
-        that.descriptionResource = this.descriptionResource;
+        that.descriptionRes = this.descriptionRes;
         return that;
     }
 
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index cd91aa9..53aaae0 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -943,12 +943,13 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean setBufferMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) {
-        if (VDBG) log("setBufferMillis(" + codec + ", " + value + ")");
+    public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec,
+            int value) {
+        if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")");
         try {
             final IBluetoothA2dp service = getService();
             if (service != null && isEnabled()) {
-                return service.setBufferMillis(codec, value);
+                return service.setBufferLengthMillis(codec, value);
             }
             if (service == null) Log.w(TAG, "Proxy not attached to service");
             return false;
diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java
index 7e5ec1e..97d9723 100644
--- a/core/java/android/bluetooth/BufferConstraints.java
+++ b/core/java/android/bluetooth/BufferConstraints.java
@@ -90,7 +90,7 @@
      * @hide
      */
     @SystemApi
-    public @Nullable BufferConstraint getCodec(@BluetoothCodecConfig.SourceCodecType int codec) {
+    public @Nullable BufferConstraint forCodec(@BluetoothCodecConfig.SourceCodecType int codec) {
         return mBufferConstraints.get(codec);
     }
 }
diff --git a/core/java/android/companion/DeviceNotAssociatedException.java b/core/java/android/companion/DeviceNotAssociatedException.java
index bebb6c9..f8a7a7c 100644
--- a/core/java/android/companion/DeviceNotAssociatedException.java
+++ b/core/java/android/companion/DeviceNotAssociatedException.java
@@ -23,7 +23,7 @@
  * An exception for a case when a given device was not
  * {@link CompanionDeviceManager#associate associated} to the calling app.
  */
-public class DeviceNotAssociatedException extends Exception {
+public class DeviceNotAssociatedException extends RuntimeException {
     /** @hide */
     public DeviceNotAssociatedException(@Nullable String deviceName) {
         super("Device not associated with the current app: " + deviceName);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 46d8900..230c985 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -132,8 +132,11 @@
     public static final String SYNC_EXTRAS_ACCOUNT = "account";
 
     /**
-     * If this extra is set to true, the sync request will be scheduled
-     * at the front of the sync request queue and without any delay
+     * If this extra is set to true, the sync request will be scheduled at the front of the
+     * sync request queue, but it is still subject to JobScheduler quota and throttling due to
+     * App Standby buckets.
+     *
+     * <p>This is different from {@link #SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB}.
      */
     public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
 
@@ -145,6 +148,29 @@
     public static final String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging";
 
     /**
+     * Run this sync operation as an "expedited job"
+     * (see {@link android.app.job.JobInfo.Builder#setExpedited(boolean)}).
+     * Normally (if this flag isn't specified), sync operations are executed as regular
+     * {@link android.app.job.JobService} jobs.
+     *
+     * <p> Because Expedited Jobs have various restrictions compared to regular jobs, this flag
+     * cannot be combined with certain other flags, otherwise an
+     * <code>IllegalArgumentException</code> will be thrown. Notably, because Expedited Jobs do not
+     * support various constraints, the following restriction apply:
+     * <ul>
+     *  <li>Can't be used with {@link #SYNC_EXTRAS_REQUIRE_CHARGING}
+     *  <li>Can't be used with {@link #SYNC_EXTRAS_EXPEDITED}
+     *  <li>Can't be used on periodic syncs.
+     *  <li>When an expedited-job-sync fails and a retry is scheduled, the retried sync will be
+     *  scheduled as a regular job unless {@link #SYNC_EXTRAS_IGNORE_BACKOFF} is set.
+     * </ul>
+     *
+     * <p>This is different from {@link #SYNC_EXTRAS_EXPEDITED}.
+     */
+    @SuppressLint("IntentName")
+    public static final String SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB = "schedule_as_expedited_job";
+
+    /**
      * @deprecated instead use
      * {@link #SYNC_EXTRAS_MANUAL}
      */
@@ -3220,6 +3246,18 @@
     }
 
     /**
+     * {@hide}
+     * Helper function to throw an <code>IllegalArgumentException</code> if any illegal
+     * extras were set for a sync scheduled as an expedited job.
+     *
+     * @param extras bundle to validate.
+     */
+    public static boolean hasInvalidScheduleAsEjExtras(Bundle extras) {
+        return extras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING)
+                || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED);
+    }
+
+    /**
      * Specifies that a sync should be requested with the specified the account, authority,
      * and extras at the given frequency. If there is already another periodic sync scheduled
      * with the account, authority and extras then a new periodic sync won't be added, instead
@@ -3233,7 +3271,8 @@
      * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY},
      * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS},
      * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE},
-     * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true.
+     * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL},
+     * {@link #SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB} set to true.
      * If any are supplied then an {@link IllegalArgumentException} will be thrown.
      *
      * <p>This method requires the caller to hold the permission
@@ -3273,16 +3312,14 @@
      * @param extras bundle to validate.
      */
     public static boolean invalidPeriodicExtras(Bundle extras) {
-        if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)
+        return extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)
                 || extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)
                 || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)
                 || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
                 || extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)
                 || extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false)
-                || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
-            return true;
-        }
-        return false;
+                || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)
+                || extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false);
     }
 
     /**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index e20f706..f3a4e1f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -370,6 +370,25 @@
     /***********    Hidden flags below this line ***********/
 
     /**
+     * Flag for {@link #bindService}: allow the process hosting the target service to be treated
+     * as if it's as important as a perceptible app to the user and avoid the oom killer killing
+     * this process in low memory situations until there aren't any other processes left but the
+     * ones which are user-perceptible.
+     *
+     * @hide
+     */
+    public static final int BIND_ALMOST_PERCEPTIBLE = 0x000010000;
+
+    /**
+     * Flag for {@link #bindService}: allow the process hosting the target service to gain
+     * {@link ActivityManager#PROCESS_CAPABILITY_NETWORK}, which allows it be able
+     * to access network regardless of any power saving restrictions.
+     *
+     * @hide
+     */
+    public static final int BIND_ALLOW_NETWORK_ACCESS = 0x00020000;
+
+    /**
      * Flag for {@link #bindService}: allow background foreground service starts from the bound
      * service's process.
      * This flag is only respected if the caller is holding
@@ -6674,15 +6693,6 @@
     }
 
     /**
-     * Indicates if this context is a visual context such as {@link android.app.Activity} or
-     * a context created from {@link #createWindowContext(int, Bundle)}.
-     * @hide
-     */
-    public boolean isUiContext() {
-        throw new RuntimeException("Not implemented. Must override in a subclass.");
-    }
-
-    /**
      * Returns {@code true} if the context is a UI context which can access UI components such as
      * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or
      * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI
@@ -6694,12 +6704,16 @@
      * {@link #createWindowContext(int, Bundle)} or
      * {@link android.inputmethodservice.InputMethodService InputMethodService}
      * </p>
+     * <p>
+     * Note that even if it is allowed programmatically, it is not suggested to override this
+     * method to bypass {@link android.os.strictmode.IncorrectContextUseViolation} verification.
+     * </p>
      *
      * @see #getDisplay()
      * @see #getSystemService(String)
      * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
      */
-    public static boolean isUiContext(@NonNull Context context) {
-        return context.isUiContext();
+    public boolean isUiContext() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
 }
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 858d1e4..b1252fd 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -18,6 +18,7 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityManager.PendingIntentInfo;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.Handler;
@@ -60,6 +61,9 @@
     private final IIntentSender mTarget;
     IBinder mWhitelistToken;
 
+    // cached pending intent information
+    private @Nullable PendingIntentInfo mCachedInfo;
+
     /**
      * Exception thrown when trying to send through a PendingIntent that
      * has been canceled or is otherwise no longer able to execute the request.
@@ -209,13 +213,7 @@
      */
     @Deprecated
     public String getTargetPackage() {
-        try {
-            return ActivityManager.getService()
-                .getPackageForIntentSender(mTarget);
-        } catch (RemoteException e) {
-            // Should never happen.
-            return null;
-        }
+        return getCreatorPackage();
     }
 
     /**
@@ -228,13 +226,7 @@
      * none associated with it.
      */
     public String getCreatorPackage() {
-        try {
-            return ActivityManager.getService()
-                .getPackageForIntentSender(mTarget);
-        } catch (RemoteException e) {
-            // Should never happen.
-            return null;
-        }
+        return getCachedInfo().getCreatorPackage();
     }
 
     /**
@@ -247,13 +239,7 @@
      * none associated with it.
      */
     public int getCreatorUid() {
-        try {
-            return ActivityManager.getService()
-                .getUidForIntentSender(mTarget);
-        } catch (RemoteException e) {
-            // Should never happen.
-            return -1;
-        }
+        return getCachedInfo().getCreatorUid();
     }
 
     /**
@@ -268,14 +254,8 @@
      * none associated with it.
      */
     public UserHandle getCreatorUserHandle() {
-        try {
-            int uid = ActivityManager.getService()
-                .getUidForIntentSender(mTarget);
-            return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null;
-        } catch (RemoteException e) {
-            // Should never happen.
-            return null;
-        }
+        int uid = getCachedInfo().getCreatorUid();
+        return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null;
     }
 
     /**
@@ -384,4 +364,16 @@
     public IntentSender(IBinder target) {
         mTarget = IIntentSender.Stub.asInterface(target);
     }
+
+    private PendingIntentInfo getCachedInfo() {
+        if (mCachedInfo == null) {
+            try {
+                mCachedInfo = ActivityManager.getService().getInfoForIntentSender(mTarget);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        return mCachedInfo;
+    }
 }
diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java
index 9e568a4..e1e6f75 100644
--- a/core/java/android/content/SyncRequest.java
+++ b/core/java/android/content/SyncRequest.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.accounts.Account;
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
@@ -58,6 +59,8 @@
     private final boolean mIsAuthority;
     /** Sync should be run in lieu of other syncs. */
     private final boolean mIsExpedited;
+    /** Sync sound be ran as an expedited job. */
+    private final boolean mIsScheduledAsExpeditedJob;
 
     /**
      * {@hide}
@@ -79,6 +82,14 @@
 
     /**
      * {@hide}
+     * @return whether this sync is scheduled as an expedited job.
+     */
+    public boolean isScheduledAsExpeditedJob() {
+        return mIsScheduledAsExpeditedJob;
+    }
+
+    /**
+     * {@hide}
      *
      * @return account object for this sync.
      * @throws IllegalArgumentException if this function is called for a request that targets a
@@ -149,6 +160,7 @@
         parcel.writeInt((mDisallowMetered ? 1 : 0));
         parcel.writeInt((mIsAuthority ? 1 : 0));
         parcel.writeInt((mIsExpedited? 1 : 0));
+        parcel.writeInt(mIsScheduledAsExpeditedJob ? 1 : 0);
         parcel.writeParcelable(mAccountToSync, flags);
         parcel.writeString(mAuthority);
     }
@@ -161,6 +173,7 @@
         mDisallowMetered = (in.readInt() != 0);
         mIsAuthority = (in.readInt() != 0);
         mIsExpedited = (in.readInt() != 0);
+        mIsScheduledAsExpeditedJob = (in.readInt() != 0);
         mAccountToSync = in.readParcelable(null);
         mAuthority = in.readString();
     }
@@ -174,6 +187,7 @@
         mIsPeriodic = (b.mSyncType == Builder.SYNC_TYPE_PERIODIC);
         mIsAuthority = (b.mSyncTarget == Builder.SYNC_TARGET_ADAPTER);
         mIsExpedited = b.mExpedited;
+        mIsScheduledAsExpeditedJob = b.mScheduleAsExpeditedJob;
         mExtras = new Bundle(b.mCustomExtras);
         // For now we merge the sync config extras & the custom extras into one bundle.
         // TODO: pass the configuration extras through separately.
@@ -258,6 +272,11 @@
          */
         private boolean mRequiresCharging;
 
+        /**
+         * Whether the sync should be scheduled as an expedited job.
+         */
+        private boolean mScheduleAsExpeditedJob;
+
         public Builder() {
         }
 
@@ -309,7 +328,8 @@
          * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE},
          * {@link ContentResolver#SYNC_EXTRAS_FORCE},
          * {@link ContentResolver#SYNC_EXTRAS_EXPEDITED},
-         * {@link ContentResolver#SYNC_EXTRAS_MANUAL}
+         * {@link ContentResolver#SYNC_EXTRAS_MANUAL},
+         * {@link ContentResolver#SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB}
          * set to true. If any are supplied then an <code>IllegalArgumentException</code> will
          * be thrown.
          *
@@ -500,6 +520,22 @@
         }
 
         /**
+         * Convenience function for setting
+         * {@link ContentResolver#SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB}.
+         *
+         * <p> Not to be confused with {@link ContentResolver#SYNC_EXTRAS_EXPEDITED}.
+         *
+         * <p> Not valid for periodic syncs, expedited syncs, and syncs that require charging - an
+         * <code>IllegalArgumentException</code> will be thrown in {@link #build()}.
+         *
+         * @param scheduleAsExpeditedJob whether to schedule as an expedited job. Default false.
+         */
+        public @NonNull Builder setScheduleAsExpeditedJob(boolean scheduleAsExpeditedJob) {
+            mScheduleAsExpeditedJob = scheduleAsExpeditedJob;
+            return this;
+        }
+
+        /**
          * Performs validation over the request and throws the runtime exception
          * <code>IllegalArgumentException</code> if this validation fails.
          *
@@ -507,11 +543,6 @@
          *         builder.
          */
         public SyncRequest build() {
-            // Validate the extras bundle
-            ContentResolver.validateSyncExtrasBundle(mCustomExtras);
-            if (mCustomExtras == null) {
-                mCustomExtras = new Bundle();
-            }
             // Combine builder extra flags into the config bundle.
             mSyncConfigExtras = new Bundle();
             if (mIgnoreBackoff) {
@@ -532,17 +563,35 @@
             if (mExpedited) {
                 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
             }
+            if (mScheduleAsExpeditedJob) {
+                mSyncConfigExtras.putBoolean(
+                        ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true);
+            }
             if (mIsManual) {
                 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
                 mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
             }
+
+            if (mCustomExtras == null) {
+                mCustomExtras = new Bundle();
+            }
+            // Validate the extras bundles
+            ContentResolver.validateSyncExtrasBundle(mCustomExtras);
+            // If this is a periodic sync ensure than invalid extras were not set.
             if (mSyncType == SYNC_TYPE_PERIODIC) {
-                // If this is a periodic sync ensure than invalid extras were not set.
                 if (ContentResolver.invalidPeriodicExtras(mCustomExtras) ||
                         ContentResolver.invalidPeriodicExtras(mSyncConfigExtras)) {
                     throw new IllegalArgumentException("Illegal extras were set");
                 }
             }
+            // If this sync is scheduled as an EJ, ensure that invalid extras were not set.
+            if (mCustomExtras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB)
+                    || mScheduleAsExpeditedJob) {
+                if (ContentResolver.hasInvalidScheduleAsEjExtras(mCustomExtras)
+                        || ContentResolver.hasInvalidScheduleAsEjExtras(mSyncConfigExtras)) {
+                    throw new IllegalArgumentException("Illegal extras were set");
+                }
+            }
             // Ensure that a target for the sync has been set.
             if (mSyncTarget == SYNC_TARGET_UNKNOWN) {
                 throw new IllegalArgumentException("Must specify an adapter with" +
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index dec2c3d..0aa1be9 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -136,6 +136,18 @@
     public int fullBackupContent = 0;
 
     /**
+     * Applications can set this attribute to an xml resource within their app where they specified
+     * the rules determining which files and directories can be copied from the device as part of
+     * backup or transfer operations.
+     *<p>
+     * Set from the {@link android.R.styleable#AndroidManifestApplication_dataExtractionRules}
+     * attribute in the manifest.
+     *
+     * @hide
+     */
+    public int dataExtractionRulesRes = 0;
+
+    /**
      * <code>true</code> if the package is capable of presenting a unified interface representing
      * multiple profiles.
      * @hide
@@ -1520,6 +1532,9 @@
                 pw.println(prefix + "fullBackupContent="
                         + (fullBackupContent < 0 ? "false" : "true"));
             }
+            if (dataExtractionRulesRes != 0) {
+                pw.println(prefix + "dataExtractionRules=@xml/" + dataExtractionRulesRes);
+            }
             pw.println(prefix + "crossProfile=" + (crossProfile ? "true" : "false"));
             if (networkSecurityConfigRes != 0) {
                 pw.println(prefix + "networkSecurityConfigRes=0x"
@@ -1749,6 +1764,7 @@
         uiOptions = orig.uiOptions;
         backupAgentName = orig.backupAgentName;
         fullBackupContent = orig.fullBackupContent;
+        dataExtractionRulesRes = orig.dataExtractionRulesRes;
         crossProfile = orig.crossProfile;
         networkSecurityConfigRes = orig.networkSecurityConfigRes;
         category = orig.category;
@@ -1836,6 +1852,7 @@
         dest.writeInt(descriptionRes);
         dest.writeInt(uiOptions);
         dest.writeInt(fullBackupContent);
+        dest.writeInt(dataExtractionRulesRes);
         dest.writeBoolean(crossProfile);
         dest.writeInt(networkSecurityConfigRes);
         dest.writeInt(category);
@@ -1920,6 +1937,7 @@
         descriptionRes = source.readInt();
         uiOptions = source.readInt();
         fullBackupContent = source.readInt();
+        dataExtractionRulesRes = source.readInt();
         crossProfile = source.readBoolean();
         networkSecurityConfigRes = source.readInt();
         category = source.readInt();
diff --git a/core/java/android/content/pm/DataLoaderParamsParcel.aidl b/core/java/android/content/pm/DataLoaderParamsParcel.aidl
index d40012fd..29d472e 100644
--- a/core/java/android/content/pm/DataLoaderParamsParcel.aidl
+++ b/core/java/android/content/pm/DataLoaderParamsParcel.aidl
@@ -23,7 +23,7 @@
  * @hide
  */
 parcelable DataLoaderParamsParcel {
-    DataLoaderType type;
+    DataLoaderType type = DataLoaderType.NONE;
     @utf8InCpp String packageName;
     @utf8InCpp String className;
     @utf8InCpp String arguments;
diff --git a/core/java/android/content/pm/InstallationFileParcel.aidl b/core/java/android/content/pm/InstallationFileParcel.aidl
index b7efc19..09d1a32 100644
--- a/core/java/android/content/pm/InstallationFileParcel.aidl
+++ b/core/java/android/content/pm/InstallationFileParcel.aidl
@@ -24,7 +24,7 @@
  */
 parcelable InstallationFileParcel {
     String name;
-    InstallationFileLocation location;
+    InstallationFileLocation location = InstallationFileLocation.UNKNOWN;
     long size;
     byte[] metadata;
     byte[] signature;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 567501c..42cbe35 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1173,7 +1173,6 @@
          * {@hide}
          */
         @SystemApi
-        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
         public @Nullable DataLoaderParams getDataLoaderParams() {
             try {
                 DataLoaderParamsParcel data = mSession.getDataLoaderParams();
@@ -1213,7 +1212,6 @@
          * {@hide}
          */
         @SystemApi
-        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
         public void addFile(@FileLocation int location, @NonNull String name, long lengthBytes,
                 @NonNull byte[] metadata, @Nullable byte[] signature) {
             try {
@@ -1237,7 +1235,6 @@
          * {@hide}
          */
         @SystemApi
-        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
         public void removeFile(@FileLocation int location, @NonNull String name) {
             try {
                 mSession.removeFile(location, name);
@@ -2050,9 +2047,7 @@
          * {@hide}
          */
         @SystemApi
-        @RequiresPermission(allOf = {
-                Manifest.permission.INSTALL_PACKAGES,
-                Manifest.permission.USE_INSTALLER_V2})
+        @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
         public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) {
             this.dataLoaderParams = dataLoaderParams;
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f9122b1..ca88241 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3601,7 +3601,7 @@
      * 1 - IncFs v1, core features, no PerUid support. Optional in R.
      * 2 - IncFs v2, PerUid support, fs-verity support. Required in S.
      *
-     * @see IncrementalManager#isFeatureEnabled and IncrementalManager#isV2()
+     * @see IncrementalManager#getVersion()
      * @hide
      */
     @SystemApi
@@ -3623,11 +3623,26 @@
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for
-     * camera. When sensory privacy for the camera is enabled no camera data is send to clients,
+     * microphone. When sensory privacy for the microphone is enabled no microphone data is sent to
+     * clients, e.g. all audio data is silent.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for
+     * camera. When sensory privacy for the camera is enabled no camera data is sent to clients,
      * e.g. the view finder in a camera app would appear blank.
      *
      * @hide
      */
+    @SystemApi
+    @TestApi
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
 
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index d81dff8..cfb6e1b 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.annotation.UserIdInt;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
@@ -47,6 +48,7 @@
  *
  * @hide
  */
+@TestApi
 public class UserInfo implements Parcelable {
 
     /**
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 7a01392..29edd40 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -260,6 +260,8 @@
 
     ParsingPackage setFullBackupContent(int fullBackupContent);
 
+    ParsingPackage setDataExtractionRules(int dataExtractionRules);
+
     ParsingPackage setHasDomainUrls(boolean hasDomainUrls);
 
     ParsingPackage setIconRes(int iconRes);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index c1a93d8..067787d 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -334,6 +334,7 @@
     private int descriptionRes;
 
     private int fullBackupContent;
+    private int dataExtractionRules;
     private int iconRes;
     private int installLocation = ParsingPackageUtils.PARSE_DEFAULT_INSTALL_LOCATION;
     private int labelRes;
@@ -1015,6 +1016,7 @@
         appInfo.enabled = getBoolean(Booleans.ENABLED);
 //        appInfo.enabledSetting
         appInfo.fullBackupContent = fullBackupContent;
+        appInfo.dataExtractionRulesRes = dataExtractionRules;
         // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy
 //        appInfo.mHiddenApiPolicy
 //        appInfo.hiddenUntilInstalled
@@ -1163,6 +1165,7 @@
         dest.writeInt(this.compatibleWidthLimitDp);
         dest.writeInt(this.descriptionRes);
         dest.writeInt(this.fullBackupContent);
+        dest.writeInt(this.dataExtractionRules);
         dest.writeInt(this.iconRes);
         dest.writeInt(this.installLocation);
         dest.writeInt(this.labelRes);
@@ -1284,6 +1287,7 @@
         this.compatibleWidthLimitDp = in.readInt();
         this.descriptionRes = in.readInt();
         this.fullBackupContent = in.readInt();
+        this.dataExtractionRules = in.readInt();
         this.iconRes = in.readInt();
         this.installLocation = in.readInt();
         this.labelRes = in.readInt();
@@ -1808,6 +1812,11 @@
     }
 
     @Override
+    public int getDataExtractionRules() {
+        return dataExtractionRules;
+    }
+
+    @Override
     public int getIconRes() {
         return iconRes;
     }
@@ -2264,6 +2273,12 @@
     }
 
     @Override
+    public ParsingPackageImpl setDataExtractionRules(int value) {
+        dataExtractionRules = value;
+        return this;
+    }
+
+    @Override
     public ParsingPackageImpl setIconRes(int value) {
         iconRes = value;
         return this;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index ff4cebd..f7f3e19 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -587,6 +587,11 @@
      */
     int getFullBackupContent();
 
+    /**
+     * @see R.styleable#AndroidManifestApplication_dataExtractionRules
+     */
+    int getDataExtractionRules();
+
     /** @see ApplicationInfo#PRIVATE_FLAG_HAS_DOMAIN_URLS */
     boolean isHasDomainUrls();
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index b7aa30f..0c033fd 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -2168,6 +2168,8 @@
                 .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa))
                 .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa))
                 .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa))
+                .setDataExtractionRules(
+                        resId(R.styleable.AndroidManifestApplication_dataExtractionRules, sa))
                 // Strings
                 .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa))
                 .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa))
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 2f7aeb8..b66f048 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1956,7 +1956,7 @@
         dest.writeInt(mnc);
 
         fixUpLocaleList();
-        dest.writeParcelable(mLocaleList, flags);
+        dest.writeTypedObject(mLocaleList, flags);
 
         if(userSetLocale) {
             dest.writeInt(1);
@@ -1980,7 +1980,7 @@
         dest.writeInt(compatScreenWidthDp);
         dest.writeInt(compatScreenHeightDp);
         dest.writeInt(compatSmallestScreenWidthDp);
-        dest.writeValue(windowConfiguration);
+        windowConfiguration.writeToParcel(dest, flags);
         dest.writeInt(assetsSeq);
         dest.writeInt(seq);
         dest.writeInt(fontWeightAdjustment);
@@ -1991,7 +1991,7 @@
         mcc = source.readInt();
         mnc = source.readInt();
 
-        mLocaleList = source.readParcelable(LocaleList.class.getClassLoader());
+        mLocaleList = source.readTypedObject(LocaleList.CREATOR);
         locale = mLocaleList.get(0);
 
         userSetLocale = (source.readInt()==1);
@@ -2012,7 +2012,7 @@
         compatScreenWidthDp = source.readInt();
         compatScreenHeightDp = source.readInt();
         compatSmallestScreenWidthDp = source.readInt();
-        windowConfiguration.setTo((WindowConfiguration) source.readValue(null));
+        windowConfiguration.readFromParcel(source);
         assetsSeq = source.readInt();
         seq = source.readInt();
         fontWeightAdjustment = source.readInt();
diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java
index e512cf1..429eef9 100644
--- a/core/java/android/graphics/fonts/FontManager.java
+++ b/core/java/android/graphics/fonts/FontManager.java
@@ -20,7 +20,6 @@
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -29,7 +28,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.text.FontConfig;
-import android.util.Log;
 
 import com.android.internal.graphics.fonts.IFontManager;
 
@@ -198,12 +196,11 @@
      * @return The current font configuration. null if failed to fetch information from the system
      *         service.
      */
-    public @Nullable FontConfig getFontConfig() {
+    public @NonNull FontConfig getFontConfig() {
         try {
             return mIFontManager.getFontConfig();
         } catch (RemoteException e) {
-            Log.e(TAG, "Failed to call getFontConfig", e);
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index f4f9e17..e03c1f4 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
@@ -33,6 +34,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
 
 /**
  * This class provides access to the sensor privacy services; sensor privacy allows the
@@ -42,9 +44,21 @@
  *
  * @hide
  */
+@SystemApi
 @TestApi
 @SystemService(Context.SENSOR_PRIVACY_SERVICE)
 public final class SensorPrivacyManager {
+
+    /**
+     * @hide
+     */
+    public static final boolean USE_MICROPHONE_TOGGLE = true;
+
+    /**
+     * @hide
+     */
+    public static final boolean USE_CAMERA_TOGGLE = true;
+
     /**
      * Unique Id of this manager to identify to the service
      * @hide
@@ -58,28 +72,39 @@
     public static final String EXTRA_SENSOR = SensorPrivacyManager.class.getName()
             + ".extra.sensor";
 
-    /** Microphone
-     * @hide */
-    @TestApi
-    public static final int INDIVIDUAL_SENSOR_MICROPHONE =
-            SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
-
-    /** Camera
-     * @hide */
-    @TestApi
-    public static final int INDIVIDUAL_SENSOR_CAMERA =
-            SensorPrivacyIndividualEnabledSensorProto.CAMERA;
-
     /**
-     * Individual sensors not listed in {@link Sensor}
+     * Individual sensors not listed in {@link Sensors}
      * @hide
      */
-    @IntDef(prefix = "INDIVIDUAL_SENSOR_", value = {
-            INDIVIDUAL_SENSOR_MICROPHONE,
-            INDIVIDUAL_SENSOR_CAMERA
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface IndividualSensor {}
+    @SystemApi
+    @TestApi
+    public static class Sensors {
+
+        private Sensors() {}
+
+        /** Microphone
+         * @hide */
+        @SystemApi
+        @TestApi
+        public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
+        /** Camera
+         * @hide */
+        @SystemApi
+        @TestApi
+        public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA;
+
+        /**
+         * Individual sensors not listed in {@link Sensors}
+         *
+         * @hide
+         */
+        @IntDef(value = {
+                MICROPHONE,
+                CAMERA
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface Sensor {}
+    }
 
     /**
      * A class implementing this interface can register with the {@link
@@ -88,6 +113,8 @@
      *
      * @hide
      */
+    @SystemApi
+    @TestApi
     public interface OnSensorPrivacyChangedListener {
         /**
          * Callback invoked when the sensor privacy state changes.
@@ -165,7 +192,8 @@
      *
      * @hide
      */
-    public void addSensorPrivacyListener(final OnSensorPrivacyChangedListener listener) {
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public void addSensorPrivacyListener(@NonNull final OnSensorPrivacyChangedListener listener) {
         synchronized (mListeners) {
             ISensorPrivacyListener iListener = mListeners.get(listener);
             if (iListener == null) {
@@ -196,15 +224,37 @@
      *
      * @hide
      */
-    public void addSensorPrivacyListener(@IndividualSensor int sensor,
-            final OnSensorPrivacyChangedListener listener) {
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public void addSensorPrivacyListener(@Sensors.Sensor int sensor,
+            @NonNull OnSensorPrivacyChangedListener listener) {
+        addSensorPrivacyListener(sensor, mContext.getMainExecutor(), listener);
+    }
+
+    /**
+     * Registers a new listener to receive notification when the state of sensor privacy
+     * changes.
+     *
+     * @param sensor the sensor to listen to changes to
+     * @param executor the executor to dispatch the callback on
+     * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
+     *                 privacy changes.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor,
+            @NonNull OnSensorPrivacyChangedListener listener) {
         synchronized (mListeners) {
             ISensorPrivacyListener iListener = mListeners.get(listener);
             if (iListener == null) {
                 iListener = new ISensorPrivacyListener.Stub() {
                     @Override
                     public void onSensorPrivacyChanged(boolean enabled) {
-                        listener.onSensorPrivacyChanged(enabled);
+                        executor.execute(() -> listener.onSensorPrivacyChanged(enabled));
                     }
                 };
                 mListeners.put(listener, iListener);
@@ -228,7 +278,10 @@
      *
      * @hide
      */
-    public void removeSensorPrivacyListener(OnSensorPrivacyChangedListener listener) {
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public void removeSensorPrivacyListener(@NonNull OnSensorPrivacyChangedListener listener) {
         synchronized (mListeners) {
             ISensorPrivacyListener iListener = mListeners.get(listener);
             if (iListener != null) {
@@ -249,6 +302,7 @@
      *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public boolean isSensorPrivacyEnabled() {
         try {
             return mService.isSensorPrivacyEnabled();
@@ -264,8 +318,10 @@
      *
      * @hide
      */
+    @SystemApi
     @TestApi
-    public boolean isIndividualSensorPrivacyEnabled(@IndividualSensor int sensor) {
+    @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
         try {
             return mService.isIndividualSensorPrivacyEnabled(mContext.getUserId(), sensor);
         } catch (RemoteException e) {
@@ -283,8 +339,7 @@
      */
     @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
-    public void setIndividualSensorPrivacy(@IndividualSensor int sensor,
-            boolean enable) {
+    public void setSensorPrivacy(@Sensors.Sensor int sensor, boolean enable) {
         try {
             mService.setIndividualSensorPrivacy(mContext.getUserId(), sensor, enable);
         } catch (RemoteException e) {
@@ -303,7 +358,7 @@
      */
     @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
-    public void setIndividualSensorPrivacyForProfileGroup(@IndividualSensor int sensor,
+    public void setSensorPrivacyForProfileGroup(@Sensors.Sensor int sensor,
             boolean enable) {
         try {
             mService.setIndividualSensorPrivacyForProfileGroup(mContext.getUserId(), sensor,
@@ -321,7 +376,8 @@
      *
      * @hide
      */
-    public void suppressIndividualSensorPrivacyReminders(@NonNull String packageName,
+    @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+    public void suppressSensorPrivacyReminders(@NonNull String packageName,
             boolean suppress) {
         try {
             mService.suppressIndividualSensorPrivacyReminders(mContext.getUserId(), packageName,
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 76d50bd..43ef33e 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -145,6 +145,12 @@
     int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
 
     /**
+     * Authentication cannot proceed because re-enrollment is required.
+     * @hide
+     */
+    int BIOMETRIC_ERROR_RE_ENROLL = 16;
+
+    /**
      * This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
      * because the authentication attempt was unsuccessful.
      * @hide
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index eafcf52..4385b1da 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -153,6 +153,12 @@
     int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
 
     /**
+     * Authentication cannot proceed because re-enrollment is required.
+     * @hide
+     */
+    int BIOMETRIC_ERROR_RE_ENROLL = 16;
+
+    /**
      * @hide
      */
     int FACE_ERROR_VENDOR_BASE = 1000;
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 01f0e71..30e24d2e 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -166,6 +166,12 @@
     public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
 
     /**
+     * Authentication cannot proceed because re-enrollment is required.
+     * @hide
+     */
+    int BIOMETRIC_ERROR_RE_ENROLL = 16;
+
+    /**
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
index 9457d8f1..41126b7 100644
--- a/core/java/android/hardware/display/DeviceProductInfo.java
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -16,69 +16,40 @@
 
 package android.hardware.display;
 
-import android.annotation.IntDef;
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 import java.util.Objects;
 
 /**
  * Product-specific information about the display or the directly connected device on the
  * display chain. For example, if the display is transitively connected, this field may contain
  * product information about the intermediate device.
+ * @hide
  */
 public final class DeviceProductInfo implements Parcelable {
-    /** @hide */
-    @IntDef(prefix = {"CONNECTION_TO_SINK_"}, value = {
-            CONNECTION_TO_SINK_UNKNOWN,
-            CONNECTION_TO_SINK_BUILT_IN,
-            CONNECTION_TO_SINK_DIRECT,
-            CONNECTION_TO_SINK_TRANSITIVE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ConnectionToSinkType { }
-
-    /** The device connection to the display sink is unknown. */
-    public static final int CONNECTION_TO_SINK_UNKNOWN =
-            IDeviceProductInfoConstants.CONNECTION_TO_SINK_UNKNOWN;
-
-    /** The display sink is built-in to the device */
-    public static final int CONNECTION_TO_SINK_BUILT_IN =
-            IDeviceProductInfoConstants.CONNECTION_TO_SINK_BUILT_IN;
-
-    /** The device is directly connected to the display sink. */
-    public static final int CONNECTION_TO_SINK_DIRECT =
-            IDeviceProductInfoConstants.CONNECTION_TO_SINK_DIRECT;
-
-    /** The device is transitively connected to the display sink. */
-    public static final int CONNECTION_TO_SINK_TRANSITIVE =
-            IDeviceProductInfoConstants.CONNECTION_TO_SINK_TRANSITIVE;
-
     private final String mName;
     private final String mManufacturerPnpId;
     private final String mProductId;
     private final Integer mModelYear;
     private final ManufactureDate mManufactureDate;
-    private final @ConnectionToSinkType int mConnectionToSinkType;
+    private final int[] mRelativeAddress;
 
-    /** @hide */
     public DeviceProductInfo(
             String name,
             String manufacturerPnpId,
             String productId,
             Integer modelYear,
             ManufactureDate manufactureDate,
-            int connectionToSinkType) {
+            int[] relativeAddress) {
         this.mName = name;
         this.mManufacturerPnpId = manufacturerPnpId;
         this.mProductId = productId;
         this.mModelYear = modelYear;
         this.mManufactureDate = manufactureDate;
-        this.mConnectionToSinkType = connectionToSinkType;
+        this.mRelativeAddress = relativeAddress;
     }
 
     private DeviceProductInfo(Parcel in) {
@@ -87,13 +58,12 @@
         mProductId = (String) in.readValue(null);
         mModelYear = (Integer) in.readValue(null);
         mManufactureDate = (ManufactureDate) in.readValue(null);
-        mConnectionToSinkType = in.readInt();
+        mRelativeAddress = in.createIntArray();
     }
 
     /**
      * @return Display name.
      */
-    @Nullable
     public String getName() {
         return mName;
     }
@@ -101,7 +71,6 @@
     /**
      * @return Manufacturer Plug and Play ID.
      */
-    @NonNull
     public String getManufacturerPnpId() {
         return mManufacturerPnpId;
     }
@@ -109,58 +78,32 @@
     /**
      * @return Manufacturer product ID.
      */
-    @NonNull
     public String getProductId() {
         return mProductId;
     }
 
     /**
-     * @return Model year of the device. Return -1 if not available. Typically,
-     * one of model year or manufacture year is available.
+     * @return Model year of the device. Typically exactly one of model year or
+     *      manufacture date will be present.
      */
-    public int getModelYear()  {
-        return mModelYear != null ? mModelYear : -1;
-    }
-
-    /**
-     * @return The year of manufacture, or -1 it is not available. Typically,
-     * one of model year or manufacture year is available.
-     */
-    public int getManufactureYear()  {
-        if (mManufactureDate == null) {
-            return -1;
-        }
-        return mManufactureDate.mYear != null ? mManufactureDate.mYear : -1;
-    }
-
-    /**
-     * @return The week of manufacture, or -1 it is not available. Typically,
-     * not present if model year is available.
-     */
-    public int getManufactureWeek() {
-        if (mManufactureDate == null) {
-            return -1;
-        }
-        return mManufactureDate.mWeek != null ?  mManufactureDate.mWeek : -1;
+    public Integer getModelYear() {
+        return mModelYear;
     }
 
     /**
      * @return Manufacture date. Typically exactly one of model year or manufacture
      * date will be present.
-     *
-     * @hide
      */
     public ManufactureDate getManufactureDate() {
         return mManufactureDate;
     }
 
     /**
-     * @return How the current device is connected to the display sink. For example, the display
-     * can be connected immediately to the device or there can be a receiver in between.
+     * @return Relative address in the display network. For example, for HDMI connected devices this
+     * can be its physical address. Each component of the address is in the range [0, 255].
      */
-    @ConnectionToSinkType
-    public int getConnectionToSinkType() {
-        return mConnectionToSinkType;
+    public int[] getRelativeAddress() {
+        return mRelativeAddress;
     }
 
     @Override
@@ -176,8 +119,8 @@
                 + mModelYear
                 + ", manufactureDate="
                 + mManufactureDate
-                + ", connectionToSinkType="
-                + mConnectionToSinkType
+                + ", relativeAddress="
+                + Arrays.toString(mRelativeAddress)
                 + '}';
     }
 
@@ -191,16 +134,16 @@
                 && Objects.equals(mProductId, that.mProductId)
                 && Objects.equals(mModelYear, that.mModelYear)
                 && Objects.equals(mManufactureDate, that.mManufactureDate)
-                && mConnectionToSinkType == that.mConnectionToSinkType;
+                && Arrays.equals(mRelativeAddress, that.mRelativeAddress);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mName, mManufacturerPnpId, mProductId, mModelYear, mManufactureDate,
-                mConnectionToSinkType);
+            Arrays.hashCode(mRelativeAddress));
     }
 
-    @NonNull public static final Creator<DeviceProductInfo> CREATOR =
+    public static final Creator<DeviceProductInfo> CREATOR =
             new Creator<DeviceProductInfo>() {
                 @Override
                 public DeviceProductInfo createFromParcel(Parcel in) {
@@ -219,13 +162,13 @@
     }
 
     @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
+    public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mName);
         dest.writeString(mManufacturerPnpId);
         dest.writeValue(mProductId);
         dest.writeValue(mModelYear);
         dest.writeValue(mManufactureDate);
-        dest.writeInt(mConnectionToSinkType);
+        dest.writeIntArray(mRelativeAddress);
     }
 
     /**
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index a9bcdeff..0256b7b 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -831,6 +831,9 @@
             case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
                 return context.getString(
                         com.android.internal.R.string.face_error_security_update_required);
+            case BIOMETRIC_ERROR_RE_ENROLL:
+                return context.getString(
+                        com.android.internal.R.string.face_recalibrate_notification_content);
             case FACE_ERROR_VENDOR: {
                 String[] msgArray = context.getResources().getStringArray(
                         com.android.internal.R.array.face_error_vendor);
@@ -1389,7 +1392,7 @@
             case FACE_ACQUIRED_PAN_TOO_EXTREME:
             case FACE_ACQUIRED_TILT_TOO_EXTREME:
             case FACE_ACQUIRED_ROLL_TOO_EXTREME:
-                return context.getString(R.string.face_acquired_not_detected);
+                return context.getString(R.string.face_acquired_poor_gaze);
 
             // Provide more detailed feedback for other soft errors.
             case FACE_ACQUIRED_INSUFFICIENT:
@@ -1445,13 +1448,17 @@
             case FACE_ACQUIRED_TOO_FAR:
                 return context.getString(R.string.face_acquired_too_far);
             case FACE_ACQUIRED_TOO_HIGH:
-                return context.getString(R.string.face_acquired_too_high);
-            case FACE_ACQUIRED_TOO_LOW:
+                // TODO(b/181269243): Change back once error codes are fixed.
                 return context.getString(R.string.face_acquired_too_low);
+            case FACE_ACQUIRED_TOO_LOW:
+                // TODO(b/181269243) Change back once error codes are fixed.
+                return context.getString(R.string.face_acquired_too_high);
             case FACE_ACQUIRED_TOO_RIGHT:
-                return context.getString(R.string.face_acquired_too_right);
-            case FACE_ACQUIRED_TOO_LEFT:
+                // TODO(b/181269243) Change back once error codes are fixed.
                 return context.getString(R.string.face_acquired_too_left);
+            case FACE_ACQUIRED_TOO_LEFT:
+                // TODO(b/181269243) Change back once error codes are fixed.
+                return context.getString(R.string.face_acquired_too_right);
             case FACE_ACQUIRED_POOR_GAZE:
                 return context.getString(R.string.face_acquired_poor_gaze);
             case FACE_ACQUIRED_NOT_DETECTED:
diff --git a/core/java/android/hardware/soundtrigger/OWNERS b/core/java/android/hardware/soundtrigger/OWNERS
index 816bc6b..e5d0370 100644
--- a/core/java/android/hardware/soundtrigger/OWNERS
+++ b/core/java/android/hardware/soundtrigger/OWNERS
@@ -1 +1,2 @@
-include /core/java/android/media/soundtrigger/OWNERS
+ytai@google.com
+elaurent@google.com
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index 79f9e6e..dbb3127 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -15,9 +15,6 @@
  */
 package android.net;
 
-import static android.Manifest.permission.NETWORK_STACK;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -26,8 +23,7 @@
 import android.os.IBinder;
 import android.os.ServiceManager;
 
-import java.util.ArrayList;
-import java.util.Arrays;
+import com.android.net.module.util.PermissionUtils;
 /**
  * Constants and utilities for client code communicating with the network stack service.
  * @hide
@@ -79,9 +75,14 @@
      * @param context {@link android.content.Context} for the process.
      *
      * @hide
+     *
+     * @deprecated Use {@link PermissionUtils#enforceNetworkStackPermission} instead.
+     *
+     * TODO: remove this method and let the users call to PermissionUtils directly.
      */
+    @Deprecated
     public static void checkNetworkStackPermission(final @NonNull Context context) {
-        checkNetworkStackPermissionOr(context);
+        PermissionUtils.enforceNetworkStackPermission(context);
     }
 
     /**
@@ -92,31 +93,14 @@
      * @param otherPermissions The set of permissions that could be the candidate permissions , or
      *                         empty string if none of other permissions needed.
      * @hide
+     *
+     * @deprecated Use {@link PermissionUtils#enforceNetworkStackPermissionOr} instead.
+     *
+     * TODO: remove this method and let the users call to PermissionUtils directly.
      */
+    @Deprecated
     public static void checkNetworkStackPermissionOr(final @NonNull Context context,
             final @NonNull String... otherPermissions) {
-        ArrayList<String> permissions = new ArrayList<String>(Arrays.asList(otherPermissions));
-        permissions.add(NETWORK_STACK);
-        permissions.add(PERMISSION_MAINLINE_NETWORK_STACK);
-        enforceAnyPermissionOf(context, permissions.toArray(new String[0]));
+        PermissionUtils.enforceNetworkStackPermissionOr(context, otherPermissions);
     }
-
-    private static void enforceAnyPermissionOf(final @NonNull Context context,
-            final @NonNull String... permissions) {
-        if (!checkAnyPermissionOf(context, permissions)) {
-            throw new SecurityException("Requires one of the following permissions: "
-                + String.join(", ", permissions) + ".");
-        }
-    }
-
-    private static boolean checkAnyPermissionOf(final @NonNull Context context,
-            final @NonNull String... permissions) {
-        for (String permission : permissions) {
-            if (context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
-                return true;
-            }
-        }
-        return false;
-    }
-
 }
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
index b172ccc..f0e7da7 100644
--- a/core/java/android/net/UidRange.java
+++ b/core/java/android/net/UidRange.java
@@ -42,10 +42,6 @@
         stop  = stopUid;
     }
 
-    public static UidRange createForUser(int userId) {
-        return new UidRange(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1);
-    }
-
     /** Creates a UidRange for the specified user. */
     public static UidRange createForUser(UserHandle user) {
         final UserHandle nextUser = UserHandle.of(user.getIdentifier() + 1);
diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl
index 555e9b5..d91cef5 100644
--- a/core/java/android/net/vcn/IVcnStatusCallback.aidl
+++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl
@@ -17,8 +17,9 @@
 package android.net.vcn;
 
 /** @hide */
-interface IVcnStatusCallback {
+oneway interface IVcnStatusCallback {
     void onEnteredSafeMode();
+    void onVcnStatusChanged(int statusCode);
     void onGatewayConnectionError(
             in int[] gatewayNetworkCapabilities,
             int errorCode,
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 729e68a..eb8c251 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -349,6 +349,56 @@
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
+        VCN_STATUS_CODE_NOT_CONFIGURED,
+        VCN_STATUS_CODE_INACTIVE,
+        VCN_STATUS_CODE_ACTIVE,
+        VCN_STATUS_CODE_SAFE_MODE
+    })
+    public @interface VcnStatusCode {}
+
+    /**
+     * Value indicating that the VCN for the subscription group is not configured, or that the
+     * callback is not privileged for the subscription group.
+     *
+     * @hide
+     */
+    public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0;
+
+    /**
+     * Value indicating that the VCN for the subscription group is inactive.
+     *
+     * <p>A VCN is inactive if a {@link VcnConfig} is present for the subscription group, but the
+     * provisioning package is not privileged.
+     *
+     * @hide
+     */
+    public static final int VCN_STATUS_CODE_INACTIVE = 1;
+
+    /**
+     * Value indicating that the VCN for the subscription group is active.
+     *
+     * <p>A VCN is active if a {@link VcnConfig} is present for the subscription, the provisioning
+     * package is privileged, and the VCN is not in Safe Mode. In other words, a VCN is considered
+     * active while it is connecting, fully connected, and disconnecting.
+     *
+     * @hide
+     */
+    public static final int VCN_STATUS_CODE_ACTIVE = 2;
+
+    /**
+     * Value indicating that the VCN for the subscription group is in Safe Mode.
+     *
+     * <p>A VCN will be put into Safe Mode if any of the gateway connections were unable to
+     * establish a connection within a system-determined timeout (while underlying networks were
+     * available).
+     *
+     * @hide
+     */
+    public static final int VCN_STATUS_CODE_SAFE_MODE = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
         VCN_ERROR_CODE_INTERNAL_ERROR,
         VCN_ERROR_CODE_CONFIG_ERROR,
         VCN_ERROR_CODE_NETWORK_ERROR
@@ -403,8 +453,18 @@
          *
          * <p>A VCN-configuring app may opt to exit safe mode by (re)setting the VCN configuration
          * via {@link #setVcnConfig(ParcelUuid, VcnConfig)}.
+         *
+         * @hide
          */
-        public abstract void onEnteredSafeMode();
+        public void onEnteredSafeMode() {}
+
+        /**
+         * Invoked when status of the VCN for this callback's subscription group changes.
+         *
+         * @param statusCode the code for the status change encountered by this {@link
+         *     VcnStatusCallback}'s subscription group.
+         */
+        public abstract void onVcnStatusChanged(@VcnStatusCode int statusCode);
 
         /**
          * Invoked when a VCN Gateway Connection corresponding to this callback's subscription
@@ -436,6 +496,11 @@
      * <p>A {@link VcnStatusCallback} will only be invoked if the registering package has carrier
      * privileges for the specified subscription at the time of invocation.
      *
+     * <p>{@link VcnStatusCallback#onVcnStatusChanged(int)} will be invoked on registration with the
+     * current status for the specified subscription group's VCN. If the registrant is not
+     * privileged for this subscription group, {@link #VCN_STATUS_CODE_NOT_CONFIGURED} will be
+     * returned.
+     *
      * @param subscriptionGroup The subscription group to match for callbacks
      * @param executor The {@link Executor} to be used for invoking callbacks
      * @param callback The VcnStatusCallback to be registered
@@ -539,6 +604,12 @@
                     () -> mExecutor.execute(() -> mCallback.onEnteredSafeMode()));
         }
 
+        @Override
+        public void onVcnStatusChanged(@VcnStatusCode int statusCode) {
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> mCallback.onVcnStatusChanged(statusCode)));
+        }
+
         // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling'
         @Override
         public void onGatewayConnectionError(
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index c6efaac..2b6f336 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -47,6 +47,7 @@
             POWER_COMPONENT_SYSTEM_SERVICES,
             POWER_COMPONENT_SENSORS,
             POWER_COMPONENT_GNSS,
+            POWER_COMPONENT_WIFI,
             POWER_COMPONENT_WAKELOCK,
             POWER_COMPONENT_SCREEN,
             POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
@@ -66,6 +67,7 @@
     public static final int POWER_COMPONENT_MOBILE_RADIO = 8;
     public static final int POWER_COMPONENT_SENSORS = 9;
     public static final int POWER_COMPONENT_GNSS = 10;
+    public static final int POWER_COMPONENT_WIFI = 11;
     public static final int POWER_COMPONENT_WAKELOCK = 12;
     public static final int POWER_COMPONENT_SCREEN = 13;
     // Power that is re-attributed to other battery consumers. For example, for System Server
@@ -94,6 +96,7 @@
             TIME_COMPONENT_MOBILE_RADIO,
             TIME_COMPONENT_SENSORS,
             TIME_COMPONENT_GNSS,
+            TIME_COMPONENT_WIFI,
             TIME_COMPONENT_WAKELOCK,
             TIME_COMPONENT_SCREEN,
     })
@@ -112,6 +115,7 @@
     public static final int TIME_COMPONENT_MOBILE_RADIO = 8;
     public static final int TIME_COMPONENT_SENSORS = 9;
     public static final int TIME_COMPONENT_GNSS = 10;
+    public static final int TIME_COMPONENT_WIFI = 11;
     public static final int TIME_COMPONENT_WAKELOCK = 12;
     public static final int TIME_COMPONENT_SCREEN = 13;
 
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index bb40d90..dfa0c39 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -16,9 +16,13 @@
 
 package android.os;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Contains power consumption data attributed to a specific UID.
  *
@@ -26,9 +30,37 @@
  */
 public final class UidBatteryConsumer extends BatteryConsumer implements Parcelable {
 
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            STATE_FOREGROUND,
+            STATE_BACKGROUND
+    })
+    public @interface State {
+    }
+
+    /**
+     * The state of an application when it is either running a foreground (top) activity
+     * or a foreground service.
+     */
+    public static final int STATE_FOREGROUND = 0;
+
+    /**
+     * The state of an application when it is running in the background, including the following
+     * states:
+     *
+     * {@link android.app.ActivityManager#PROCESS_STATE_IMPORTANT_BACKGROUND},
+     * {@link android.app.ActivityManager#PROCESS_STATE_TRANSIENT_BACKGROUND},
+     * {@link android.app.ActivityManager#PROCESS_STATE_BACKUP},
+     * {@link android.app.ActivityManager#PROCESS_STATE_SERVICE},
+     * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER}.
+     */
+    public static final int STATE_BACKGROUND = 1;
+
     private final int mUid;
     @Nullable
     private final String mPackageWithHighestDrain;
+    private final long mTimeInForegroundMs;
+    private final long mTimeInBackgroundMs;
 
     public int getUid() {
         return mUid;
@@ -39,16 +71,33 @@
         return mPackageWithHighestDrain;
     }
 
+    /**
+     * Returns the amount of time in milliseconds this UID spent in the specified state.
+     */
+    public long getTimeInStateMs(@State int state) {
+        switch (state) {
+            case STATE_BACKGROUND:
+                return mTimeInBackgroundMs;
+            case STATE_FOREGROUND:
+                return mTimeInForegroundMs;
+        }
+        return 0;
+    }
+
     private UidBatteryConsumer(@NonNull Builder builder) {
         super(builder.mPowerComponentsBuilder.build());
         mUid = builder.mUid;
         mPackageWithHighestDrain = builder.mPackageWithHighestDrain;
+        mTimeInForegroundMs = builder.mTimeInForegroundMs;
+        mTimeInBackgroundMs = builder.mTimeInBackgroundMs;
     }
 
     private UidBatteryConsumer(@NonNull Parcel source) {
         super(new PowerComponents(source));
         mUid = source.readInt();
         mPackageWithHighestDrain = source.readString();
+        mTimeInForegroundMs = source.readLong();
+        mTimeInBackgroundMs = source.readLong();
     }
 
     /**
@@ -59,6 +108,8 @@
         super.writeToParcel(dest, flags);
         dest.writeInt(mUid);
         dest.writeString(mPackageWithHighestDrain);
+        dest.writeLong(mTimeInForegroundMs);
+        dest.writeLong(mTimeInBackgroundMs);
     }
 
     @NonNull
@@ -84,6 +135,8 @@
         private final BatteryStats.Uid mBatteryStatsUid;
         private final int mUid;
         private String mPackageWithHighestDrain;
+        public long mTimeInForegroundMs;
+        public long mTimeInBackgroundMs;
         private boolean mExcludeFromBatteryUsageStats;
 
         public Builder(int customPowerComponentCount, int customTimeComponentCount,
@@ -113,6 +166,25 @@
         }
 
         /**
+         * Sets the duration, in milliseconds, that this UID was active in a particular state,
+         * such as foreground or background.
+         */
+        @NonNull
+        public Builder setTimeInStateMs(@State int state, long timeInStateMs) {
+            switch (state) {
+                case STATE_FOREGROUND:
+                    mTimeInForegroundMs = timeInStateMs;
+                    break;
+                case STATE_BACKGROUND:
+                    mTimeInBackgroundMs = timeInStateMs;
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported state: " + state);
+            }
+            return this;
+        }
+
+        /**
          * Marks the UidBatteryConsumer for exclusion from the result set.
          */
         public Builder excludeFromBatteryUsageStats() {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8bdfd3d..682754e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1688,6 +1688,7 @@
      * @return Whether guest user is always ephemeral
      * @hide
      */
+    @TestApi
     public static boolean isGuestUserEphemeral() {
         return Resources.getSystem()
                 .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
@@ -1802,6 +1803,20 @@
     }
 
     /**
+     * @return the user type of the context user.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    @UserHandleAware
+    public @NonNull String getUserType() {
+        UserInfo userInfo = getUserInfo(mUserId);
+        return userInfo == null ? "" : userInfo.userType;
+    }
+
+    /**
      * Returns the user name of the context user. This call is only available to applications on
      * the system image; it requires the {@code android.permission.MANAGE_USERS} or {@code
      * android.permission.GET_ACCOUNTS_PRIVILEGED} permissions.
@@ -1809,7 +1824,8 @@
      * @return the user name
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
-            android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}, conditional = true)
+            android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED,
+            android.Manifest.permission.CREATE_USERS}, conditional = true)
     @UserHandleAware
     public @NonNull String getUserName() {
         if (UserHandle.myUserId() == mUserId) {
@@ -2792,6 +2808,7 @@
      */
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
+    @TestApi
     public @Nullable UserInfo createUser(@Nullable String name, @NonNull String userType,
             @UserInfoFlag int flags) {
         try {
@@ -2828,6 +2845,7 @@
      * @throws UserOperationException if the user could not be created.
      * @hide
      */
+    @TestApi
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
     public @NonNull UserInfo preCreateUser(@NonNull String userType)
@@ -2976,10 +2994,11 @@
      *
      * @hide
      */
+    @TestApi
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
-    public UserInfo createProfileForUser(String name, @NonNull String userType,
-            @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages) {
+    public @Nullable UserInfo createProfileForUser(@Nullable String name, @NonNull String userType,
+            @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) {
         try {
             return mService.createProfileForUserWithThrow(name, userType, flags, userId,
                     disallowedPackages);
@@ -3022,9 +3041,10 @@
      *
      * @hide
      */
+    @TestApi
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
-    public UserInfo createRestrictedProfile(String name) {
+    public @Nullable UserInfo createRestrictedProfile(@Nullable String name) {
         try {
             UserHandle parentUserHandle = Process.myUserHandle();
             UserInfo user = mService.createRestrictedProfileWithThrow(name,
@@ -3248,10 +3268,11 @@
 
     /**
      * Return the number of users currently created on the device.
-     * <p>This API is not for use by third-party apps. It requires the {@code MANAGE_USERS}
-     * permission.</p>
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public int getUserCount() {
         List<UserInfo> users = getUsers();
         return users != null ? users.size() : 1;
@@ -3274,7 +3295,10 @@
      * @hide
      */
     @UnsupportedAppUsage
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public List<UserInfo> getUsers() {
         return getUsers(/*excludePartial= */ true, /* excludeDying= */ false,
                 /* excludePreCreated= */ true);
@@ -3292,7 +3316,10 @@
      * @return the list of users that were created.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public @NonNull List<UserInfo> getAliveUsers() {
         return getUsers(/*excludePartial= */ true, /* excludeDying= */ true,
                 /* excludePreCreated= */ true);
@@ -3306,7 +3333,10 @@
      */
     @Deprecated
     @UnsupportedAppUsage
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
         return getUsers(/*excludePartial= */ true, excludeDying,
                 /* excludePreCreated= */ true);
@@ -3317,8 +3347,12 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
-    public List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
+    @TestApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
             boolean excludePreCreated) {
         try {
             return mService.getUsers(excludePartial, excludeDying, excludePreCreated);
@@ -3335,7 +3369,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public @NonNull List<UserHandle> getUserHandles(boolean excludeDying) {
         List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying,
                 /* excludePreCreated= */ true);
@@ -3354,7 +3391,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public long[] getSerialNumbersOfUsers(boolean excludeDying) {
         List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying,
                 /* excludePreCreated= */ true);
@@ -3678,7 +3718,10 @@
      * @hide
      */
     @UnsupportedAppUsage
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS
+    })
     public UserInfo getProfileParent(@UserIdInt int userId) {
         try {
             return mService.getProfileParent(userId);
@@ -3697,7 +3740,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS
+    })
     public @Nullable UserHandle getProfileParent(@NonNull UserHandle user) {
         UserInfo info = getProfileParent(user.getIdentifier());
 
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index cec6a1f..592e98a 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -241,10 +241,12 @@
     }
 
     /**
-     * Checks if device supports V2 calls (e.g. PerUid).
+     * 0 - IncFs is disabled.
+     * 1 - IncFs v1, core features, no PerUid support. Optional in R.
+     * 2 - IncFs v2, PerUid support, fs-verity support. Required in S.
      */
-    public static boolean isV2Available() {
-        return nativeIsV2Available();
+    public static int getVersion() {
+        return nativeIsEnabled() ? nativeIsV2Available() ? 2 : 1 : 0;
     }
 
     /**
diff --git a/core/java/android/os/strictmode/IncorrectContextUseViolation.java b/core/java/android/os/strictmode/IncorrectContextUseViolation.java
index 11d26ca..d8c22fd 100644
--- a/core/java/android/os/strictmode/IncorrectContextUseViolation.java
+++ b/core/java/android/os/strictmode/IncorrectContextUseViolation.java
@@ -24,7 +24,7 @@
  * instance.
  *
  * @see Context#getSystemService(String)
- * @see Context#isUiContext(Context)
+ * @see Context#isUiContext
  * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
  */
 public final class IncorrectContextUseViolation extends Violation {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 4354920..6e89faf 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -157,6 +157,14 @@
     public static final String NAMESPACE_BLUETOOTH = "bluetooth";
 
     /**
+     * Namespace for features relating to clipboard.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_CLIPBOARD = "clipboard";
+
+    /**
      * Namespace for all networking connectivity related features.
      *
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ff10b0c..09d0af1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -417,6 +417,22 @@
             "android.settings.MANAGE_UNKNOWN_APP_SOURCES";
 
     /**
+     * Activity Action: Show settings to allow configuration of
+     * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission
+     *
+     * Input: Optionally, the Intent's data URI can specify the application package name to
+     * directly invoke the management GUI specific to the package name. For example
+     * "package:com.my.app".
+     * <p>
+     * Output: When a package data uri is passed as input, the activity result is set to
+     * {@link android.app.Activity#RESULT_OK} if the permission was granted to the app. Otherwise,
+     * the result is set to {@link android.app.Activity#RESULT_CANCELED}.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM =
+            "android.settings.REQUEST_SCHEDULE_EXACT_ALARM";
+
+    /**
      * Activity Action: Show settings to allow configuration of cross-profile access for apps
      *
      * Input: Optionally, the Intent's data URI can specify the application package name to
diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java
index 074d5f1..030b863 100644
--- a/core/java/android/provider/SimPhonebookContract.java
+++ b/core/java/android/provider/SimPhonebookContract.java
@@ -44,8 +44,11 @@
  * The contract between the provider of contact records on the device's SIM cards and applications.
  * Contains definitions of the supported URIs and columns.
  *
- * <p>This content provider does not support any of the QUERY_ARG_SQL* bundle arguments. An
- * IllegalArgumentException will be thrown if these are included.
+ * <h3>Permissions</h3>
+ * <p>
+ * Querying this provider requires {@link android.Manifest.permission#READ_CONTACTS} and writing
+ * to this provider requires {@link android.Manifest.permission#WRITE_CONTACTS}
+ * </p>
  */
 public final class SimPhonebookContract {
 
@@ -85,7 +88,73 @@
         }
     }
 
-    /** Constants for the contact records on a SIM card. */
+    /**
+     * Constants for the contact records on a SIM card.
+     *
+     * <h3 id="simrecords-data">Data</h3>
+     * <p>
+     * Data is stored in a specific elementary file on a specific SIM card and these are isolated
+     * from each other. SIM cards are identified by their subscription ID. SIM cards may not support
+     * all or even any of the elementary file types. A SIM will have constraints on
+     * the values of the data that can be stored in each elementary file. The available SIMs,
+     * their supported elementary file types and the constraints on the data can be discovered by
+     * querying {@link ElementaryFiles#CONTENT_URI}. Each elementary file has a fixed capacity
+     * for the number of records that may be stored. This can be determined from the value
+     * of the {@link ElementaryFiles#MAX_RECORDS} column.
+     * </p>
+     * <p>
+     * The {@link SimRecords#PHONE_NUMBER} column can only contain dialable characters and this
+     * applies regardless of the SIM that is being used. See
+     * {@link android.telephony.PhoneNumberUtils#isDialable(char)} for more details. Additionally
+     * the phone number can contain at most {@link ElementaryFiles#PHONE_NUMBER_MAX_LENGTH}
+     * characters. The {@link SimRecords#NAME} column can contain at most
+     * {@link ElementaryFiles#NAME_MAX_LENGTH} bytes when it is encoded for storage on the SIM.
+     * Encoding is done internally and so the name should be provided unencoded but the number of
+     * bytes required to encode it will vary depending on the characters it contains. This length
+     * can be determined by calling
+     * {@link SimRecords#getEncodedNameLength(ContentResolver, String)}.
+     * </p>
+     * <h3>Operations </h3>
+     * <dl>
+     * <dd><b>Insert</b></dd>
+     * <p>
+     * Only {@link ElementaryFiles#EF_ADN} supports inserts. {@link SimRecords#PHONE_NUMBER}
+     * is a required column. If the value provided for this column is missing, null, empty
+     * or violates the requirements discussed in the <a href="#simrecords-data">Data</a>
+     * section above an {@link IllegalArgumentException} will be thrown. The
+     * {@link SimRecords#NAME} column may be omitted but if provided and it violates any of
+     * the requirements discussed in the <a href="#simrecords-data">Data</a> section above
+     * an {@link IllegalArgumentException} will be thrown.
+     * </p>
+     * <p>
+     * If an insert is not possible because the elementary file is full then an
+     * {@link IllegalStateException} will be thrown.
+     * </p>
+     * <dd><b>Update</b></dd>
+     * <p>
+     * Updates can only be performed for individual records on {@link ElementaryFiles#EF_ADN}.
+     * A specific record is referenced via the Uri returned by
+     * {@link SimRecords#getItemUri(int, int, int)}. Updates have the same constraints and
+     * behavior for the {@link SimRecords#PHONE_NUMBER} and {@link SimRecords#NAME} as insert.
+     * However, in the case of update the {@link SimRecords#PHONE_NUMBER} may be omitted as
+     * the existing record will already have a valid value.
+     * </p>
+     * <dd><b>Delete</b></dd>
+     * <p>
+     * Delete may only be performed for individual records on {@link ElementaryFiles#EF_ADN}.
+     * Deleting records will free up space for use by future inserts.
+     * </p>
+     * <dd><b>Query</b></dd>
+     * <p>
+     * All the records stored on a specific elementary file can be read via a Uri returned by
+     * {@link SimRecords#getContentUri(int, int)}. This query always returns all records; there
+     * is no support for filtering via a selection. An individual record can be queried via a Uri
+     * returned by {@link SimRecords#getItemUri(int, int, int)}. Queries will throw an
+     * {@link IllegalArgumentException} when the SIM with the subscription ID or the elementary file
+     * type are invalid or unavailable.
+     * </p>
+     * </dl>
+     */
     public static final class SimRecords {
 
         /**
@@ -197,8 +266,8 @@
          * be discovered by querying {@link ElementaryFiles#CONTENT_URI}.
          *
          * <p>If a SIM with the provided subscription ID does not exist or the SIM with the provided
-         * subscription ID doesn't support the specified entity file then queries will return
-         * and empty cursor and inserts will throw an {@link IllegalArgumentException}
+         * subscription ID doesn't support the specified entity file then all operations will
+         * throw an {@link IllegalArgumentException}.
          *
          * @param subscriptionId the subscriptionId of the SIM card that this Uri will reference
          * @param efType         the elementary file on the SIM that this Uri will reference
@@ -233,6 +302,9 @@
          *                       must be greater than 0. If there is no record with this record
          *                       number in the specified entity file then it will be treated as a
          *                       non-existent record.
+         * @see ElementaryFiles#SUBSCRIPTION_ID
+         * @see ElementaryFiles#EF_TYPE
+         * @see #RECORD_NUMBER
          */
         @NonNull
         public static Uri getItemUri(
@@ -287,7 +359,28 @@
 
     }
 
-    /** Constants for metadata about the elementary files of the SIM cards in the phone. */
+    /**
+     * Constants for metadata about the elementary files of the SIM cards in the phone.
+     *
+     * <h3>Operations </h3>
+     * <dl>
+     * <dd><b>Insert</b></dd>
+     * <p>Insert is not supported for the Uris defined in this class.</p>
+     * <dd><b>Update</b></dd>
+     * <p>Update is not supported for the Uris defined in this class.</p>
+     * <dd><b>Delete</b></dd>
+     * <p>Delete is not supported for the Uris defined in this class.</p>
+     * <dd><b>Query</b></dd>
+     * <p>
+     * The elementary files for all the inserted SIMs can be read via
+     * {@link ElementaryFiles#CONTENT_URI}. Unsupported elementary files are omitted from the
+     * results. This Uri always returns all supported elementary files for all available SIMs; it
+     * does not support filtering via a selection. A specific elementary file can be queried
+     * via a Uri returned by {@link ElementaryFiles#getItemUri(int, int)}. If the elementary file
+     * referenced by this Uri is unsupported by the SIM then the query will return an empty cursor.
+     * </p>
+     * </dl>
+     */
     public static final class ElementaryFiles {
 
         /** {@link SubscriptionInfo#getSimSlotIndex()} of the SIM for this row. */
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index ffb4a6e..3c355d4 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -270,13 +270,13 @@
     /**
      * Notify call state changed on certain subscription.
      *
-     * @param subId for which call state changed.
      * @param slotIndex for which call state changed. Can be derived from subId except when subId is
      * invalid.
+     * @param subId for which call state changed.
      * @param state latest call state. e.g, offhook, ringing
      * @param incomingNumber incoming phone number.
      */
-    public void notifyCallStateChanged(int subId, int slotIndex, @CallState int state,
+    public void notifyCallStateChanged(int slotIndex, int subId, @CallState int state,
             @Nullable String incomingNumber) {
         try {
             sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber);
@@ -329,12 +329,12 @@
     /**
      * Notify {@link ServiceState} update on certain subscription.
      *
-     * @param subId for which the service state changed.
      * @param slotIndex for which the service state changed. Can be derived from subId except
      * subId is invalid.
+     * @param subId for which the service state changed.
      * @param state service state e.g, in service, out of service or roaming status.
      */
-    public void notifyServiceStateChanged(int subId, int slotIndex, @NonNull ServiceState state) {
+    public void notifyServiceStateChanged(int slotIndex, int subId, @NonNull ServiceState state) {
         try {
             sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state);
         } catch (RemoteException ex) {
@@ -345,12 +345,12 @@
     /**
      * Notify {@link SignalStrength} update on certain subscription.
      *
-     * @param subId for which the signalstrength changed.
      * @param slotIndex for which the signalstrength changed. Can be derived from subId except when
      * subId is invalid.
+     * @param subId for which the signalstrength changed.
      * @param signalStrength e.g, signalstrength level {@see SignalStrength#getLevel()}
      */
-    public void notifySignalStrengthChanged(int subId, int slotIndex,
+    public void notifySignalStrengthChanged(int slotIndex, int subId,
             @NonNull SignalStrength signalStrength) {
         try {
             sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength);
@@ -363,13 +363,13 @@
      * Notify changes to the message-waiting indicator on certain subscription. e.g, The status bar
      * uses message waiting indicator to determine when to display the voicemail icon.
      *
-     * @param subId for which message waiting indicator changed.
      * @param slotIndex for which message waiting indicator changed. Can be derived from subId
      * except when subId is invalid.
+     * @param subId for which message waiting indicator changed.
      * @param msgWaitingInd {@code true} indicates there is message-waiting indicator, {@code false}
      * otherwise.
      */
-    public void notifyMessageWaitingChanged(int subId, int slotIndex, boolean msgWaitingInd) {
+    public void notifyMessageWaitingChanged(int slotIndex, int subId, boolean msgWaitingInd) {
         try {
             sRegistry.notifyMessageWaitingChangedForPhoneId(slotIndex, subId, msgWaitingInd);
         } catch (RemoteException ex) {
@@ -410,9 +410,9 @@
     /**
      * Notify changes to default (Internet) data connection state on certain subscription.
      *
-     * @param subId for which data connection state changed.
      * @param slotIndex for which data connections state changed. Can be derived from subId except
      * when subId is invalid.
+     * @param subId for which data connection state changed.
      * @param preciseState the PreciseDataConnectionState
      *
      * @see PreciseDataConnectionState
@@ -431,13 +431,13 @@
     /**
      * Notify {@link CallQuality} change on certain subscription.
      *
-     * @param subId for which call quality state changed.
      * @param slotIndex for which call quality state changed. Can be derived from subId except when
      * subId is invalid.
+     * @param subId for which call quality state changed.
      * @param callQuality Information about call quality e.g, call quality level
      * @param networkType associated with this data connection. e.g, LTE
      */
-    public void notifyCallQualityChanged(int subId, int slotIndex, @NonNull CallQuality callQuality,
+    public void notifyCallQualityChanged(int slotIndex, int subId, @NonNull CallQuality callQuality,
         @NetworkType int networkType) {
         try {
             sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType);
@@ -449,11 +449,11 @@
     /**
      * Notify emergency number list changed on certain subscription.
      *
-     * @param subId for which emergency number list changed.
      * @param slotIndex for which emergency number list changed. Can be derived from subId except
      * when subId is invalid.
+     * @param subId for which emergency number list changed.
      */
-    public void notifyEmergencyNumberList(int subId, int slotIndex) {
+    public void notifyEmergencyNumberList( int slotIndex, int subId) {
         try {
             sRegistry.notifyEmergencyNumberList(slotIndex, subId);
         } catch (RemoteException ex) {
@@ -494,13 +494,13 @@
     /**
      * Notify radio power state changed on certain subscription.
      *
-     * @param subId for which radio power state changed.
      * @param slotIndex for which radio power state changed. Can be derived from subId except when
      * subId is invalid.
+     * @param subId for which radio power state changed.
      * @param radioPowerState the current modem radio state.
      */
-    public void notifyRadioPowerStateChanged(int subId, int slotIndex,
-        @RadioPowerState int radioPowerState) {
+    public void notifyRadioPowerStateChanged(int slotIndex, int subId,
+            @RadioPowerState int radioPowerState) {
         try {
             sRegistry.notifyRadioPowerStateChanged(slotIndex, subId, radioPowerState);
         } catch (RemoteException ex) {
@@ -538,13 +538,13 @@
      * Notify data activation state changed on certain subscription.
      * @see TelephonyManager#getDataActivationState()
      *
-     * @param subId for which data activation state changed.
      * @param slotIndex for which data activation state changed. Can be derived from subId except
      * when subId is invalid.
+     * @param subId for which data activation state changed.
      * @param activationState sim activation state e.g, activated.
      */
-    public void notifyDataActivationStateChanged(int subId, int slotIndex,
-        @SimActivationState int activationState) {
+    public void notifyDataActivationStateChanged(int slotIndex, int subId,
+            @SimActivationState int activationState) {
         try {
             sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId,
                     SIM_ACTIVATION_TYPE_DATA, activationState);
@@ -557,13 +557,13 @@
      * Notify voice activation state changed on certain subscription.
      * @see TelephonyManager#getVoiceActivationState()
      *
-     * @param subId for which voice activation state changed.
      * @param slotIndex for which voice activation state changed. Can be derived from subId except
      * subId is invalid.
+     * @param subId for which voice activation state changed.
      * @param activationState sim activation state e.g, activated.
      */
-    public void notifyVoiceActivationStateChanged(int subId, int slotIndex,
-        @SimActivationState int activationState) {
+    public void notifyVoiceActivationStateChanged(int slotIndex, int subId,
+            @SimActivationState int activationState) {
         try {
             sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId,
                     SIM_ACTIVATION_TYPE_VOICE, activationState);
@@ -576,9 +576,9 @@
      * Notify User mobile data state changed on certain subscription. e.g, mobile data is enabled
      * or disabled.
      *
-     * @param subId for which mobile data state has changed.
      * @param slotIndex for which mobile data state has changed. Can be derived from subId except
      * when subId is invalid.
+     * @param subId for which mobile data state has changed.
      * @param state {@code true} indicates mobile data is enabled/on. {@code false} otherwise.
      */
     public void notifyUserMobileDataStateChanged(int slotIndex, int subId, boolean state) {
@@ -599,7 +599,7 @@
      * @param telephonyDisplayInfo The display info.
      */
     public void notifyDisplayInfoChanged(int slotIndex, int subscriptionId,
-                                         @NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
+            @NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
         try {
             sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, telephonyDisplayInfo);
         } catch (RemoteException ex) {
@@ -640,14 +640,14 @@
      * Notify precise call state changed on certain subscription, including foreground, background
      * and ringcall states.
      *
-     * @param subId for which precise call state changed.
      * @param slotIndex for which precise call state changed. Can be derived from subId except when
      * subId is invalid.
+     * @param subId for which precise call state changed.
      * @param ringCallPreciseState ringCall state.
      * @param foregroundCallPreciseState foreground call state.
      * @param backgroundCallPreciseState background call state.
      */
-    public void notifyPreciseCallState(int subId, int slotIndex,
+    public void notifyPreciseCallState(int slotIndex, int subId,
             @PreciseCallStates int ringCallPreciseState,
             @PreciseCallStates int foregroundCallPreciseState,
             @PreciseCallStates int backgroundCallPreciseState) {
@@ -790,9 +790,10 @@
      * @param reason Reason for data enabled/disabled. See {@code REASON_*} in
      * {@link TelephonyManager}.
      */
-    public void notifyDataEnabled(boolean enabled, @TelephonyManager.DataEnabledReason int reason) {
+    public void notifyDataEnabled(int slotIndex, int subId, boolean enabled,
+            @TelephonyManager.DataEnabledReason int reason) {
         try {
-            sRegistry.notifyDataEnabled(enabled, reason);
+            sRegistry.notifyDataEnabled(slotIndex, subId, enabled, reason);
         } catch (RemoteException ex) {
             // system server crash
         }
@@ -801,11 +802,11 @@
     /**
      * Notify emergency number list changed on certain subscription.
      *
-     * @param subId for which emergency number list changed.
      * @param slotIndex for which emergency number list changed. Can be derived from subId except
      * when subId is invalid.
+     * @param subId for which emergency number list changed.
      */
-    public void notifyAllowedNetworkTypesChanged(int subId, int slotIndex,
+    public void notifyAllowedNetworkTypesChanged(int slotIndex, int subId,
             Map<Integer, Long> allowedNetworkTypeList) {
         try {
             sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, allowedNetworkTypeList);
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index aef185c..33b7c75 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -18,6 +18,7 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.CurrentTimeMillisLong;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -101,7 +102,7 @@
      *
      * If there is no update, this return 0.
      */
-    public long getLastModifiedTimeMillis() {
+    public @CurrentTimeMillisLong long getLastModifiedTimeMillis() {
         return mLastModifiedTimeMillis;
     }
 
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index b229212..2b577d0 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -46,6 +46,9 @@
             "settings_do_not_restore_preserved";
     /** @hide */
     public static final String SETTINGS_PROVIDER_MODEL = "settings_provider_model";
+    /** @hide */
+    public static final String SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES
+            = "settings_use_new_backup_eligibility_rules";
 
     private static final Map<String, String> DEFAULT_FLAGS;
 
@@ -68,6 +71,7 @@
         DEFAULT_FLAGS.put("settings_silky_home", "false");
         DEFAULT_FLAGS.put("settings_contextual_home", "false");
         DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
+        DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "false");
     }
 
     private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/util/MergedConfiguration.java b/core/java/android/util/MergedConfiguration.java
index 3ac44aa..4328e08 100644
--- a/core/java/android/util/MergedConfiguration.java
+++ b/core/java/android/util/MergedConfiguration.java
@@ -33,9 +33,9 @@
  */
 public class MergedConfiguration implements Parcelable {
 
-    private Configuration mGlobalConfig = new Configuration();
-    private Configuration mOverrideConfig = new Configuration();
-    private Configuration mMergedConfig = new Configuration();
+    private final Configuration mGlobalConfig = new Configuration();
+    private final Configuration mOverrideConfig = new Configuration();
+    private final Configuration mMergedConfig = new Configuration();
 
     public MergedConfiguration() {
     }
@@ -59,15 +59,15 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mGlobalConfig, flags);
-        dest.writeParcelable(mOverrideConfig, flags);
-        dest.writeParcelable(mMergedConfig, flags);
+        mGlobalConfig.writeToParcel(dest, flags);
+        mOverrideConfig.writeToParcel(dest, flags);
+        mMergedConfig.writeToParcel(dest, flags);
     }
 
     public void readFromParcel(Parcel source) {
-        mGlobalConfig = source.readParcelable(Configuration.class.getClassLoader());
-        mOverrideConfig = source.readParcelable(Configuration.class.getClassLoader());
-        mMergedConfig = source.readParcelable(Configuration.class.getClassLoader());
+        mGlobalConfig.readFromParcel(source);
+        mOverrideConfig.readFromParcel(source);
+        mMergedConfig.readFromParcel(source);
     }
 
     @Override
diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java
index 698cb77..0ac2c9c 100644
--- a/core/java/android/util/RotationUtils.java
+++ b/core/java/android/util/RotationUtils.java
@@ -24,6 +24,7 @@
 import android.annotation.Dimension;
 import android.graphics.Insets;
 import android.graphics.Matrix;
+import android.graphics.Rect;
 import android.view.Surface.Rotation;
 
 /**
@@ -73,6 +74,60 @@
     }
 
     /**
+     * Rotates bounds as if parentBounds and bounds are a group. The group is rotated from
+     * oldRotation to newRotation. This assumes that parentBounds is at 0,0 and remains at 0,0 after
+     * rotation. The bounds will be at the same physical position in parentBounds.
+     *
+     * Only 'inOutBounds' is mutated.
+     */
+    public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int oldRotation,
+            @Rotation int newRotation) {
+        rotateBounds(inOutBounds, parentBounds, deltaRotation(oldRotation, newRotation));
+    }
+
+    /**
+     * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta`
+     * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and
+     * remains at 0,0 after rotation. The bounds will be at the same physical position in
+     * parentBounds.
+     *
+     * Only 'inOutBounds' is mutated.
+     */
+    public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation) {
+        final int origLeft = inOutBounds.left;
+        final int origTop = inOutBounds.top;
+        switch (rotation) {
+            case ROTATION_0:
+                return;
+            case ROTATION_90:
+                inOutBounds.left = inOutBounds.top;
+                inOutBounds.top = parentBounds.right - inOutBounds.right;
+                inOutBounds.right = inOutBounds.bottom;
+                inOutBounds.bottom = parentBounds.right - origLeft;
+                return;
+            case ROTATION_180:
+                inOutBounds.left = parentBounds.right - inOutBounds.right;
+                inOutBounds.right = parentBounds.right - origLeft;
+                inOutBounds.top = parentBounds.bottom - inOutBounds.bottom;
+                inOutBounds.bottom = parentBounds.bottom - origTop;
+                return;
+            case ROTATION_270:
+                inOutBounds.left = parentBounds.bottom - inOutBounds.bottom;
+                inOutBounds.bottom = inOutBounds.right;
+                inOutBounds.right = parentBounds.bottom - inOutBounds.top;
+                inOutBounds.top = origLeft;
+        }
+    }
+
+    /** @return the rotation needed to rotate from oldRotation to newRotation. */
+    @Rotation
+    public static int deltaRotation(int oldRotation, int newRotation) {
+        int delta = newRotation - oldRotation;
+        if (delta < 0) delta += 4;
+        return delta;
+    }
+
+    /**
      * Sets a matrix such that given a rotation, it transforms physical display
      * coordinates to that rotation's logical coordinates.
      *
diff --git a/core/java/android/uwb/AngleOfArrivalSupport.aidl b/core/java/android/uwb/AngleOfArrivalSupport.aidl
deleted file mode 100644
index 57666ff..0000000
--- a/core/java/android/uwb/AngleOfArrivalSupport.aidl
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-/**
- * @hide
- */
-@Backing(type="int")
-enum AngleOfArrivalSupport {
-  /**
-   * The device does not support angle of arrival
-   */
-  NONE,
-
-  /**
-   * The device supports planar angle of arrival
-   */
-  TWO_DIMENSIONAL,
-
-  /**
-   * The device does supports three dimensional angle of arrival with hemispherical azimuth angles
-   */
-  THREE_DIMENSIONAL_HEMISPHERICAL,
-
-  /**
-   * The device does supports three dimensional angle of arrival with full azimuth angles
-   */
-  THREE_DIMENSIONAL_SPHERICAL,
-}
-
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index b9c5508..468a69c 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -17,7 +17,6 @@
 package android.uwb;
 
 import android.os.PersistableBundle;
-import android.uwb.AngleOfArrivalSupport;
 import android.uwb.IUwbAdapterStateCallbacks;
 import android.uwb.IUwbRangingCallbacks;
 import android.uwb.SessionHandle;
@@ -47,43 +46,6 @@
   void unregisterAdapterStateCallbacks(in IUwbAdapterStateCallbacks callbacks);
 
   /**
-   * Returns true if ranging is supported, false otherwise
-   */
-  boolean isRangingSupported();
-
-  /**
-   * Get the angle of arrival supported by this device
-   *
-   * @return the angle of arrival type supported
-   */
-  AngleOfArrivalSupport getAngleOfArrivalSupport();
-
-  /**
-   * Generates a list of the supported 802.15.4z channels
-   *
-   * The list must be prioritized in the order of preferred channel usage.
-   *
-   * The list must only contain channels that are permitted to be used in the
-   * device's current location.
-   *
-   * @return an array of support channels on the device for the current location.
-   */
-  int[] getSupportedChannels();
-
-  /**
-   * Generates a list of the supported 802.15.4z preamble codes
-   *
-   * The list must be prioritized in the order of preferred preamble usage.
-   *
-   * The list must only contain preambles that are permitted to be used in the
-   * device's current location.
-   *
-   * @return an array of supported preambles on the device for the current
-   *         location.
-   */
-  int[] getSupportedPreambleCodes();
-
-  /**
    * Get the accuracy of the ranging timestamps
    *
    * @return accuracy of the ranging timestamps in nanoseconds
@@ -91,27 +53,6 @@
   long getTimestampResolutionNanos();
 
   /**
-   * Get the supported number of simultaneous ranging sessions
-   *
-   * @return the supported number of simultaneous ranging sessions
-   */
-  int getMaxSimultaneousSessions();
-
-  /**
-   * Get the maximum number of remote devices per session when local device is initiator
-   *
-   * @return the maximum number of remote devices supported in a single session
-   */
-  int getMaxRemoteDevicesPerInitiatorSession();
-
-  /**
-   * Get the maximum number of remote devices per session when local device is responder
-   *
-   * @return the maximum number of remote devices supported in a single session
-   */
-  int getMaxRemoteDevicesPerResponderSession();
-
-  /**
    * Provides the capabilities and features of the device
    *
    * @return specification specific capabilities and features of the device
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 2dc0ba0..63a6d05 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -32,10 +32,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -195,133 +191,6 @@
     }
 
     /**
-     * Check if ranging is supported, regardless of ranging method
-     *
-     * @return true if ranging is supported
-     */
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
-    public boolean isRangingSupported() {
-        try {
-            return mUwbAdapter.isRangingSupported();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE,
-            ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D,
-            ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL,
-            ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL})
-    public @interface AngleOfArrivalSupportType {}
-
-    /**
-     * Indicate absence of support for angle of arrival measurement
-     */
-    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1;
-
-    /**
-     * Indicate support for planar angle of arrival measurement, due to antenna
-     * limitation. Typically requires at least two antennas.
-     */
-    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2;
-
-    /**
-     * Indicate support for three dimensional angle of arrival measurement.
-     * Typically requires at least three antennas. However, due to antenna
-     * arrangement, a platform may only support hemi-spherical azimuth angles
-     * ranging from -pi/2 to pi/2
-     */
-    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3;
-
-    /**
-     * Indicate support for three dimensional angle of arrival measurement.
-     * Typically requires at least three antennas. This mode supports full
-     * azimuth angles ranging from -pi to pi.
-     */
-    public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4;
-
-    /**
-     * Gets the {@link AngleOfArrivalSupportType} supported on this platform
-     * <p>Possible return values are
-     * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE},
-     * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D},
-     * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL},
-     * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL}.
-     *
-     * @return angle of arrival type supported
-     */
-    @AngleOfArrivalSupportType
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
-    public int getAngleOfArrivalSupport() {
-        try {
-            switch (mUwbAdapter.getAngleOfArrivalSupport()) {
-                case AngleOfArrivalSupport.TWO_DIMENSIONAL:
-                    return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D;
-
-                case AngleOfArrivalSupport.THREE_DIMENSIONAL_HEMISPHERICAL:
-                    return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL;
-
-                case AngleOfArrivalSupport.THREE_DIMENSIONAL_SPHERICAL:
-                    return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL;
-
-                case AngleOfArrivalSupport.NONE:
-                default:
-                    return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE;
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Get a {@link List} of supported channel numbers based on the device's current location
-     * <p>The returned values are ordered by the system's desired ordered of use, with the first
-     * entry being the most preferred.
-     *
-     * <p>Channel numbers are defined based on the IEEE 802.15.4z standard for UWB.
-     *
-     * @return {@link List} of supported channel numbers ordered by preference
-     */
-    @NonNull
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
-    public List<Integer> getSupportedChannelNumbers() {
-        List<Integer> channels = new ArrayList<>();
-        try {
-            for (int channel : mUwbAdapter.getSupportedChannels()) {
-                channels.add(channel);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-        return channels;
-    }
-
-    /**
-     * Get a {@link List} of supported preamble code indices
-     * <p> Preamble code indices are defined based on the IEEE 802.15.4z standard for UWB.
-     *
-     * @return {@link List} of supported preamble code indices
-     */
-    @NonNull
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
-    public Set<Integer> getSupportedPreambleCodeIndices() {
-        Set<Integer> preambles = new HashSet<>();
-        try {
-            for (int preamble : mUwbAdapter.getSupportedPreambleCodes()) {
-                preambles.add(preamble);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-        return preambles;
-    }
-
-    /**
      * Get the timestamp resolution for events in nanoseconds
      * <p>This value defines the maximum error of all timestamps for events reported to
      * {@link RangingSession.Callback}.
@@ -339,50 +208,6 @@
     }
 
     /**
-     * Get the number of simultaneous sessions allowed in the system
-     *
-     * @return the maximum allowed number of simultaneously open {@link RangingSession} instances.
-     */
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
-    public int getMaxSimultaneousSessions() {
-        try {
-            return mUwbAdapter.getMaxSimultaneousSessions();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Get the maximum number of remote devices in a {@link RangingSession} when the local device
-     * is the initiator.
-     *
-     * @return the maximum number of remote devices per {@link RangingSession}
-     */
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
-    public int getMaxRemoteDevicesPerInitiatorSession() {
-        try {
-            return mUwbAdapter.getMaxRemoteDevicesPerInitiatorSession();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Get the maximum number of remote devices in a {@link RangingSession} when the local device
-     * is a responder.
-     *
-     * @return the maximum number of remote devices per {@link RangingSession}
-     */
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
-    public int getMaxRemoteDevicesPerResponderSession() {
-        try {
-            return mUwbAdapter.getMaxRemoteDevicesPerResponderSession();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Open a {@link RangingSession} with the given parameters
      * <p>The {@link RangingSession.Callback#onOpened(RangingSession)} function is called with a
      * {@link RangingSession} object used to control ranging when the session is successfully
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 8117c96..0ba1dfe 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -34,7 +34,6 @@
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.hardware.display.DeviceProductInfo;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Build;
@@ -1182,18 +1181,6 @@
     }
 
     /**
-     * Returns the product-specific information about the display or the directly connected
-     * device on the display chain.
-     * For example, if the display is transitively connected, this field may contain product
-     * information about the intermediate device.
-     * Returns {@code null} if product information is not available.
-     */
-    @Nullable
-    public DeviceProductInfo getDeviceProductInfo() {
-        return mDisplayInfo.deviceProductInfo;
-    }
-
-    /**
      * Gets display metrics that describe the size and density of this display.
      * The size returned by this method does not necessarily represent the
      * actual raw size (native resolution) of the display.
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 6543de1..eb49e52 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -213,8 +213,7 @@
             Index.FRAME_TIMELINE_VSYNC_ID,
             Index.INTENDED_VSYNC,
             Index.VSYNC,
-            Index.OLDEST_INPUT_EVENT,
-            Index.NEWEST_INPUT_EVENT,
+            Index.INPUT_EVENT_ID,
             Index.HANDLE_INPUT_START,
             Index.ANIMATION_START,
             Index.PERFORM_TRAVERSALS_START,
@@ -225,8 +224,11 @@
             Index.ISSUE_DRAW_COMMANDS_START,
             Index.SWAP_BUFFERS,
             Index.FRAME_COMPLETED,
+            Index.DEQUEUE_BUFFER_DURATION,
+            Index.QUEUE_BUFFER_DURATION,
             Index.GPU_COMPLETED,
-            Index.SWAP_BUFFERS_COMPLETED
+            Index.SWAP_BUFFERS_COMPLETED,
+            Index.DISPLAY_PRESENT_TIME,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Index {
@@ -234,20 +236,22 @@
         int FRAME_TIMELINE_VSYNC_ID = 1;
         int INTENDED_VSYNC = 2;
         int VSYNC = 3;
-        int OLDEST_INPUT_EVENT = 4;
-        int NEWEST_INPUT_EVENT = 5;
-        int HANDLE_INPUT_START = 6;
-        int ANIMATION_START = 7;
-        int PERFORM_TRAVERSALS_START = 8;
-        int DRAW_START = 9;
-        int FRAME_DEADLINE = 10;
-        int SYNC_QUEUED = 11;
-        int SYNC_START = 12;
-        int ISSUE_DRAW_COMMANDS_START = 13;
-        int SWAP_BUFFERS = 14;
-        int FRAME_COMPLETED = 15;
-        int GPU_COMPLETED = 18;
-        int SWAP_BUFFERS_COMPLETED = 19;
+        int INPUT_EVENT_ID = 4;
+        int HANDLE_INPUT_START = 5;
+        int ANIMATION_START = 6;
+        int PERFORM_TRAVERSALS_START = 7;
+        int DRAW_START = 8;
+        int FRAME_DEADLINE = 9;
+        int SYNC_QUEUED = 10;
+        int SYNC_START = 11;
+        int ISSUE_DRAW_COMMANDS_START = 12;
+        int SWAP_BUFFERS = 13;
+        int FRAME_COMPLETED = 14;
+        int DEQUEUE_BUFFER_DURATION = 15;
+        int QUEUE_BUFFER_DURATION = 16;
+        int GPU_COMPLETED = 17;
+        int SWAP_BUFFERS_COMPLETED = 18;
+        int DISPLAY_PRESENT_TIME = 19;
 
         int FRAME_STATS_COUNT = 20; // must always be last and in sync with
                                     // FrameInfoIndex::NumIndexes in libs/hwui/FrameInfo.h
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedTaskListener.aidl
similarity index 95%
rename from core/java/android/view/IPinnedStackListener.aidl
rename to core/java/android/view/IPinnedTaskListener.aidl
index 29c9c15..c31e67e 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedTaskListener.aidl
@@ -23,11 +23,11 @@
 import android.view.DisplayInfo;
 
 /**
- * Listener for changes to the pinned stack made by the WindowManager.
+ * Listener for changes to the pinned task made by the WindowManager.
  *
  * @hide
  */
-oneway interface IPinnedStackListener {
+oneway interface IPinnedTaskListener {
 
     /**
      * Called when the window manager has detected a change that would cause the movement bounds
diff --git a/core/java/android/view/IScrollCaptureCallbacks.aidl b/core/java/android/view/IScrollCaptureCallbacks.aidl
index d97e3c6..26eaac0 100644
--- a/core/java/android/view/IScrollCaptureCallbacks.aidl
+++ b/core/java/android/view/IScrollCaptureCallbacks.aidl
@@ -16,12 +16,10 @@
 
 package android.view;
 
-import android.graphics.Point;
 import android.graphics.Rect;
+import android.view.ScrollCaptureResponse;
 import android.view.Surface;
 
-import android.view.IScrollCaptureConnection;
-
 /**
  * Asynchronous callback channel for responses to scroll capture requests.
  *
@@ -29,34 +27,30 @@
  */
 interface IScrollCaptureCallbacks {
     /**
-     * Scroll capture is available, and a connection has been provided.
+     * Provides the result of WindowManagerService#requestScrollCapture
      *
-     * @param connection a connection to a window process and scrollable content
-     * @param scrollAreaInWindow the location of scrolling in global (window) coordinate space
+     * @param response the response which describes the result
      */
-    oneway void onConnected(in IScrollCaptureConnection connection, in Rect scrollBounds,
-            in Point positionInWindow);
+    oneway void onScrollCaptureResponse(in ScrollCaptureResponse response);
 
     /**
-     * The window does not support scroll capture.
-     */
-    oneway void onUnavailable();
-
-    /**
-     * Called when the remote end has confirmed the request and is ready to begin providing image
-     * requests.
+     * Called in reply to IScrollCaptureConnection#startCapture, when the remote end has confirmed
+     * the request and is ready to begin capturing images.
      */
     oneway void onCaptureStarted();
 
     /**
-     * Received a response from a capture request.
+     * Received a response from a capture request. The provided rectangle indicates the portion
+     * of the requested rectangle which was captured. An empty rectangle indicates that the request
+     * could not be satisfied (most commonly due to the available scrolling range).
+     *
+     * @param flags flags describing additional status of the result
+     * @param capturedArea the actual area of the image captured
      */
-    oneway void onCaptureBufferSent(long frameNumber, in Rect capturedArea);
+    oneway void onImageRequestCompleted(int flags, in Rect capturedArea);
 
     /**
-     * Signals that the capture session has completed and the target window may be returned to
-     * normal interactive use. This may be due to normal shutdown, or after a timeout or other
-     * unrecoverable state change such as activity lifecycle, window visibility or focus.
+     * Signals that the capture session has completed and the target window is ready for normal use.
      */
-    oneway void onConnectionClosed();
+    oneway void onCaptureEnded();
 }
diff --git a/core/java/android/view/IScrollCaptureConnection.aidl b/core/java/android/view/IScrollCaptureConnection.aidl
index 63a4f48..c55e888 100644
--- a/core/java/android/view/IScrollCaptureConnection.aidl
+++ b/core/java/android/view/IScrollCaptureConnection.aidl
@@ -17,33 +17,45 @@
 package android.view;
 
 import android.graphics.Rect;
+import android.os.ICancellationSignal;
 import android.view.Surface;
 
 
  /**
-   * Interface implemented by a client of the Scroll Capture framework to receive requests
-   * to start, capture images and end the session.
+   * A remote connection to a scroll capture target.
    *
    * {@hide}
    */
 interface IScrollCaptureConnection {
 
     /**
-     * Informs the client that it has been selected for scroll capture and should prepare to
-     * to begin handling capture requests.
-     */
-    oneway void startCapture(in Surface surface);
-
-    /**
-     * Request the client capture an image within the provided rectangle.
+     * Informs the target that it has been selected for scroll capture.
      *
-     * @see android.view.ScrollCaptureCallback#onScrollCaptureRequest
+     * @param surface a return channel for image buffers
+     *
+     * @return a cancallation signal which is used cancel the request
      */
-    oneway void requestImage(in Rect captureArea);
+    ICancellationSignal startCapture(in Surface surface);
 
     /**
-     * Inform the client that capture has ended. The client should shut down and release all
-     * local resources in use and prepare for return to normal interactive usage.
+     * Request the target capture an image within the provided rectangle.
+     *
+     * @param surface a return channel for image buffers
+     * @param signal a cancallation signal which can interrupt the request
+     *
+     * @return a cancallation signal which is used cancel the request
      */
-    oneway void endCapture();
+    ICancellationSignal requestImage(in Rect captureArea);
+
+    /**
+     * Inform the target that capture has ended.
+     *
+     * @return a cancallation signal which is used cancel the request
+     */
+    ICancellationSignal endCapture();
+
+    /**
+     * Closes the connection.
+     */
+    oneway void close();
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 4e4ba3f..afdf798 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -35,13 +35,12 @@
 import android.view.DisplayCutout;
 import android.view.IApplicationToken;
 import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.IDockedStackListener;
 import android.view.IDisplayWindowInsetsController;
 import android.view.IDisplayWindowListener;
 import android.view.IDisplayFoldListener;
 import android.view.IDisplayWindowRotationController;
 import android.view.IOnKeyguardExitResult;
-import android.view.IPinnedStackListener;
+import android.view.IPinnedTaskListener;
 import android.view.IScrollCaptureCallbacks;
 import android.view.RemoteAnimationAdapter;
 import android.view.IRotationWatcher;
@@ -446,12 +445,12 @@
      * Sets the region the user can touch the divider. This region will be excluded from the region
      * which is used to cause a focus switch when dispatching touch.
      */
-    void setDockedStackDividerTouchRegion(in Rect touchableRegion);
+    void setDockedTaskDividerTouchRegion(in Rect touchableRegion);
 
     /**
-     * Registers a listener that will be called when the pinned stack state changes.
+     * Registers a listener that will be called when the pinned task state changes.
      */
-    void registerPinnedStackListener(int displayId, IPinnedStackListener listener);
+    void registerPinnedTaskListener(int displayId, IPinnedTaskListener listener);
 
     /**
      * Requests Keyboard Shortcuts from the displayed window.
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 5f2bccc..e6cf683 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -261,11 +261,7 @@
 
     public InsetsSource(Parcel in) {
         mType = in.readInt();
-        if (in.readInt() != 0) {
-            mFrame = Rect.CREATOR.createFromParcel(in);
-        } else {
-            mFrame = null;
-        }
+        mFrame = Rect.CREATOR.createFromParcel(in);
         if (in.readInt() != 0) {
             mVisibleFrame = Rect.CREATOR.createFromParcel(in);
         } else {
@@ -282,12 +278,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mType);
-        if (mFrame != null) {
-            dest.writeInt(1);
-            mFrame.writeToParcel(dest, 0);
-        } else {
-            dest.writeInt(0);
-        }
+        mFrame.writeToParcel(dest, 0);
         if (mVisibleFrame != null) {
             dest.writeInt(1);
             mVisibleFrame.writeToParcel(dest, 0);
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index c717c2a..1d4b411 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -77,8 +77,8 @@
 
     public InsetsSourceControl(Parcel in) {
         mType = in.readInt();
-        mLeash = in.readParcelable(null /* loader */);
-        mSurfacePosition = in.readParcelable(null /* loader */);
+        mLeash = in.readTypedObject(SurfaceControl.CREATOR);
+        mSurfacePosition = in.readTypedObject(Point.CREATOR);
         mSkipAnimationOnce = in.readBoolean();
     }
 
@@ -119,8 +119,8 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mType);
-        dest.writeParcelable(mLeash, 0 /* flags*/);
-        dest.writeParcelable(mSurfacePosition, 0 /* flags*/);
+        dest.writeTypedObject(mLeash, 0 /* parcelableFlags */);
+        dest.writeTypedObject(mSurfacePosition, 0 /* parcelableFlags */);
         dest.writeBoolean(mSkipAnimationOnce);
     }
 
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 21dd1fb..fce1952 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -156,7 +156,7 @@
     static final int ISIDE_FLOATING = 4;
     static final int ISIDE_UNKNOWN = 5;
 
-    private InsetsSource[] mSources = new InsetsSource[SIZE];
+    private final InsetsSource[] mSources = new InsetsSource[SIZE];
 
     /**
      * The frame of the display these sources are relative to.
@@ -804,7 +804,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         mDisplayFrame.writeToParcel(dest, flags);
         mDisplayCutout.writeToParcel(dest, flags);
-        dest.writeParcelableArray(mSources, 0);
+        dest.writeTypedArray(mSources, 0 /* parcelableFlags */);
         dest.writeTypedObject(mRoundedCorners, flags);
     }
 
@@ -820,9 +820,9 @@
     };
 
     public void readFromParcel(Parcel in) {
-        mDisplayFrame.set(Rect.CREATOR.createFromParcel(in));
-        mDisplayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in));
-        mSources = in.readParcelableArray(null, InsetsSource.class);
+        mDisplayFrame.readFromParcel(in);
+        mDisplayCutout.readFromParcel(in);
+        in.readTypedArray(mSources, InsetsSource.CREATOR);
         mRoundedCorners = in.readTypedObject(RoundedCorners.CREATOR);
     }
 
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index e66b17a..c43c410 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -77,3 +77,7 @@
 per-file ViewRootInsetsControllerHost.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file Window*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Scroll Capture
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/java/android/view/ScrollCaptureCallback.java b/core/java/android/view/ScrollCaptureCallback.java
index d3aad2c..1688616 100644
--- a/core/java/android/view/ScrollCaptureCallback.java
+++ b/core/java/android/view/ScrollCaptureCallback.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.UiThread;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 
 import java.util.function.Consumer;
 
@@ -58,8 +59,6 @@
  * @see View#setScrollCaptureHint(int)
  * @see View#setScrollCaptureCallback(ScrollCaptureCallback)
  * @see Window#registerScrollCaptureCallback(ScrollCaptureCallback)
- *
- * @hide
  */
 @UiThread
 public interface ScrollCaptureCallback {
@@ -68,80 +67,84 @@
      * The system is searching for the appropriate scrolling container to capture and would like to
      * know the size and position of scrolling content handled by this callback.
      * <p>
-     * Implementations should inset {@code containingViewBounds} to cover only the area within the
-     * containing view where scrolling content may be positioned. This should cover only the content
-     * which tracks with scrolling movement.
+     * To determine scroll bounds, an implementation should inset the visible bounds of the
+     * containing view to cover only the area where scrolling content may be positioned. This
+     * should cover only the content which tracks with scrolling movement.
      * <p>
-     * Return the updated rectangle to {@code resultConsumer}. If for any reason the scrolling
-     * content is not available to capture, a {@code null} rectangle may be returned, and this view
-     * will be excluded as the target for this request.
+     * Return the updated rectangle to {@link Consumer#accept onReady.accept}. If for any reason the
+     * scrolling content is not available to capture, a empty rectangle may be returned which will
+     * exclude this view from consideration.
      * <p>
-     * Responses received after XXXms will be discarded.
-     * <p>
-     * TODO: finalize timeout
+     * This request may be cancelled via the provided {@link CancellationSignal}. When this happens,
+     * any future call to {@link Consumer#accept onReady.accept} will have no effect and this
+     * content will be omitted from the search results.
      *
-     * @param onReady              consumer for the updated rectangle
+     * @param signal  signal to cancel the operation in progress
+     * @param onReady consumer for the updated rectangle
      */
-    void onScrollCaptureSearch(@NonNull Consumer<Rect> onReady);
+    void onScrollCaptureSearch(@NonNull CancellationSignal signal, @NonNull Consumer<Rect> onReady);
 
     /**
      * Scroll Capture has selected this callback to provide the scrolling image content.
      * <p>
-     * The onReady signal should be called when ready to begin handling image requests.
+     * {@link Runnable#run onReady.run} should be called when ready to begin handling image
+     * requests.
+     * <p>
+     * This request may be cancelled via the provided {@link CancellationSignal}. When this happens,
+     * any future call to {@link Runnable#run onReady.run} will have no effect and provided session
+     * will not be activated.
+     *
+     * @param session the current session, resources provided by it are valid for use until the
+     *                {@link #onScrollCaptureEnd(Runnable) session ends}
+     * @param signal  signal to cancel the operation in progress
+     * @param onReady signal used to report completion of the request
      */
-    void onScrollCaptureStart(@NonNull ScrollCaptureSession session, @NonNull Runnable onReady);
+    void onScrollCaptureStart(@NonNull ScrollCaptureSession session,
+            @NonNull CancellationSignal signal, @NonNull Runnable onReady);
 
     /**
      * An image capture has been requested from the scrolling content.
      * <p>
-     * <code>captureArea</code> contains the bounds of the image requested, relative to the
-     * rectangle provided by {@link ScrollCaptureCallback#onScrollCaptureSearch}, referred to as
-     * {@code scrollBounds}.
-     * here.
+     * The requested rectangle describes an area inside the target view, relative to
+     * <code>scrollBounds</code>. The content may be offscreen, above or below the current visible
+     * portion of the target view. To handle the request, render the available portion of this
+     * rectangle to a buffer and return it via the Surface available from {@link
+     * ScrollCaptureSession#getSurface()}.
      * <p>
-     * A series of requests will step by a constant vertical amount relative to {@code
-     * scrollBounds}, moving through the scrolling range of content, above and below the current
-     * visible area. The rectangle's vertical position will not account for any scrolling movement
-     * since capture started. Implementations therefore must track any scroll position changes and
-     * subtract this distance from requests.
+     * Note: Implementations are only required to render the requested content, and may do so into
+     * off-screen buffers without scrolling if they are able.
      * <p>
-     * To handle a request, the content should be scrolled to maximize the visible area of the
-     * requested rectangle. Offset {@code captureArea} again to account for any further scrolling.
+     * The resulting available portion of the request must be computed as a portion of {@code
+     * captureArea}, and sent to signal the operation is complete, using  {@link Consumer#accept
+     * onComplete.accept}. If the requested rectangle is  partially or fully out of bounds the
+     * resulting portion should be returned. If no portion is available (outside of available
+     * content), then skip sending any buffer and report an empty Rect as result.
      * <p>
-     * Finally, clip this rectangle against scrollBounds to determine what portion, if any is
-     * visible content to capture. If the rectangle is completely clipped, set it to {@link
-     * Rect#setEmpty() empty} and skip the next step.
-     * <p>
-     * Make a copy of {@code captureArea}, transform to window coordinates and draw the window,
-     * clipped to this rectangle, into the {@link ScrollCaptureSession#getSurface() surface} at
-     * offset (0,0).
-     * <p>
-     * Finally, return the resulting {@code captureArea} using
-     * {@link ScrollCaptureSession#notifyBufferSent}.
-     * <p>
-     * If the response is not supplied within XXXms, the session will end with a call to {@link
-     * #onScrollCaptureEnd}, after which {@code session} is invalid and should be discarded.
-     * <p>
-     * TODO: finalize timeout
-     * <p>
+     * This request may be cancelled via the provided {@link CancellationSignal}. When this happens,
+     * any future call to {@link Consumer#accept onComplete.accept} will be ignored until the next
+     * request.
      *
+     * @param session the current session, resources provided by it are valid for use until the
+     *                {@link #onScrollCaptureEnd(Runnable) session ends}
+     * @param signal      signal to cancel the operation in progress
      * @param captureArea the area to capture, a rectangle within {@code scrollBounds}
+     * @param onComplete  a consumer for the captured area
      */
-    void onScrollCaptureImageRequest(
-            @NonNull ScrollCaptureSession session, @NonNull Rect captureArea);
+    void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session,
+            @NonNull CancellationSignal signal, @NonNull Rect captureArea,
+            @NonNull Consumer<Rect> onComplete);
 
     /**
      * Signals that capture has ended. Implementations should release any temporary resources or
      * references to objects in use during the capture. Any resources obtained from the session are
      * now invalid and attempts to use them after this point may throw an exception.
      * <p>
-     * The window should be returned as much as possible to its original state when capture started.
-     * At a minimum, the content should be scrolled to its original position.
+     * The window should be returned to its original state when capture started. At a minimum, the
+     * content should be scrolled to its original position.
      * <p>
-     * <code>onReady</code> should be called when the window should be made visible and
-     * interactive. The system will wait up to XXXms for this call before proceeding.
-     * <p>
-     * TODO: finalize timeout
+     * {@link Runnable#run onReady.run} should be called as soon as possible after the window is
+     * ready for normal interactive use. After the callback (or after a timeout, if not called) the
+     * screenshot tool will be dismissed and the window may become visible to the user at any time.
      *
      * @param onReady a callback to inform the system that the application has completed any
      *                cleanup and is ready to become visible
diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java
index 0e6cdd1d..3456e01 100644
--- a/core/java/android/view/ScrollCaptureConnection.java
+++ b/core/java/android/view/ScrollCaptureConnection.java
@@ -18,18 +18,23 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.BinderThread;
 import android.annotation.NonNull;
 import android.annotation.UiThread;
-import android.annotation.WorkerThread;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.os.Handler;
+import android.os.CancellationSignal;
+import android.os.ICancellationSignal;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.util.CloseGuard;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Mediator between a selected scroll capture target view and a remote process.
@@ -41,270 +46,276 @@
 public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub {
 
     private static final String TAG = "ScrollCaptureConnection";
-    private static final int DEFAULT_TIMEOUT = 1000;
 
-    private final Handler mHandler;
-    private ScrollCaptureTarget mSelectedTarget;
-    private int mTimeoutMillis = DEFAULT_TIMEOUT;
-
-    protected Surface mSurface;
-    private IScrollCaptureCallbacks mCallbacks;
-
+    private final Object mLock = new Object();
     private final Rect mScrollBounds;
     private final Point mPositionInWindow;
     private final CloseGuard mCloseGuard;
+    private final Executor mUiThread;
 
-    // The current session instance in use by the callback.
+    private ScrollCaptureCallback mLocal;
+    private IScrollCaptureCallbacks mRemote;
+
     private ScrollCaptureSession mSession;
 
-    // Helps manage timeout callbacks registered to handler and aids testing.
-    private DelayedAction mTimeoutAction;
+    private CancellationSignal mCancellation;
+
+    private volatile boolean mStarted;
+    private volatile boolean mConnected;
 
     /**
      * Constructs a ScrollCaptureConnection.
      *
      * @param selectedTarget  the target the client is controlling
-     * @param callbacks the callbacks to reply to system requests
+     * @param remote the callbacks to reply to system requests
      *
      * @hide
      */
     public ScrollCaptureConnection(
+            @NonNull Executor uiThread,
             @NonNull ScrollCaptureTarget selectedTarget,
-            @NonNull IScrollCaptureCallbacks callbacks) {
+            @NonNull IScrollCaptureCallbacks remote) {
+        mUiThread = requireNonNull(uiThread, "<uiThread> must non-null");
         requireNonNull(selectedTarget, "<selectedTarget> must non-null");
-        requireNonNull(callbacks, "<callbacks> must non-null");
-        final Rect scrollBounds = requireNonNull(selectedTarget.getScrollBounds(),
+        mRemote = requireNonNull(remote, "<callbacks> must non-null");
+        mScrollBounds = requireNonNull(Rect.copyOrNull(selectedTarget.getScrollBounds()),
                 "target.getScrollBounds() must be non-null to construct a client");
 
-        mSelectedTarget = selectedTarget;
-        mHandler = selectedTarget.getContainingView().getHandler();
-        mScrollBounds = new Rect(scrollBounds);
+        mLocal = selectedTarget.getCallback();
         mPositionInWindow = new Point(selectedTarget.getPositionInWindow());
 
-        mCallbacks = callbacks;
         mCloseGuard = new CloseGuard();
         mCloseGuard.open("close");
-
-        selectedTarget.getContainingView().addOnAttachStateChangeListener(
-                new View.OnAttachStateChangeListener() {
-                    @Override
-                    public void onViewAttachedToWindow(View v) {
-
-                    }
-
-                    @Override
-                    public void onViewDetachedFromWindow(View v) {
-                        selectedTarget.getContainingView().removeOnAttachStateChangeListener(this);
-                        endCapture();
-                    }
-                });
+        mConnected = true;
     }
 
-    @VisibleForTesting
-    public void setTimeoutMillis(int timeoutMillis) {
-        mTimeoutMillis = timeoutMillis;
-    }
-
-    @VisibleForTesting
-    public DelayedAction getTimeoutAction() {
-        return mTimeoutAction;
-    }
-
-    private void checkConnected() {
-        if (mSelectedTarget == null || mCallbacks == null) {
-            throw new IllegalStateException("This client has been disconnected.");
-        }
-    }
-
-    private void checkStarted() {
-        if (mSession == null) {
-            throw new IllegalStateException("Capture session has not been started!");
-        }
-    }
-
-    @WorkerThread // IScrollCaptureConnection
+    @BinderThread
     @Override
-    public void startCapture(Surface surface) throws RemoteException {
+    public ICancellationSignal startCapture(Surface surface) throws RemoteException {
         checkConnected();
-        mSurface = surface;
-        scheduleTimeout(mTimeoutMillis, this::onStartCaptureTimeout);
-        mSession = new ScrollCaptureSession(mSurface, mScrollBounds, mPositionInWindow, this);
-        mHandler.post(() -> mSelectedTarget.getCallback().onScrollCaptureStart(mSession,
-                this::onStartCaptureCompleted));
+        if (!surface.isValid()) {
+            throw new RemoteException(new IllegalArgumentException("surface must be valid"));
+        }
+
+        ICancellationSignal cancellation = CancellationSignal.createTransport();
+        mCancellation = CancellationSignal.fromTransport(cancellation);
+        mSession = new ScrollCaptureSession(surface, mScrollBounds, mPositionInWindow);
+
+        Runnable listener =
+                SafeCallback.create(mCancellation, mUiThread, this::onStartCaptureCompleted);
+        // -> UiThread
+        mUiThread.execute(() -> mLocal.onScrollCaptureStart(mSession, mCancellation, listener));
+        return cancellation;
     }
 
     @UiThread
     private void onStartCaptureCompleted() {
-        if (cancelTimeout()) {
-            mHandler.post(() -> {
-                try {
-                    mCallbacks.onCaptureStarted();
-                } catch (RemoteException e) {
-                    doShutdown();
-                }
-            });
+        mStarted = true;
+        try {
+            mRemote.onCaptureStarted();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Shutting down due to error: ", e);
+            close();
         }
     }
 
-    @UiThread
-    private void onStartCaptureTimeout() {
-        endCapture();
-    }
 
-    @WorkerThread // IScrollCaptureConnection
+    @BinderThread
     @Override
-    public void requestImage(Rect requestRect) {
+    public ICancellationSignal requestImage(Rect requestRect) throws RemoteException {
+        Trace.beginSection("requestImage");
         checkConnected();
         checkStarted();
-        scheduleTimeout(mTimeoutMillis, this::onRequestImageTimeout);
-        // Response is dispatched via ScrollCaptureSession, to onRequestImageCompleted
-        mHandler.post(() -> mSelectedTarget.getCallback().onScrollCaptureImageRequest(
-                mSession, new Rect(requestRect)));
+
+        ICancellationSignal cancellation = CancellationSignal.createTransport();
+        mCancellation = CancellationSignal.fromTransport(cancellation);
+
+        Consumer<Rect> listener =
+                SafeCallback.create(mCancellation, mUiThread, this::onImageRequestCompleted);
+        // -> UiThread
+        mUiThread.execute(() -> mLocal.onScrollCaptureImageRequest(
+                mSession, mCancellation, new Rect(requestRect), listener));
+        Trace.endSection();
+        return cancellation;
     }
 
     @UiThread
-    void onRequestImageCompleted(long frameNumber, Rect capturedArea) {
-        final Rect finalCapturedArea = new Rect(capturedArea);
-        if (cancelTimeout()) {
-            mHandler.post(() -> {
-                try {
-                    mCallbacks.onCaptureBufferSent(frameNumber, finalCapturedArea);
-                } catch (RemoteException e) {
-                    doShutdown();
-                }
-            });
-        }
-    }
-
-    @UiThread
-    private void onRequestImageTimeout() {
-        endCapture();
-    }
-
-    @WorkerThread // IScrollCaptureConnection
-    @Override
-    public void endCapture() {
-        if (isStarted()) {
-            scheduleTimeout(mTimeoutMillis, this::onEndCaptureTimeout);
-            mHandler.post(() ->
-                    mSelectedTarget.getCallback().onScrollCaptureEnd(this::onEndCaptureCompleted));
-        } else {
-            disconnect();
-        }
-    }
-
-    private boolean isStarted() {
-        return mCallbacks != null && mSelectedTarget != null;
-    }
-
-    @UiThread
-    private void onEndCaptureCompleted() { // onEndCaptureCompleted
-        if (cancelTimeout()) {
-            doShutdown();
-        }
-    }
-
-    @UiThread
-    private void onEndCaptureTimeout() {
-        doShutdown();
-    }
-
-
-    private void doShutdown() {
+    void onImageRequestCompleted(Rect capturedArea) {
         try {
-            if (mCallbacks != null) {
-                mCallbacks.onConnectionClosed();
-            }
+            mRemote.onImageRequestCompleted(0, capturedArea);
         } catch (RemoteException e) {
-            // Ignore
-        } finally {
-            disconnect();
+            Log.w(TAG, "Shutting down due to error: ", e);
+            close();
         }
     }
 
+    @BinderThread
+    @Override
+    public ICancellationSignal endCapture() throws RemoteException {
+        checkConnected();
+        checkStarted();
+
+        ICancellationSignal cancellation = CancellationSignal.createTransport();
+        mCancellation = CancellationSignal.fromTransport(cancellation);
+
+        Runnable listener =
+                SafeCallback.create(mCancellation, mUiThread, this::onEndCaptureCompleted);
+        // -> UiThread
+        mUiThread.execute(() -> mLocal.onScrollCaptureEnd(listener));
+        return cancellation;
+    }
+
+    @UiThread
+    private void onEndCaptureCompleted() {
+        synchronized (mLock) {
+            mStarted = false;
+            try {
+                mRemote.onCaptureEnded();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Shutting down due to error: ", e);
+                close();
+            }
+        }
+    }
+
+    @BinderThread
+    @Override
+    public void close() {
+        if (mStarted) {
+            Log.w(TAG, "close(): capture is still started?! Ending now.");
+
+            // -> UiThread
+            mUiThread.execute(() -> mLocal.onScrollCaptureEnd(() -> { /* ignore */ }));
+            mStarted = false;
+        }
+        disconnect();
+    }
+
     /**
      * Shuts down this client and releases references to dependent objects. No attempt is made
      * to notify the controller, use with caution!
      */
-    public void disconnect() {
-        if (mSession != null) {
-            mSession.disconnect();
+    private void disconnect() {
+        synchronized (mLock) {
             mSession = null;
+            mConnected = false;
+            mStarted = false;
+            mRemote = null;
+            mLocal = null;
+            mCloseGuard.close();
         }
+    }
 
-        mSelectedTarget = null;
-        mCallbacks = null;
+    public boolean isConnected() {
+        return mConnected;
+    }
+
+    public boolean isStarted() {
+        return mStarted;
+    }
+
+    private synchronized void checkConnected() throws RemoteException {
+        synchronized (mLock) {
+            if (!mConnected) {
+                throw new RemoteException(new IllegalStateException("Not connected"));
+            }
+        }
+    }
+
+    private void checkStarted() throws RemoteException {
+        synchronized (mLock) {
+            if (!mStarted) {
+                throw new RemoteException(new IllegalStateException("Not started!"));
+            }
+        }
     }
 
     /** @return a string representation of the state of this client */
     public String toString() {
         return "ScrollCaptureConnection{"
+                + "connected=" + mConnected
+                + ", started=" + mStarted
                 + ", session=" + mSession
-                + ", selectedTarget=" + mSelectedTarget
-                + ", clientCallbacks=" + mCallbacks
+                + ", remote=" + mRemote
+                + ", local=" + mLocal
                 + "}";
     }
 
-    private boolean cancelTimeout() {
-        if (mTimeoutAction != null) {
-            return mTimeoutAction.cancel();
-        }
-        return false;
-    }
-
-    private void scheduleTimeout(long timeoutMillis, Runnable action) {
-        if (mTimeoutAction != null) {
-            mTimeoutAction.cancel();
-        }
-        mTimeoutAction = new DelayedAction(mHandler, timeoutMillis, action);
-    }
-
-    /** @hide */
     @VisibleForTesting
-    public static class DelayedAction {
-        private final AtomicBoolean mCompleted = new AtomicBoolean();
-        private final Object mToken = new Object();
-        private final Handler mHandler;
-        private final Runnable mAction;
+    public CancellationSignal getCancellation() {
+        return mCancellation;
+    }
 
-        @VisibleForTesting
-        public DelayedAction(Handler handler, long timeoutMillis, Runnable action) {
-            mHandler = handler;
-            mAction = action;
-            mHandler.postDelayed(this::onTimeout, mToken, timeoutMillis);
-        }
-
-        private boolean onTimeout() {
-            if (mCompleted.compareAndSet(false, true)) {
-                mAction.run();
-                return true;
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
             }
-            return false;
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static class SafeCallback<T> {
+        private final CancellationSignal mSignal;
+        private final WeakReference<T> mTargetRef;
+        private final Executor mExecutor;
+        private boolean mExecuted;
+
+        protected SafeCallback(CancellationSignal signal, Executor executor, T target) {
+            mSignal = signal;
+            mTargetRef = new WeakReference<>(target);
+            mExecutor = executor;
         }
 
-        /**
-         * Cause the timeout action to run immediately and mark as timed out.
-         *
-         * @return true if the timeout was run, false if the timeout had already been canceled
-         */
-        @VisibleForTesting
-        public boolean timeoutNow() {
-            return onTimeout();
-        }
-
-        /**
-         * Attempt to cancel the timeout action (such as after a callback is made)
-         *
-         * @return true if the timeout was canceled and will not run, false if time has expired and
-         * the timeout action has or will run momentarily
-         */
-        public boolean cancel() {
-            if (!mCompleted.compareAndSet(false, true)) {
-                // Whoops, too late!
-                return false;
+        // Provide the target to the consumer to invoke, forward on handler thread ONCE,
+        // and only if noy cancelled, and the target is still available (not collected)
+        protected final void maybeAccept(Consumer<T> targetConsumer) {
+            if (mExecuted) {
+                return;
             }
-            mHandler.removeCallbacksAndMessages(mToken);
-            return true;
+            mExecuted = true;
+            if (mSignal.isCanceled()) {
+                return;
+            }
+            T target = mTargetRef.get();
+            if (target == null) {
+                return;
+            }
+            mExecutor.execute(() -> targetConsumer.accept(target));
+        }
+
+        static Runnable create(CancellationSignal signal, Executor executor, Runnable target) {
+            return new RunnableCallback(signal, executor, target);
+        }
+
+        static <T> Consumer<T> create(CancellationSignal signal, Executor executor,
+                Consumer<T> target) {
+            return new ConsumerCallback<T>(signal, executor, target);
+        }
+    }
+
+    private static final class RunnableCallback extends SafeCallback<Runnable> implements Runnable {
+        RunnableCallback(CancellationSignal signal, Executor executor, Runnable target) {
+            super(signal, executor, target);
+        }
+
+        @Override
+        public void run() {
+            maybeAccept(Runnable::run);
+        }
+    }
+
+    private static final class ConsumerCallback<T> extends SafeCallback<Consumer<T>>
+            implements Consumer<T> {
+        ConsumerCallback(CancellationSignal signal, Executor executor, Consumer<T> target) {
+            super(signal, executor, target);
+        }
+
+        @Override
+        public void accept(T value) {
+            maybeAccept((target) -> target.accept(value));
         }
     }
 }
diff --git a/core/java/android/view/ScrollCaptureResponse.aidl b/core/java/android/view/ScrollCaptureResponse.aidl
new file mode 100644
index 0000000..3de2b80
--- /dev/null
+++ b/core/java/android/view/ScrollCaptureResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+parcelable ScrollCaptureResponse;
diff --git a/core/java/android/view/ScrollCaptureResponse.java b/core/java/android/view/ScrollCaptureResponse.java
new file mode 100644
index 0000000..564113e
--- /dev/null
+++ b/core/java/android/view/ScrollCaptureResponse.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+
+/** @hide */
+@DataClass(genToString = true, genGetters = true)
+public class ScrollCaptureResponse implements Parcelable {
+
+    /** Developer-facing human readable description of the result. */
+    @NonNull
+    private String mDescription = "";
+
+    // Remaining fields are non-null when isConnected() == true
+
+    /** The active connection for a successful result. */
+    @Nullable
+    @DataClass.MaySetToNull
+    private IScrollCaptureConnection mConnection = null;
+
+    /** The bounds of the window within the display */
+    @Nullable
+    private Rect mWindowBounds = null;
+
+    /** The bounds of the scrolling content, in window space. */
+    @Nullable
+    private Rect mBoundsInWindow = null;
+
+    /** The current window title. */
+    @Nullable
+    private String mWindowTitle = null;
+
+    /** Carries additional logging and debugging information when enabled. */
+    @NonNull
+    @DataClass.PluralOf("message")
+    private ArrayList<String> mMessages = new ArrayList<>();
+
+    /** Whether a connection has been returned. */
+    public boolean isConnected() {
+        return mConnection != null;
+    }
+
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/ScrollCaptureResponse.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ ScrollCaptureResponse(
+            @NonNull String description,
+            @Nullable IScrollCaptureConnection connection,
+            @Nullable Rect windowBounds,
+            @Nullable Rect boundsInWindow,
+            @Nullable String windowTitle,
+            @NonNull ArrayList<String> messages) {
+        this.mDescription = description;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mDescription);
+        this.mConnection = connection;
+        this.mWindowBounds = windowBounds;
+        this.mBoundsInWindow = boundsInWindow;
+        this.mWindowTitle = windowTitle;
+        this.mMessages = messages;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mMessages);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Developer-facing human readable description of the result.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * The active connection for a successful result.
+     */
+    @DataClass.Generated.Member
+    public @Nullable IScrollCaptureConnection getConnection() {
+        return mConnection;
+    }
+
+    /**
+     * The bounds of the window within the display
+     */
+    @DataClass.Generated.Member
+    public @Nullable Rect getWindowBounds() {
+        return mWindowBounds;
+    }
+
+    /**
+     * The bounds of the scrolling content, in window space.
+     */
+    @DataClass.Generated.Member
+    public @Nullable Rect getBoundsInWindow() {
+        return mBoundsInWindow;
+    }
+
+    /**
+     * The current window title.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getWindowTitle() {
+        return mWindowTitle;
+    }
+
+    /**
+     * Carries additional logging and debugging information when enabled.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArrayList<String> getMessages() {
+        return mMessages;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "ScrollCaptureResponse { " +
+                "description = " + mDescription + ", " +
+                "connection = " + mConnection + ", " +
+                "windowBounds = " + mWindowBounds + ", " +
+                "boundsInWindow = " + mBoundsInWindow + ", " +
+                "windowTitle = " + mWindowTitle + ", " +
+                "messages = " + mMessages +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mConnection != null) flg |= 0x2;
+        if (mWindowBounds != null) flg |= 0x4;
+        if (mBoundsInWindow != null) flg |= 0x8;
+        if (mWindowTitle != null) flg |= 0x10;
+        dest.writeByte(flg);
+        dest.writeString(mDescription);
+        if (mConnection != null) dest.writeStrongInterface(mConnection);
+        if (mWindowBounds != null) dest.writeTypedObject(mWindowBounds, flags);
+        if (mBoundsInWindow != null) dest.writeTypedObject(mBoundsInWindow, flags);
+        if (mWindowTitle != null) dest.writeString(mWindowTitle);
+        dest.writeStringList(mMessages);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ScrollCaptureResponse(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String description = in.readString();
+        IScrollCaptureConnection connection = (flg & 0x2) == 0 ? null : IScrollCaptureConnection.Stub.asInterface(in.readStrongBinder());
+        Rect windowBounds = (flg & 0x4) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR);
+        Rect boundsInWindow = (flg & 0x8) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR);
+        String windowTitle = (flg & 0x10) == 0 ? null : in.readString();
+        ArrayList<String> messages = new ArrayList<>();
+        in.readStringList(messages);
+
+        this.mDescription = description;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mDescription);
+        this.mConnection = connection;
+        this.mWindowBounds = windowBounds;
+        this.mBoundsInWindow = boundsInWindow;
+        this.mWindowTitle = windowTitle;
+        this.mMessages = messages;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mMessages);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ScrollCaptureResponse> CREATOR
+            = new Parcelable.Creator<ScrollCaptureResponse>() {
+        @Override
+        public ScrollCaptureResponse[] newArray(int size) {
+            return new ScrollCaptureResponse[size];
+        }
+
+        @Override
+        public ScrollCaptureResponse createFromParcel(@NonNull android.os.Parcel in) {
+            return new ScrollCaptureResponse(in);
+        }
+    };
+
+    /**
+     * A builder for {@link ScrollCaptureResponse}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static class Builder {
+
+        private @NonNull String mDescription;
+        private @Nullable IScrollCaptureConnection mConnection;
+        private @Nullable Rect mWindowBounds;
+        private @Nullable Rect mBoundsInWindow;
+        private @Nullable String mWindowTitle;
+        private @NonNull ArrayList<String> mMessages;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Developer-facing human readable description of the result.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setDescription(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mDescription = value;
+            return this;
+        }
+
+        /**
+         * The active connection for a successful result.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setConnection(@Nullable IScrollCaptureConnection value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mConnection = value;
+            return this;
+        }
+
+        /**
+         * The bounds of the window within the display
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setWindowBounds(@NonNull Rect value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mWindowBounds = value;
+            return this;
+        }
+
+        /**
+         * The bounds of the scrolling content, in window space.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setBoundsInWindow(@NonNull Rect value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mBoundsInWindow = value;
+            return this;
+        }
+
+        /**
+         * The current window title.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setWindowTitle(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mWindowTitle = value;
+            return this;
+        }
+
+        /**
+         * Carries additional logging and debugging information when enabled.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setMessages(@NonNull ArrayList<String> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20;
+            mMessages = value;
+            return this;
+        }
+
+        /** @see #setMessages */
+        @DataClass.Generated.Member
+        public @NonNull Builder addMessage(@NonNull String value) {
+            if (mMessages == null) setMessages(new ArrayList<>());
+            mMessages.add(value);
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull ScrollCaptureResponse build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x40; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mDescription = "";
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mConnection = null;
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mWindowBounds = null;
+            }
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mBoundsInWindow = null;
+            }
+            if ((mBuilderFieldsSet & 0x10) == 0) {
+                mWindowTitle = null;
+            }
+            if ((mBuilderFieldsSet & 0x20) == 0) {
+                mMessages = new ArrayList<>();
+            }
+            ScrollCaptureResponse o = new ScrollCaptureResponse(
+                    mDescription,
+                    mConnection,
+                    mWindowBounds,
+                    mBoundsInWindow,
+                    mWindowTitle,
+                    mMessages);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x40) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1612282689462L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/view/ScrollCaptureResponse.java",
+            inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic  boolean isConnected()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/view/ScrollCaptureSearchResults.java b/core/java/android/view/ScrollCaptureSearchResults.java
new file mode 100644
index 0000000..3469b9d
--- /dev/null
+++ b/core/java/android/view/ScrollCaptureSearchResults.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.UiThread;
+import android.graphics.Rect;
+import android.os.CancellationSignal;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Collects nodes in the view hierarchy which have been identified as scrollable content.
+ *
+ * @hide
+ */
+@UiThread
+public final class ScrollCaptureSearchResults {
+    private final Executor mExecutor;
+    private final List<ScrollCaptureTarget> mTargets;
+    private final CancellationSignal mCancel;
+
+    private Runnable mOnCompleteListener;
+    private int mCompleted;
+    private boolean mComplete = true;
+
+    public ScrollCaptureSearchResults(Executor executor) {
+        mExecutor = executor;
+        mTargets = new ArrayList<>();
+        mCancel = new CancellationSignal();
+    }
+
+    // Public
+
+    /**
+     * Add the given target to the results.
+     *
+     * @param target the target to consider
+     */
+    public void addTarget(@NonNull ScrollCaptureTarget target) {
+        requireNonNull(target);
+
+        mTargets.add(target);
+        mComplete = false;
+        final ScrollCaptureCallback callback = target.getCallback();
+        final Consumer<Rect> consumer = new SearchRequest(target);
+
+        // Defer so the view hierarchy scan completes first
+        mExecutor.execute(
+                () -> callback.onScrollCaptureSearch(mCancel, consumer));
+    }
+
+    public boolean isComplete() {
+        return mComplete;
+    }
+
+    /**
+     * Provides a callback to be invoked as soon as all responses have been received from all
+     * targets to this point.
+     *
+     * @param onComplete listener to add
+     */
+    public void setOnCompleteListener(Runnable onComplete) {
+        if (mComplete) {
+            onComplete.run();
+        } else {
+            mOnCompleteListener = onComplete;
+        }
+    }
+
+    /**
+     * Indicates whether the search results are empty.
+     *
+     * @return true if no targets have been added
+     */
+    public boolean isEmpty() {
+        return mTargets.isEmpty();
+    }
+
+    /**
+     * Force the results to complete now, cancelling any pending requests and calling a complete
+     * listener if provided.
+     */
+    public void finish() {
+        if (!mComplete) {
+            mCancel.cancel();
+            signalComplete();
+        }
+    }
+
+    private void signalComplete() {
+        mComplete = true;
+        mTargets.sort(PRIORITY_ORDER);
+        if (mOnCompleteListener != null) {
+            mOnCompleteListener.run();
+            mOnCompleteListener = null;
+        }
+    }
+
+    @VisibleForTesting
+    public List<ScrollCaptureTarget> getTargets() {
+        return new ArrayList<>(mTargets);
+    }
+
+    /**
+     * Get the top ranked result out of all completed requests.
+     *
+     * @return the top ranked result
+     */
+    public ScrollCaptureTarget getTopResult() {
+        ScrollCaptureTarget target = mTargets.isEmpty() ? null : mTargets.get(0);
+        return target != null && target.getScrollBounds() != null ? target : null;
+    }
+
+    private class SearchRequest implements Consumer<Rect> {
+        private ScrollCaptureTarget mTarget;
+
+        SearchRequest(ScrollCaptureTarget target) {
+            mTarget = target;
+        }
+
+        @Override
+        public void accept(Rect scrollBounds) {
+            if (mTarget == null || mCancel.isCanceled()) {
+                return;
+            }
+            mExecutor.execute(() -> consume(scrollBounds));
+        }
+
+        private void consume(Rect scrollBounds) {
+            if (mTarget == null || mCancel.isCanceled()) {
+                return;
+            }
+            if (!nullOrEmpty(scrollBounds)) {
+                mTarget.setScrollBounds(scrollBounds);
+                mTarget.updatePositionInWindow();
+            }
+            mCompleted++;
+            mTarget = null;
+
+            // All done?
+            if (mCompleted == mTargets.size()) {
+                signalComplete();
+            }
+        }
+    }
+
+    private static final int AFTER = 1;
+    private static final int BEFORE = -1;
+    private static final int EQUAL = 0;
+
+    static final Comparator<ScrollCaptureTarget> PRIORITY_ORDER = (a, b) -> {
+        if (a == null && b == null) {
+            return 0;
+        } else if (a == null || b == null) {
+            return (a == null) ? 1 : -1;
+        }
+
+        boolean emptyScrollBoundsA = nullOrEmpty(a.getScrollBounds());
+        boolean emptyScrollBoundsB = nullOrEmpty(b.getScrollBounds());
+        if (emptyScrollBoundsA || emptyScrollBoundsB) {
+            if (emptyScrollBoundsA && emptyScrollBoundsB) {
+                return EQUAL;
+            }
+            // Prefer the one with a non-empty scroll bounds
+            if (emptyScrollBoundsA) {
+                return AFTER;
+            }
+            return BEFORE;
+        }
+
+        final View viewA = a.getContainingView();
+        final View viewB = b.getContainingView();
+
+        // Prefer any view with scrollCaptureHint="INCLUDE", over one without
+        // This is an escape hatch for the next rule (descendants first)
+        boolean hintIncludeA = hasIncludeHint(viewA);
+        boolean hintIncludeB = hasIncludeHint(viewB);
+        if (hintIncludeA != hintIncludeB) {
+            return (hintIncludeA) ? BEFORE : AFTER;
+        }
+        // If the views are relatives, prefer the descendant. This allows implementations to
+        // leverage nested scrolling APIs by interacting with the innermost scrollable view (as
+        // would happen with touch input).
+        if (isDescendant(viewA, viewB)) {
+            return BEFORE;
+        }
+        if (isDescendant(viewB, viewA)) {
+            return AFTER;
+        }
+
+        // finally, prefer one with larger scroll bounds
+        int scrollAreaA = area(a.getScrollBounds());
+        int scrollAreaB = area(b.getScrollBounds());
+        return (scrollAreaA >= scrollAreaB) ? BEFORE : AFTER;
+    };
+
+    private static int area(Rect r) {
+        return r.width() * r.height();
+    }
+
+    private static boolean nullOrEmpty(Rect r) {
+        return r == null || r.isEmpty();
+    }
+
+    private static boolean hasIncludeHint(View view) {
+        return (view.getScrollCaptureHint() & View.SCROLL_CAPTURE_HINT_INCLUDE) != 0;
+    }
+
+    /**
+     * Determines if {@code otherView} is a descendant of {@code view}.
+     *
+     * @param view      a view
+     * @param otherView another view
+     * @return true if {@code view} is an ancestor of {@code otherView}
+     */
+    private static boolean isDescendant(@NonNull View view, @NonNull View otherView) {
+        if (view == otherView) {
+            return false;
+        }
+        ViewParent otherParent = otherView.getParent();
+        while (otherParent != view && otherParent != null) {
+            otherParent = otherParent.getParent();
+        }
+        return otherParent == view;
+    }
+
+    void dump(IndentingPrintWriter writer) {
+        writer.println("results:");
+        writer.increaseIndent();
+        writer.println("complete: " + isComplete());
+        writer.println("cancelled: " + mCancel.isCanceled());
+        writer.println("targets:");
+        writer.increaseIndent();
+        if (isEmpty()) {
+            writer.println("None");
+        } else {
+            for (int i = 0; i < mTargets.size(); i++) {
+                writer.println("[" + i + "]");
+                writer.increaseIndent();
+                mTargets.get(i).dump(writer);
+                writer.decreaseIndent();
+            }
+            writer.decreaseIndent();
+        }
+        writer.decreaseIndent();
+    }
+}
diff --git a/core/java/android/view/ScrollCaptureSession.java b/core/java/android/view/ScrollCaptureSession.java
index 92617a3..748e7ea 100644
--- a/core/java/android/view/ScrollCaptureSession.java
+++ b/core/java/android/view/ScrollCaptureSession.java
@@ -16,18 +16,15 @@
 
 package android.view;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.graphics.Point;
 import android.graphics.Rect;
 
 /**
  * A session represents the scope of interaction between a {@link ScrollCaptureCallback} and the
- * system during an active scroll capture operation. During the scope of a session, a callback
- * will receive a series of requests for image data. Resources provided here are valid for use
- * until {@link ScrollCaptureCallback#onScrollCaptureEnd(Runnable)}.
- *
- * @hide
+ * system during an active scroll capture operation.
  */
 public class ScrollCaptureSession {
 
@@ -35,22 +32,27 @@
     private final Rect mScrollBounds;
     private final Point mPositionInWindow;
 
-    @Nullable
-    private ScrollCaptureConnection mConnection;
-
-    /** @hide */
-    public ScrollCaptureSession(Surface surface, Rect scrollBounds, Point positionInWindow,
-            ScrollCaptureConnection connection) {
-        mSurface = surface;
-        mScrollBounds = scrollBounds;
-        mPositionInWindow = positionInWindow;
-        mConnection = connection;
+    /**
+     * Constructs a new session instance.
+     *
+     * @param surface the surface to consume generated images
+     * @param scrollBounds the bounds of the capture area within the containing view
+     * @param positionInWindow the offset of scrollBounds within the window
+     */
+    public ScrollCaptureSession(@NonNull Surface surface, @NonNull Rect scrollBounds,
+            @NonNull Point positionInWindow) {
+        mSurface = requireNonNull(surface);
+        mScrollBounds = requireNonNull(scrollBounds);
+        mPositionInWindow = requireNonNull(positionInWindow);
     }
 
     /**
      * Returns a
      * <a href="https://source.android.com/devices/graphics/arch-bq-gralloc">BufferQueue</a> in the
      * form of a {@link Surface} for transfer of image buffers.
+     * <p>
+     * The surface is guaranteed to remain {@link Surface#isValid() valid} until the session
+     * {@link ScrollCaptureCallback#onScrollCaptureEnd(Runnable) ends}.
      *
      * @return the surface for transferring image buffers
      * @throws IllegalStateException if the session has been closed
@@ -80,26 +82,4 @@
     public Point getPositionInWindow() {
         return mPositionInWindow;
     }
-
-    /**
-     * Notify the system that an a buffer has been posted via the getSurface() channel.
-     *
-     * @param frameNumber  the frame number of the queued buffer
-     * @param capturedArea the area captured, relative to scroll bounds
-     */
-    public void notifyBufferSent(long frameNumber, @NonNull Rect capturedArea) {
-        if (mConnection != null) {
-            mConnection.onRequestImageCompleted(frameNumber, capturedArea);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void disconnect() {
-        mConnection = null;
-        if (mSurface.isValid()) {
-            mSurface.release();
-        }
-    }
 }
diff --git a/core/java/android/view/ScrollCaptureTarget.java b/core/java/android/view/ScrollCaptureTarget.java
index f3fcabb..4fd4889 100644
--- a/core/java/android/view/ScrollCaptureTarget.java
+++ b/core/java/android/view/ScrollCaptureTarget.java
@@ -22,14 +22,16 @@
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 
 import com.android.internal.util.FastMath;
 
+import java.io.PrintWriter;
+import java.util.function.Consumer;
+
 /**
  * A target collects the set of contextual information for a ScrollCaptureHandler discovered during
  * a {@link View#dispatchScrollCaptureSearch scroll capture search}.
- *
- * @hide
  */
 public final class ScrollCaptureTarget {
     private final View mContainingView;
@@ -41,7 +43,6 @@
 
     private final float[] mTmpFloatArr = new float[2];
     private final Matrix mMatrixViewLocalToWindow = new Matrix();
-    private final Rect mTmpRect = new Rect();
 
     public ScrollCaptureTarget(@NonNull View scrollTarget, @NonNull Rect localVisibleRect,
             @NonNull Point positionInWindow, @NonNull ScrollCaptureCallback callback) {
@@ -52,7 +53,10 @@
         mPositionInWindow = positionInWindow;
     }
 
-    /** @return the hint that the {@code containing view} had during the scroll capture search */
+    /**
+     * @return the hint that the {@code containing view} had during the scroll capture search
+     * @see View#getScrollCaptureHint()
+     */
     @View.ScrollCaptureHint
     public int getHint() {
         return mHint;
@@ -71,8 +75,7 @@
     }
 
     /**
-     * Returns the un-clipped, visible bounds of the containing view during the scroll capture
-     * search. This is used to determine on-screen area to assist in selecting the primary target.
+     * Returns the visible bounds of the containing view.
      *
      * @return the visible bounds of the {@code containing view} in view-local coordinates
      */
@@ -81,13 +84,17 @@
         return mLocalVisibleRect;
     }
 
-    /** @return the position of the {@code containing view} within the window */
+    /** @return the position of the visible bounds of the containing view within the window */
     @NonNull
     public Point getPositionInWindow() {
         return mPositionInWindow;
     }
 
-    /** @return the {@code scroll bounds} for this {@link ScrollCaptureCallback callback} */
+    /**
+     * @return the {@code scroll bounds} for this {@link ScrollCaptureCallback callback}
+     *
+     * @see ScrollCaptureCallback#onScrollCaptureSearch(CancellationSignal, Consumer)
+     */
     @Nullable
     public Rect getScrollBounds() {
         return mScrollBounds;
@@ -119,8 +126,8 @@
     }
 
     /**
-     * Refresh the value of {@link #mLocalVisibleRect} and {@link #mPositionInWindow} based on the
-     * current state of the {@code containing view}.
+     * Refresh the local visible bounds and it's offset within the window, based on the current
+     * state of the {@code containing view}.
      */
     @UiThread
     public void updatePositionInWindow() {
@@ -132,4 +139,27 @@
         roundIntoPoint(mPositionInWindow, mTmpFloatArr);
     }
 
+    public String toString() {
+        return "ScrollCaptureTarget{" + "view=" + mContainingView
+                + ", callback=" + mCallback
+                + ", scrollBounds=" + mScrollBounds
+                + ", localVisibleRect=" + mLocalVisibleRect
+                + ", positionInWindow=" + mPositionInWindow
+                + "}";
+    }
+
+    void dump(@NonNull PrintWriter writer) {
+        View view = getContainingView();
+        writer.println("view: " + view);
+        writer.println("hint: " + mHint);
+        writer.println("callback: " + mCallback);
+        writer.println("scrollBounds: "
+                + (mScrollBounds == null ? "null" : mScrollBounds.toShortString()));
+        Point inWindow = getPositionInWindow();
+        writer.println("positionInWindow: "
+                + ((inWindow == null) ? "null" : "[" + inWindow.x + "," + inWindow.y + "]"));
+        Rect localVisible = getLocalVisibleRect();
+        writer.println("localVisibleRect: "
+                + (localVisible == null ? "null" : localVisible.toShortString()));
+    }
 }
diff --git a/core/java/android/view/ScrollCaptureTargetResolver.java b/core/java/android/view/ScrollCaptureTargetResolver.java
deleted file mode 100644
index e4316bb..0000000
--- a/core/java/android/view/ScrollCaptureTargetResolver.java
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.annotation.AnyThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UiThread;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-
-import java.util.Queue;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Consumer;
-
-/**
- * Queries additional state from a list of {@link ScrollCaptureTarget targets} via asynchronous
- * callbacks, then aggregates and reduces the target list to a single target, or null if no target
- * is suitable.
- * <p>
- * The rules for selection are (in order):
- * <ul>
- * <li>prefer getScrollBounds(): non-empty
- * <li>prefer View.getScrollCaptureHint == SCROLL_CAPTURE_HINT_INCLUDE
- * <li>prefer descendants before parents
- * <li>prefer larger area for getScrollBounds() (clipped to view bounds)
- * </ul>
- *
- * <p>
- * All calls to {@link ScrollCaptureCallback#onScrollCaptureSearch} are made on the main thread,
- * with results are queued and consumed to the main thread as well.
- *
- * @see #start(Handler, long, Consumer)
- *
- * @hide
- */
-@UiThread
-public class ScrollCaptureTargetResolver {
-    private static final String TAG = "ScrollCaptureTargetRes";
-
-    private final Object mLock = new Object();
-
-    private final Queue<ScrollCaptureTarget> mTargets;
-    private Handler mHandler;
-    private long mTimeLimitMillis;
-
-    private Consumer<ScrollCaptureTarget> mWhenComplete;
-    private int mPendingBoundsRequests;
-    private long mDeadlineMillis;
-
-    private ScrollCaptureTarget mResult;
-    private boolean mFinished;
-
-    private boolean mStarted;
-
-    private static int area(Rect r) {
-        return r.width() * r.height();
-    }
-
-    private static boolean nullOrEmpty(Rect r) {
-        return r == null || r.isEmpty();
-    }
-
-    /**
-     * Binary operator which selects the best {@link ScrollCaptureTarget}.
-     */
-    private static ScrollCaptureTarget chooseTarget(ScrollCaptureTarget a, ScrollCaptureTarget b) {
-        if (a == null && b == null) {
-            return null;
-        } else if (a == null || b == null) {
-            ScrollCaptureTarget c = (a == null) ? b : a;
-            return c;
-        }
-
-        boolean emptyScrollBoundsA = nullOrEmpty(a.getScrollBounds());
-        boolean emptyScrollBoundsB = nullOrEmpty(b.getScrollBounds());
-        if (emptyScrollBoundsA || emptyScrollBoundsB) {
-            if (emptyScrollBoundsA && emptyScrollBoundsB) {
-                // Both have an empty or null scrollBounds
-                Log.d(TAG, "chooseTarget: (both have empty or null bounds) return " + null);
-                return null;
-            }
-            // Prefer the one with a non-empty scroll bounds
-            if (emptyScrollBoundsA) {
-                Log.d(TAG, "chooseTarget: (a has empty or null bounds) return " + b);
-                return b;
-            }
-            Log.d(TAG, "chooseTarget: (b has empty or null bounds) return " + a);
-            return a;
-        }
-
-        final View viewA = a.getContainingView();
-        final View viewB = b.getContainingView();
-
-        // Prefer any view with scrollCaptureHint="INCLUDE", over one without
-        // This is an escape hatch for the next rule (descendants first)
-        boolean hintIncludeA = hasIncludeHint(viewA);
-        boolean hintIncludeB = hasIncludeHint(viewB);
-        if (hintIncludeA != hintIncludeB) {
-            ScrollCaptureTarget c = (hintIncludeA) ? a : b;
-            Log.d(TAG, "chooseTarget: (has hint=INCLUDE) return " + c);
-            return c;
-        }
-
-        // If the views are relatives, prefer the descendant. This allows implementations to
-        // leverage nested scrolling APIs by interacting with the innermost scrollable view (as
-        // would happen with touch input).
-        if (isDescendant(viewA, viewB)) {
-            Log.d(TAG, "chooseTarget: (b is descendant of a) return " + b);
-            return b;
-        }
-        if (isDescendant(viewB, viewA)) {
-            Log.d(TAG, "chooseTarget: (a is descendant of b) return " + a);
-            return a;
-        }
-
-        // finally, prefer one with larger scroll bounds
-        int scrollAreaA = area(a.getScrollBounds());
-        int scrollAreaB = area(b.getScrollBounds());
-        ScrollCaptureTarget c = (scrollAreaA >= scrollAreaB) ? a : b;
-        Log.d(TAG, "chooseTarget: return " + c);
-        return c;
-    }
-
-    /**
-     * Creates an instance to query and filter {@code target}.
-     *
-     * @param targets   a list of {@link ScrollCaptureTarget} as collected by {@link
-     *                  View#dispatchScrollCaptureSearch}.
-     * @see #start(Handler, long, Consumer)
-     */
-    public ScrollCaptureTargetResolver(Queue<ScrollCaptureTarget> targets) {
-        mTargets = targets;
-    }
-
-    void checkThread() {
-        if (mHandler.getLooper() != Looper.myLooper()) {
-            throw new IllegalStateException("Called from wrong thread! ("
-                    + Thread.currentThread().getName() + ")");
-        }
-    }
-
-    /**
-     * Blocks until a result is returned (after completion or timeout).
-     * <p>
-     * For testing only. Normal usage should receive a callback after calling {@link #start}.
-     */
-    @VisibleForTesting
-    public ScrollCaptureTarget waitForResult() throws InterruptedException {
-        synchronized (mLock) {
-            while (!mFinished) {
-                mLock.wait();
-            }
-        }
-        return mResult;
-    }
-
-    private void supplyResult(ScrollCaptureTarget target) {
-        checkThread();
-        if (mFinished) {
-            return;
-        }
-        mResult = chooseTarget(mResult, target);
-        boolean finish = mPendingBoundsRequests == 0
-                || SystemClock.uptimeMillis() >= mDeadlineMillis;
-        if (finish) {
-            mPendingBoundsRequests = 0;
-            mWhenComplete.accept(mResult);
-            synchronized (mLock) {
-                mFinished = true;
-                mLock.notify();
-            }
-            mWhenComplete = null;
-        }
-    }
-
-    /**
-     * Asks all targets for {@link ScrollCaptureCallback#onScrollCaptureSearch(Consumer)
-     * scrollBounds}, and selects the primary target according to the {@link
-     * #chooseTarget} function.
-     *
-     * @param timeLimitMillis the amount of time to wait for all responses before delivering the top
-     *                        result
-     * @param resultConsumer  the consumer to receive the primary target
-     */
-    @AnyThread
-    public void start(Handler uiHandler, long timeLimitMillis,
-            Consumer<ScrollCaptureTarget> resultConsumer) {
-        synchronized (mLock) {
-            if (mStarted) {
-                throw new IllegalStateException("already started!");
-            }
-            if (timeLimitMillis < 0) {
-                throw new IllegalArgumentException("Time limit must be positive");
-            }
-            mHandler = uiHandler;
-            mTimeLimitMillis = timeLimitMillis;
-            mWhenComplete = resultConsumer;
-            if (mTargets.isEmpty()) {
-                mHandler.post(() -> supplyResult(null));
-                return;
-            }
-            mStarted = true;
-            uiHandler.post(this::run);
-        }
-    }
-
-    private void run() {
-        checkThread();
-
-        mPendingBoundsRequests = mTargets.size();
-        for (ScrollCaptureTarget target : mTargets) {
-            queryTarget(target);
-        }
-        mDeadlineMillis = SystemClock.uptimeMillis() + mTimeLimitMillis;
-        mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis);
-    }
-
-    private final Runnable mTimeoutRunnable = () -> {
-        checkThread();
-        supplyResult(null);
-    };
-
-    /**
-     * Adds a target to the list and requests {@link ScrollCaptureCallback#onScrollCaptureSearch}
-     * scrollBounds} from it. Results are returned by a call to {@link #onScrollBoundsProvided}.
-     *
-     * @param target the target to add
-     */
-    @UiThread
-    private void queryTarget(@NonNull ScrollCaptureTarget target) {
-        checkThread();
-        final ScrollCaptureCallback callback = target.getCallback();
-        // from the UI thread, request scroll bounds
-        callback.onScrollCaptureSearch(
-                // allow only one callback to onReady.accept():
-                new SingletonConsumer<Rect>(
-                        // Queue and consume on the UI thread
-                        ((scrollBounds) -> mHandler.post(
-                                () -> onScrollBoundsProvided(target, scrollBounds)))));
-    }
-
-    @UiThread
-    private void onScrollBoundsProvided(ScrollCaptureTarget target, @Nullable Rect scrollBounds) {
-        checkThread();
-        if (mFinished) {
-            return;
-        }
-
-        // Record progress.
-        mPendingBoundsRequests--;
-
-        // Remove the timeout.
-        mHandler.removeCallbacks(mTimeoutRunnable);
-
-        boolean doneOrTimedOut = mPendingBoundsRequests == 0
-                || SystemClock.uptimeMillis() >= mDeadlineMillis;
-
-        final View containingView = target.getContainingView();
-        if (!nullOrEmpty(scrollBounds) && containingView.isAggregatedVisible()) {
-            target.updatePositionInWindow();
-            target.setScrollBounds(scrollBounds);
-            supplyResult(target);
-        }
-
-        if (!mFinished) {
-            // Reschedule the timeout.
-            mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis);
-        }
-    }
-
-    private static boolean hasIncludeHint(View view) {
-        return (view.getScrollCaptureHint() & View.SCROLL_CAPTURE_HINT_INCLUDE) != 0;
-    }
-
-    /**
-     * Determines if {@code otherView} is a descendant of {@code view}.
-     *
-     * @param view      a view
-     * @param otherView another view
-     * @return true if {@code view} is an ancestor of {@code otherView}
-     */
-    private static boolean isDescendant(@NonNull View view, @NonNull View otherView) {
-        if (view == otherView) {
-            return false;
-        }
-        ViewParent otherParent = otherView.getParent();
-        while (otherParent != view && otherParent != null) {
-            otherParent = otherParent.getParent();
-        }
-        return otherParent == view;
-    }
-
-    /**
-     * A safe wrapper for a consumer callbacks intended to accept a single value. It ensures
-     * that the receiver of the consumer does not retain a reference to {@code target} after use nor
-     * cause race conditions by invoking {@link Consumer#accept accept} more than once.
-     */
-    static class SingletonConsumer<T> implements Consumer<T> {
-        final AtomicReference<Consumer<T>> mAtomicRef;
-
-        /**
-         * @param target the target consumer
-         **/
-        SingletonConsumer(Consumer<T> target) {
-            mAtomicRef = new AtomicReference<>(target);
-        }
-
-        @Override
-        public void accept(T t) {
-            final Consumer<T> consumer = mAtomicRef.getAndSet(null);
-            if (consumer != null) {
-                consumer.accept(t);
-            }
-        }
-    }
-}
diff --git a/core/java/android/view/SoundEffectConstants.java b/core/java/android/view/SoundEffectConstants.java
index 8d891bb..f177451 100644
--- a/core/java/android/view/SoundEffectConstants.java
+++ b/core/java/android/view/SoundEffectConstants.java
@@ -16,12 +16,21 @@
 
 package android.view;
 
+import android.media.AudioManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import java.util.Random;
+
 /**
- * Constants to be used to play sound effects via {@link View#playSoundEffect(int)} 
+ * Constants to be used to play sound effects via {@link View#playSoundEffect(int)}
  */
 public class SoundEffectConstants {
 
     private SoundEffectConstants() {}
+    private static final Random NAVIGATION_REPEAT_RANDOMIZER = new Random();
+    private static int sLastNavigationRepeatSoundEffectId = -1;
 
     public static final int CLICK = 0;
 
@@ -29,6 +38,14 @@
     public static final int NAVIGATION_UP = 2;
     public static final int NAVIGATION_RIGHT = 3;
     public static final int NAVIGATION_DOWN = 4;
+    /** Sound effect for a repeatedly triggered navigation, e.g. due to long pressing a button */
+    public static final int NAVIGATION_REPEAT_LEFT = 5;
+    /** @see #NAVIGATION_REPEAT_LEFT */
+    public static final int NAVIGATION_REPEAT_UP = 6;
+    /** @see #NAVIGATION_REPEAT_LEFT */
+    public static final int NAVIGATION_REPEAT_RIGHT = 7;
+    /** @see #NAVIGATION_REPEAT_LEFT */
+    public static final int NAVIGATION_REPEAT_DOWN = 8;
 
     /**
      * Get the sonification constant for the focus directions.
@@ -40,7 +57,7 @@
      * @throws {@link IllegalArgumentException} when the passed direction is not one of the
      *     documented values.
      */
-    public static int getContantForFocusDirection(int direction) {
+    public static int getContantForFocusDirection(@View.FocusDirection int direction) {
         switch (direction) {
             case View.FOCUS_RIGHT:
                 return SoundEffectConstants.NAVIGATION_RIGHT;
@@ -56,4 +73,65 @@
         throw new IllegalArgumentException("direction must be one of "
                 + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD, FOCUS_BACKWARD}.");
     }
+
+    /**
+     * Get the sonification constant for the focus directions
+     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
+     *     {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD}
+     *     or {@link View#FOCUS_BACKWARD}
+     * @param repeating True if the user long-presses a direction
+     * @return The appropriate sonification constant
+     * @throws IllegalArgumentException when the passed direction is not one of the
+     *      documented values.
+     */
+    public static int getConstantForFocusDirection(@View.FocusDirection int direction,
+            boolean repeating) {
+        if (repeating) {
+            switch (direction) {
+                case View.FOCUS_RIGHT:
+                    return SoundEffectConstants.NAVIGATION_REPEAT_RIGHT;
+                case View.FOCUS_FORWARD:
+                case View.FOCUS_DOWN:
+                    return SoundEffectConstants.NAVIGATION_REPEAT_DOWN;
+                case View.FOCUS_LEFT:
+                    return SoundEffectConstants.NAVIGATION_REPEAT_LEFT;
+                case View.FOCUS_BACKWARD:
+                case View.FOCUS_UP:
+                    return SoundEffectConstants.NAVIGATION_REPEAT_UP;
+            }
+            throw new IllegalArgumentException("direction must be one of {FOCUS_UP, FOCUS_DOWN, "
+                    + "FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD, FOCUS_BACKWARD}.");
+        } else {
+            return getContantForFocusDirection(direction);
+        }
+    }
+
+    /**
+     * @param effectId any of the effect ids defined in {@link SoundEffectConstants}
+     * @return true if the given effect id is a navigation repeat one
+     * @hide
+     */
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public static boolean isNavigationRepeat(int effectId) {
+        return effectId == SoundEffectConstants.NAVIGATION_REPEAT_DOWN
+                || effectId == SoundEffectConstants.NAVIGATION_REPEAT_LEFT
+                || effectId == SoundEffectConstants.NAVIGATION_REPEAT_RIGHT
+                || effectId == SoundEffectConstants.NAVIGATION_REPEAT_UP;
+    }
+
+    /**
+     * @return The next navigation repeat sound effect id, chosen at random in a non-repeating
+     * fashion
+     * @hide
+     */
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public static int nextNavigationRepeatSoundEffectId() {
+        int next = NAVIGATION_REPEAT_RANDOMIZER.nextInt(
+                AudioManager.NUM_NAVIGATION_REPEAT_SOUND_EFFECTS - 1);
+        if (next >= sLastNavigationRepeatSoundEffectId) {
+            next++;
+        }
+        sLastNavigationRepeatSoundEffectId = next;
+        return AudioManager.getNthNavigationRepeatSoundEffect(next);
+    }
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0832578..03dd100 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -3504,64 +3504,4 @@
     public static Transaction getGlobalTransaction() {
         return sGlobalTransaction;
     }
-
-    /**
-     * Wrapper for sending blur data to SurfaceFlinger.
-     * @hide
-     */
-    public static final class BlurRegion {
-        public int blurRadius;
-        public float cornerRadiusTL;
-        public float cornerRadiusTR;
-        public float cornerRadiusBL;
-        public float cornerRadiusBR;
-        public float alpha = 1;
-        public boolean visible = true;
-        public final Rect rect = new Rect();
-
-        private final float[] mFloatArray = new float[10];
-
-        public BlurRegion() {
-        }
-
-        public BlurRegion(BlurRegion other) {
-            rect.set(other.rect);
-            blurRadius = other.blurRadius;
-            alpha = other.alpha;
-            cornerRadiusTL = other.cornerRadiusTL;
-            cornerRadiusTR = other.cornerRadiusTR;
-            cornerRadiusBL = other.cornerRadiusBL;
-            cornerRadiusBR = other.cornerRadiusBR;
-        }
-
-        /**
-         * Serializes this class into a float array that's more JNI friendly.
-         */
-        public float[] toFloatArray() {
-            mFloatArray[0] = blurRadius;
-            mFloatArray[1] = alpha;
-            mFloatArray[2] = rect.left;
-            mFloatArray[3] = rect.top;
-            mFloatArray[4] = rect.right;
-            mFloatArray[5] = rect.bottom;
-            mFloatArray[6] = cornerRadiusTL;
-            mFloatArray[7] = cornerRadiusTR;
-            mFloatArray[8] = cornerRadiusBL;
-            mFloatArray[9] = cornerRadiusBR;
-            return mFloatArray;
-        }
-
-        @Override
-        public String toString() {
-            return "BlurRegion{"
-                    + "blurRadius=" + blurRadius
-                    + ", corners={" + cornerRadiusTL
-                    + "," + cornerRadiusTR
-                    + "," + cornerRadiusBL
-                    + "," + cornerRadiusBR
-                    + "}, alpha=" + alpha
-                    + ", rect=" + rect
-                    + "}";
-        }
-    }
 }
diff --git a/core/java/android/view/SurfaceControlFpsListener.java b/core/java/android/view/SurfaceControlFpsListener.java
new file mode 100644
index 0000000..517b0fb
--- /dev/null
+++ b/core/java/android/view/SurfaceControlFpsListener.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+
+/**
+ * Listener for sampling the frames per second for a SurfaceControl and its children.
+ * This should only be used by a system component that needs to listen to a SurfaceControl's
+ * tree's FPS when it is not actively submitting transactions for that SurfaceControl.
+ * Otherwise, ASurfaceTransaction_OnComplete callbacks should be used.
+ *
+ * @hide
+ */
+public abstract class SurfaceControlFpsListener {
+    private long mNativeListener;
+
+    public SurfaceControlFpsListener() {
+        mNativeListener = nativeCreate(this);
+    }
+
+    protected void destroy() {
+        if (mNativeListener == 0) {
+            return;
+        }
+        unregister();
+        nativeDestroy(mNativeListener);
+        mNativeListener = 0;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            destroy();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Reports the fps from the registered SurfaceControl
+     */
+    public abstract void onFpsReported(float fps);
+
+    /**
+     * Registers the sampling listener.
+     */
+    public void register(@NonNull SurfaceControl layer) {
+        if (mNativeListener == 0) {
+            return;
+        }
+
+        nativeRegister(mNativeListener, layer.mNativeObject);
+    }
+
+    /**
+     * Unregisters the sampling listener.
+     */
+    public void unregister() {
+        if (mNativeListener == 0) {
+            return;
+        }
+        nativeUnregister(mNativeListener);
+    }
+
+    /**
+     * Dispatch the collected sample.
+     *
+     * Called from native code on a binder thread.
+     */
+    private static void dispatchOnFpsReported(SurfaceControlFpsListener listener, float fps) {
+        listener.onFpsReported(fps);
+    }
+
+    private static native long nativeCreate(SurfaceControlFpsListener thiz);
+    private static native void nativeDestroy(long ptr);
+    private static native void nativeRegister(long ptr, long layerObject);
+    private static native void nativeUnregister(long ptr);
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3789324..ebef464 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -184,10 +184,10 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Queue;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -1484,7 +1484,6 @@
      *
      * @see #getScrollCaptureHint()
      * @see #setScrollCaptureHint(int)
-     * @hide
      */
     public static final int SCROLL_CAPTURE_HINT_AUTO = 0;
 
@@ -1495,7 +1494,6 @@
      *
      * @see #getScrollCaptureHint()
      * @see #setScrollCaptureHint(int)
-     * @hide
      */
     public static final int SCROLL_CAPTURE_HINT_EXCLUDE = 0x1;
 
@@ -1506,7 +1504,6 @@
      *
      * @see #getScrollCaptureHint()
      * @see #setScrollCaptureHint(int)
-     * @hide
      */
     public static final int SCROLL_CAPTURE_HINT_INCLUDE = 0x2;
 
@@ -1517,7 +1514,6 @@
      *
      * @see #getScrollCaptureHint()
      * @see #setScrollCaptureHint(int)
-     * @hide
      */
     public static final int SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS = 0x4;
 
@@ -30053,8 +30049,6 @@
      * Returns the current scroll capture hint for this view.
      *
      * @return the current scroll capture hint
-     *
-     * @hide
      */
     @ScrollCaptureHint
     public int getScrollCaptureHint() {
@@ -30067,8 +30061,6 @@
      * scroll capture targets.
      *
      * @param hint the scrollCaptureHint flags value to set
-     *
-     * @hide
      */
     public void setScrollCaptureHint(@ScrollCaptureHint int hint) {
         mPrivateFlags4 &= ~PFLAG4_SCROLL_CAPTURE_HINT_MASK;
@@ -30088,10 +30080,8 @@
      * setting a custom callback to help ensure it is selected as the target.
      *
      * @param callback the new callback to assign
-     *
-     * @hide
      */
-    public void setScrollCaptureCallback(@Nullable ScrollCaptureCallback callback) {
+    public final void setScrollCaptureCallback(@Nullable ScrollCaptureCallback callback) {
         getListenerInfo().mScrollCaptureCallback = callback;
     }
 
@@ -30110,29 +30100,54 @@
     }
 
     /**
+     * Dispatch a scroll capture search request down the view hierarchy.
+     *
+     * @param localVisibleRect the visible area of this ViewGroup in local coordinates, according to
+     *                         the parent
+     * @param windowOffset     the offset of this view within the window
+     * @param targets          accepts potential scroll capture targets; {@link Consumer#accept
+     *                         results.accept} may be called zero or more times on the calling
+     *                         thread before onScrollCaptureSearch returns
+     */
+    public void dispatchScrollCaptureSearch(
+            @NonNull Rect localVisibleRect, @NonNull Point windowOffset,
+            @NonNull Consumer<ScrollCaptureTarget> targets) {
+        onScrollCaptureSearch(localVisibleRect, windowOffset, targets);
+    }
+
+    /**
      * Called when scroll capture is requested, to search for appropriate content to scroll. If
      * applicable, this view adds itself to the provided list for consideration, subject to the
      * flags set by {@link #setScrollCaptureHint}.
      *
      * @param localVisibleRect the local visible rect of this view
      * @param windowOffset     the offset of localVisibleRect within the window
-     * @param targets          a queue which collects potential targets
-     *
+     * @param targets          accepts potential scroll capture targets; {@link Consumer#accept
+     *                         results.accept} may be called zero or more times on the calling
+     *                         thread before onScrollCaptureSearch returns
      * @throws IllegalStateException if this view is not attached to a window
-     * @hide
      */
-    public void dispatchScrollCaptureSearch(@NonNull Rect localVisibleRect,
-            @NonNull Point windowOffset, @NonNull Queue<ScrollCaptureTarget> targets) {
+    public void onScrollCaptureSearch(@NonNull Rect localVisibleRect,
+            @NonNull Point windowOffset, @NonNull Consumer<ScrollCaptureTarget> targets) {
         int hint = getScrollCaptureHint();
         if ((hint & SCROLL_CAPTURE_HINT_EXCLUDE) != 0) {
             return;
         }
+        boolean rectIsVisible = true;
+
+        // Apply clipBounds if present.
+        if (mClipBounds != null) {
+            rectIsVisible = localVisibleRect.intersect(mClipBounds);
+        }
+        if (!rectIsVisible) {
+            return;
+        }
 
         // Get a callback provided by the framework, library or application.
         ScrollCaptureCallback callback =
                 (mListenerInfo == null) ? null : mListenerInfo.mScrollCaptureCallback;
 
-        // Try internal support for standard scrolling containers.
+        // Try framework support for standard scrolling containers.
         if (callback == null) {
             callback = createScrollCaptureCallbackInternal(localVisibleRect, windowOffset);
         }
@@ -30142,7 +30157,7 @@
             // Add to the list for consideration
             Point offset = new Point(windowOffset.x, windowOffset.y);
             Rect rect = new Rect(localVisibleRect);
-            targets.add(new ScrollCaptureTarget(this, rect, offset, callback));
+            targets.accept(new ScrollCaptureTarget(this, rect, offset, callback));
         }
     }
 
diff --git a/core/java/android/view/ViewFrameInfo.java b/core/java/android/view/ViewFrameInfo.java
index 890d071..d4aaa61 100644
--- a/core/java/android/view/ViewFrameInfo.java
+++ b/core/java/android/view/ViewFrameInfo.java
@@ -58,8 +58,8 @@
     public void populateFrameInfo(FrameInfo frameInfo) {
         frameInfo.frameInfo[FrameInfo.FLAGS] |= flags;
         frameInfo.frameInfo[FrameInfo.DRAW_START] = drawStart;
-        frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT] = oldestInputEventTime;
-        frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT] = newestInputEventTime;
+        // TODO(b/169866723): Use InputEventAssigner
+        frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID] = newestInputEventTime;
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 37bea58..38a5937 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -77,7 +77,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Queue;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -7463,92 +7463,73 @@
     }
 
     /**
-     * Offsets the given rectangle in parent's local coordinates into child's coordinate space
-     * and clips the result to the child View's bounds, padding and clipRect if appropriate. If the
-     * resulting rectangle is not empty, the request is forwarded to the child.
-     * <p>
-     * Note: This method does not account for any static View transformations which may be
-     * applied to the child view.
-     *
-     * @param child            the child to dispatch to
-     * @param localVisibleRect the visible (clipped) area of this ViewGroup, in local coordinates
-     * @param windowOffset     the offset of localVisibleRect within the window
-     * @param targets          a queue to collect located targets
-     */
-    private void dispatchTransformedScrollCaptureSearch(View child, Rect localVisibleRect,
-            Point windowOffset, Queue<ScrollCaptureTarget> targets) {
-
-        // copy local visible rect for modification and dispatch
-        final Rect childVisibleRect = getTempRect();
-        childVisibleRect.set(localVisibleRect);
-
-        // transform to child coords
-        final Point childWindowOffset = getTempPoint();
-        childWindowOffset.set(windowOffset.x, windowOffset.y);
-
-        final int dx = child.mLeft - mScrollX;
-        final int dy = child.mTop - mScrollY;
-
-        childVisibleRect.offset(-dx, -dy);
-        childWindowOffset.offset(dx, dy);
-
-        boolean rectIsVisible = true;
-        final int width = mRight - mLeft;
-        final int height = mBottom - mTop;
-
-        // Clip to child bounds
-        if (getClipChildren()) {
-            rectIsVisible = childVisibleRect.intersect(0, 0, child.getWidth(), child.getHeight());
-        }
-
-        // Clip to child padding.
-        if (rectIsVisible && (child instanceof ViewGroup)
-                && ((ViewGroup) child).getClipToPadding()) {
-            rectIsVisible = childVisibleRect.intersect(
-                    child.mPaddingLeft, child.mPaddingTop,
-                    child.getWidth() - child.mPaddingRight,
-                    child.getHeight() - child.mPaddingBottom);
-        }
-        // Clip to child clipBounds.
-        if (rectIsVisible && child.mClipBounds != null) {
-            rectIsVisible = childVisibleRect.intersect(child.mClipBounds);
-        }
-        if (rectIsVisible) {
-            child.dispatchScrollCaptureSearch(childVisibleRect, childWindowOffset, targets);
-        }
-    }
-
-    /**
      * Handle the scroll capture search request by checking this view if applicable, then to each
      * child view.
      *
      * @param localVisibleRect the visible area of this ViewGroup in local coordinates, according to
      *                         the parent
      * @param windowOffset     the offset of this view within the window
-     * @param targets          the collected list of scroll capture targets
-     *
-     * @hide
+     * @param targets          accepts potential scroll capture targets; {@link Consumer#accept
+     *                         results.accept} may be called zero or more times on the calling
+     *                         thread before onScrollCaptureSearch returns
      */
     @Override
     public void dispatchScrollCaptureSearch(
             @NonNull Rect localVisibleRect, @NonNull Point windowOffset,
-            @NonNull Queue<ScrollCaptureTarget> targets) {
+            @NonNull Consumer<ScrollCaptureTarget> targets) {
+
+        // copy local visible rect for modification and dispatch
+        final Rect rect = getTempRect();
+        rect.set(localVisibleRect);
+
+        if (getClipToPadding()) {
+            rect.inset(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
+        }
 
         // Dispatch to self first.
         super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets);
 
-        // Then dispatch to children, if not excluding descendants.
-        if ((getScrollCaptureHint() & SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) == 0) {
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                View child = getChildAt(i);
-                // Only visible views can be captured.
-                if (child.getVisibility() != View.VISIBLE) {
-                    continue;
-                }
-                // Transform to child coords and dispatch
-                dispatchTransformedScrollCaptureSearch(child, localVisibleRect, windowOffset,
-                        targets);
+        // Skip children if descendants excluded.
+        if ((getScrollCaptureHint() & SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) != 0) {
+            return;
+        }
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            // Only visible views can be captured.
+            if (child.getVisibility() != View.VISIBLE) {
+                continue;
+            }
+            // Offset the given rectangle (in parent's local coordinates) into child's coordinate
+            // space and clip the result to the child View's bounds, padding and clipRect as needed.
+            // If the resulting rectangle is not empty, the request is forwarded to the child.
+
+            // copy local visible rect for modification and dispatch
+            final Rect childVisibleRect = getTempRect();
+            childVisibleRect.set(localVisibleRect);
+
+            // transform to child coords
+            final Point childWindowOffset = getTempPoint();
+            childWindowOffset.set(windowOffset.x, windowOffset.y);
+
+            final int dx = child.mLeft - mScrollX;
+            final int dy = child.mTop - mScrollY;
+
+            childVisibleRect.offset(-dx, -dy);
+            childWindowOffset.offset(dx, dy);
+
+            boolean rectIsVisible = true;
+
+            // Clip to child bounds
+            if (getClipChildren()) {
+                rectIsVisible = childVisibleRect.intersect(0, 0, child.getWidth(),
+                        child.getHeight());
+            }
+
+            // Clip to child padding.
+            if (rectIsVisible) {
+                child.dispatchScrollCaptureSearch(childVisibleRect, childWindowOffset, targets);
             }
         }
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 144691d..f8e65bd 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -141,6 +141,7 @@
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.LongArray;
 import android.util.MergedConfiguration;
@@ -202,6 +203,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -274,6 +276,11 @@
      */
     private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2;
 
+    /**
+     * Maximum time to wait for {@link View#dispatchScrollCaptureSearch} to complete.
+     */
+    private static final int SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS = 2500;
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
 
@@ -323,6 +330,8 @@
     private boolean mForceDisableBLAST;
     private boolean mEnableTripleBuffering;
 
+    private boolean mFastScrollSoundEffectsEnabled;
+
     /**
      * Signals that compatibility booleans have been initialized according to
      * target SDK versions.
@@ -666,8 +675,6 @@
     private final InsetsController mInsetsController;
     private final ImeFocusController mImeFocusController;
 
-    private ScrollCaptureConnection mScrollCaptureConnection;
-
     private boolean mIsSurfaceOpaque;
 
     private final BackgroundBlurDrawable.Aggregator mBlurRegionAggregator =
@@ -681,12 +688,6 @@
         return mImeFocusController;
     }
 
-    /** @return The current {@link ScrollCaptureConnection} for this instance, if any is active. */
-    @Nullable
-    public ScrollCaptureConnection getScrollCaptureConnection() {
-        return mScrollCaptureConnection;
-    }
-
     private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
 
     private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
@@ -728,6 +729,8 @@
 
     private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
 
+    private long mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
+
     /**
      * Increment this value when the surface has been replaced.
      */
@@ -813,6 +816,10 @@
 
         loadSystemProperties();
         mImeFocusController = new ImeFocusController(this);
+        AudioManager audioManager = mContext.getSystemService(AudioManager.class);
+        mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled();
+
+        mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
     }
 
     public static void addFirstDrawHandler(Runnable callback) {
@@ -3962,11 +3969,12 @@
     }
 
     private void addFrameCallbackIfNeeded() {
-        boolean nextDrawUseBlastSync = mNextDrawUseBlastSync;
-        boolean hasBlur = mBlurRegionAggregator.hasRegions();
-        boolean reportNextDraw = mReportNextDraw;
+        final boolean nextDrawUseBlastSync = mNextDrawUseBlastSync;
+        final boolean reportNextDraw = mReportNextDraw;
+        final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates();
+        final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions();
 
-        if (!nextDrawUseBlastSync && !reportNextDraw && !hasBlur) {
+        if (!nextDrawUseBlastSync && !reportNextDraw && !needsCallbackForBlur) {
             return;
         }
 
@@ -3974,18 +3982,22 @@
             Log.d(mTag, "Creating frameDrawingCallback"
                     + " nextDrawUseBlastSync=" + nextDrawUseBlastSync
                     + " reportNextDraw=" + reportNextDraw
-                    + " hasBlur=" + hasBlur);
+                    + " hasBlurUpdates=" + hasBlurUpdates);
         }
 
-        // The callback will run on a worker thread pool from the render thread.
+        final BackgroundBlurDrawable.BlurRegion[] blurRegionsForFrame =
+                needsCallbackForBlur ?  mBlurRegionAggregator.getBlurRegionsCopyForRT() : null;
+
+        // The callback will run on the render thread.
         HardwareRenderer.FrameDrawingCallback frameDrawingCallback = frame -> {
             if (DEBUG_BLAST) {
                 Log.d(mTag, "Received frameDrawingCallback frameNum=" + frame + "."
                         + " Creating transactionCompleteCallback=" + nextDrawUseBlastSync);
             }
 
-            if (hasBlur) {
-                mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame);
+            if (needsCallbackForBlur) {
+                mBlurRegionAggregator
+                    .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates);
             }
 
             if (mBlastBufferQueue == null) {
@@ -6081,8 +6093,10 @@
                                     v, mTempRect);
                         }
                         if (v.requestFocus(direction, mTempRect)) {
-                            playSoundEffect(SoundEffectConstants
-                                    .getContantForFocusDirection(direction));
+                            boolean isFastScrolling = event.getRepeatCount() > 0;
+                            playSoundEffect(
+                                    SoundEffectConstants.getConstantForFocusDirection(direction,
+                                            isFastScrolling));
                             return true;
                         }
                     }
@@ -7743,20 +7757,31 @@
         try {
             final AudioManager audioManager = getAudioManager();
 
+            if (mFastScrollSoundEffectsEnabled
+                    && SoundEffectConstants.isNavigationRepeat(effectId)) {
+                audioManager.playSoundEffect(
+                        SoundEffectConstants.nextNavigationRepeatSoundEffectId());
+                return;
+            }
+
             switch (effectId) {
                 case SoundEffectConstants.CLICK:
                     audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
                     return;
                 case SoundEffectConstants.NAVIGATION_DOWN:
+                case SoundEffectConstants.NAVIGATION_REPEAT_DOWN:
                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);
                     return;
                 case SoundEffectConstants.NAVIGATION_LEFT:
+                case SoundEffectConstants.NAVIGATION_REPEAT_LEFT:
                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);
                     return;
                 case SoundEffectConstants.NAVIGATION_RIGHT:
+                case SoundEffectConstants.NAVIGATION_REPEAT_RIGHT:
                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);
                     return;
                 case SoundEffectConstants.NAVIGATION_UP:
+                case SoundEffectConstants.NAVIGATION_REPEAT_UP:
                     audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);
                     return;
                 default:
@@ -9206,9 +9231,9 @@
      * Collect and include any ScrollCaptureCallback instances registered with the window.
      *
      * @see #addScrollCaptureCallback(ScrollCaptureCallback)
-     * @param targets the search queue for targets
+     * @param results an object to collect the results of the search
      */
-    private void collectRootScrollCaptureTargets(Queue<ScrollCaptureTarget> targets) {
+    private void collectRootScrollCaptureTargets(ScrollCaptureSearchResults results) {
         if (mRootScrollCaptureCallbacks == null) {
             return;
         }
@@ -9216,26 +9241,45 @@
             // Add to the list for consideration
             Point offset = new Point(mView.getLeft(), mView.getTop());
             Rect rect = new Rect(0, 0, mView.getWidth(), mView.getHeight());
-            targets.add(new ScrollCaptureTarget(mView, rect, offset, cb));
+            results.addTarget(new ScrollCaptureTarget(mView, rect, offset, cb));
         }
     }
 
     /**
-     * Handles an inbound request for scroll capture from the system. If a client is not already
-     * active, a search will be dispatched through the view tree to locate scrolling content.
+     * Update the timeout for scroll capture requests. Only affects this view root.
+     * The default value is {@link #SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS}.
+     *
+     * @param timeMillis the new timeout in milliseconds
+     */
+    public void setScrollCaptureRequestTimeout(int timeMillis) {
+        mScrollCaptureRequestTimeout = timeMillis;
+    }
+
+    /**
+     * Get the current timeout for scroll capture requests.
+     *
+     * @return the timeout in milliseconds
+     */
+    public long getScrollCaptureRequestTimeout() {
+        return mScrollCaptureRequestTimeout;
+    }
+
+    /**
+     * Handles an inbound request for scroll capture from the system. A search will be
+     * dispatched through the view tree to locate scrolling content.
      * <p>
-     * Either {@link IScrollCaptureCallbacks#onConnected(IScrollCaptureConnection, Rect,
-     * Point)} or {@link IScrollCaptureCallbacks#onUnavailable()} will be returned
-     * depending on the results of the search.
+     * A call to {@link IScrollCaptureCallbacks#onScrollCaptureResponse(ScrollCaptureResponse)}
+     * will follow.
      *
      * @param callbacks to receive responses
-     * @see ScrollCaptureTargetResolver
+     * @see ScrollCaptureTargetSelector
      */
     public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
-        LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        ScrollCaptureSearchResults results =
+                new ScrollCaptureSearchResults(mContext.getMainExecutor());
 
         // Window (root) level callbacks
-        collectRootScrollCaptureTargets(targetList);
+        collectRootScrollCaptureTargets(results);
 
         // Search through View-tree
         View rootView = getView();
@@ -9243,58 +9287,70 @@
             Point point = new Point();
             Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight());
             getChildVisibleRect(rootView, rect, point);
-            rootView.dispatchScrollCaptureSearch(rect, point, targetList);
+            rootView.dispatchScrollCaptureSearch(rect, point, results::addTarget);
         }
-
-        // No-op path. Scroll capture not offered for this window.
-        if (targetList.isEmpty()) {
-            dispatchScrollCaptureSearchResult(callbacks, null);
-            return;
+        Runnable onComplete = () -> dispatchScrollCaptureSearchResult(callbacks, results);
+        results.setOnCompleteListener(onComplete);
+        if (!results.isComplete()) {
+            mHandler.postDelayed(results::finish, getScrollCaptureRequestTimeout());
         }
-
-        // Request scrollBounds from each of the targets.
-        // Continues with the consumer once all responses are consumed, or the timeout expires.
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetList);
-        resolver.start(mHandler, 1000,
-                (selected) -> dispatchScrollCaptureSearchResult(callbacks, selected));
     }
 
     /** Called by {@link #handleScrollCaptureRequest} when a result is returned */
     private void dispatchScrollCaptureSearchResult(
             @NonNull IScrollCaptureCallbacks callbacks,
-            @Nullable ScrollCaptureTarget selectedTarget) {
+            @NonNull ScrollCaptureSearchResults results) {
 
-        // If timeout or no eligible targets found.
+        ScrollCaptureTarget selectedTarget = results.getTopResult();
+
+        ScrollCaptureResponse.Builder response = new ScrollCaptureResponse.Builder();
+        response.setWindowTitle(getTitle().toString());
+
+        StringWriter writer =  new StringWriter();
+        IndentingPrintWriter pw = new IndentingPrintWriter(writer);
+        results.dump(pw);
+        pw.flush();
+        response.addMessage(writer.toString());
+
         if (selectedTarget == null) {
+            response.setDescription("No scrollable targets found in window");
             try {
-                if (DEBUG_SCROLL_CAPTURE) {
-                    Log.d(TAG, "scrollCaptureSearch returned no targets available.");
-                }
-                callbacks.onUnavailable();
+                callbacks.onScrollCaptureResponse(response.build());
             } catch (RemoteException e) {
-                if (DEBUG_SCROLL_CAPTURE) {
-                    Log.w(TAG, "Failed to send scroll capture search result.", e);
-                }
+                Log.e(TAG, "Failed to send scroll capture search result", e);
             }
             return;
         }
 
-        // Create a client instance and return it to the caller
-        mScrollCaptureConnection = new ScrollCaptureConnection(selectedTarget, callbacks);
+        response.setDescription("Connected");
+
+        // Compute area covered by scrolling content within window
+        Rect boundsInWindow = new Rect();
+        View containingView = selectedTarget.getContainingView();
+        containingView.getLocationInWindow(mAttachInfo.mTmpLocation);
+        boundsInWindow.set(selectedTarget.getScrollBounds());
+        boundsInWindow.offset(mAttachInfo.mTmpLocation[0], mAttachInfo.mTmpLocation[1]);
+        response.setBoundsInWindow(boundsInWindow);
+
+        // Compute the area on screen covered by the window
+        Rect boundsOnScreen = new Rect();
+        mView.getLocationOnScreen(mAttachInfo.mTmpLocation);
+        boundsOnScreen.set(0, 0, mView.getWidth(), mView.getHeight());
+        boundsOnScreen.offset(mAttachInfo.mTmpLocation[0], mAttachInfo.mTmpLocation[1]);
+        response.setWindowBounds(boundsOnScreen);
+
+        // Create a connection and return it to the caller
+        ScrollCaptureConnection connection = new ScrollCaptureConnection(
+                mView.getContext().getMainExecutor(), selectedTarget, callbacks);
+        response.setConnection(connection);
+
         try {
-            if (DEBUG_SCROLL_CAPTURE) {
-                Log.d(TAG, "scrollCaptureSearch returning client: " + getScrollCaptureConnection());
-            }
-            callbacks.onConnected(
-                    mScrollCaptureConnection,
-                    selectedTarget.getScrollBounds(),
-                    selectedTarget.getPositionInWindow());
+            callbacks.onScrollCaptureResponse(response.build());
         } catch (RemoteException e) {
             if (DEBUG_SCROLL_CAPTURE) {
-                Log.w(TAG, "Failed to send scroll capture search result.", e);
+                Log.w(TAG, "Failed to send scroll capture search response.", e);
             }
-            mScrollCaptureConnection.disconnect();
-            mScrollCaptureConnection = null;
+            connection.close();
         }
     }
 
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 4ecdd78..221b334 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2621,7 +2621,6 @@
      * callback with the root view of the window.
      *
      * @param callback the callback to add
-     * @hide
      */
     public void registerScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
     }
@@ -2630,7 +2629,6 @@
      * Unregisters a {@link ScrollCaptureCallback} previously registered with this window.
      *
      * @param callback the callback to remove
-     * @hide
      */
     public void unregisterScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
     }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 05b177e..39c09b4 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -6612,6 +6612,27 @@
     }
 
     /**
+     * Returns the {@link EdgeEffect#getType()} for the edge effects.
+     * @return the {@link EdgeEffect#getType()} for the edge effects.
+     * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+     */
+    @EdgeEffect.EdgeEffectType
+    public int getEdgeEffectType() {
+        return mEdgeGlowTop.getType();
+    }
+
+    /**
+     * Sets the {@link EdgeEffect#setType(int)} for the edge effects.
+     * @param type The edge effect type to use for the edge effects.
+     * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+     */
+    public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
+        mEdgeGlowTop.setType(type);
+        mEdgeGlowBottom.setType(type);
+        invalidate();
+    }
+
+    /**
      * Sets the recycler listener to be notified whenever a View is set aside in
      * the recycler for later reuse. This listener can be used to free resources
      * associated to the View.
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 6dedd12..23915e0 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -303,6 +303,27 @@
     }
 
     /**
+     * Returns the {@link EdgeEffect#getType()} for the edge effects.
+     * @return the {@link EdgeEffect#getType()} for the edge effects.
+     * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+     */
+    @EdgeEffect.EdgeEffectType
+    public int getEdgeEffectType() {
+        return mEdgeGlowLeft.getType();
+    }
+
+    /**
+     * Sets the {@link EdgeEffect#setType(int)} for the edge effects.
+     * @param type The edge effect type to use for the edge effects.
+     * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+     */
+    public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
+        mEdgeGlowRight.setType(type);
+        mEdgeGlowLeft.setType(type);
+        invalidate();
+    }
+
+    /**
      * @return The maximum amount this scroll view will scroll in response to
      *   an arrow event.
      */
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5144717..e0b4ec7 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -46,6 +46,8 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.content.res.loader.ResourcesLoader;
+import android.content.res.loader.ResourcesProvider;
 import android.graphics.Bitmap;
 import android.graphics.BlendMode;
 import android.graphics.Outline;
@@ -62,16 +64,19 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.StrictMode;
 import android.os.UserHandle;
+import android.system.Os;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
+import android.util.SparseIntArray;
 import android.util.TypedValue;
 import android.util.TypedValue.ComplexDimensionUnit;
 import android.view.ContextThemeWrapper;
@@ -92,6 +97,12 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.internal.util.Preconditions;
 
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -520,8 +531,8 @@
      *  SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
      */
     private abstract static class Action implements Parcelable {
-        public abstract void apply(View root, ViewGroup rootParent,
-                InteractionHandler handler) throws ActionException;
+        public abstract void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) throws ActionException;
 
         public static final int MERGE_REPLACE = 0;
         public static final int MERGE_APPEND = 1;
@@ -551,8 +562,8 @@
          * and return the final action which will run on the UI thread.
          * Override this if some of the tasks can be performed async.
          */
-        public Action initActionAsync(
-                ViewTree root, ViewGroup rootParent, InteractionHandler handler) {
+        public Action initActionAsync(ViewTree root, ViewGroup rootParent,
+                InteractionHandler handler, ColorResources colorResources) {
             return this;
         }
 
@@ -595,7 +606,9 @@
     // Constant used during async execution. It is not parcelable.
     private static final Action ACTION_NOOP = new RuntimeAction() {
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { }
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
+        }
     };
 
     /**
@@ -713,7 +726,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View view = root.findViewById(viewId);
             if (!(view instanceof AdapterView<?>)) return;
 
@@ -748,7 +762,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -820,7 +835,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -843,7 +859,8 @@
                 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
                     ((RemoteViewsListAdapter) a).setViewsList(list);
                 } else {
-                    v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
+                    v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
+                            colorResources));
                 }
             } else if (target instanceof AdapterViewAnimator) {
                 AdapterViewAnimator v = (AdapterViewAnimator) target;
@@ -851,7 +868,8 @@
                 if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
                     ((RemoteViewsListAdapter) a).setViewsList(list);
                 } else {
-                    v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
+                    v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
+                            colorResources));
                 }
             }
         }
@@ -882,7 +900,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -919,7 +938,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler) {
+                InteractionHandler handler, ColorResources colorResources) {
             SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
             copy.isAsync = true;
             return copy;
@@ -958,7 +977,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1037,7 +1057,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
             if (!(target instanceof CompoundButton)) {
@@ -1240,7 +1261,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1297,7 +1319,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -1334,7 +1357,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View view = root.findViewById(viewId);
             if (view == null) return;
 
@@ -1437,12 +1461,12 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent,
-                InteractionHandler handler) throws ActionException {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) throws ActionException {
             ReflectionAction ra = new ReflectionAction(viewId, methodName,
                     BaseReflectionAction.BITMAP,
                     bitmap);
-            ra.apply(root, rootParent, handler);
+            ra.apply(root, rootParent, handler, colorResources);
         }
 
         @Override
@@ -1518,7 +1542,8 @@
         protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
 
         @Override
-        public final void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public final void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View view = root.findViewById(viewId);
             if (view == null) return;
 
@@ -1536,7 +1561,7 @@
 
         @Override
         public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler) {
+                InteractionHandler handler, ColorResources colorResources) {
             final View view = root.findViewById(viewId);
             if (view == null) return ACTION_NOOP;
 
@@ -1938,7 +1963,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             mRunnable.run();
         }
     }
@@ -1993,7 +2019,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final Context context = root.getContext();
             final ViewGroup target = root.findViewById(viewId);
 
@@ -2002,12 +2029,14 @@
             }
 
             // Inflate nested views and add as children
-            target.addView(mNestedViews.apply(context, target, handler), mIndex);
+            target.addView(
+                    mNestedViews.apply(context, target, handler, null /* size */, colorResources),
+                    mIndex);
         }
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler) {
+                InteractionHandler handler, ColorResources colorResources) {
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the current view.
             root.createTree();
@@ -2019,8 +2048,8 @@
 
             // Inflate nested views and perform all the async tasks for the child remoteView.
             final Context context = root.mRoot.getContext();
-            final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(
-                    context, targetVg, null, handler);
+            final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(context, targetVg,
+                    null /* listener */, handler, null /* size */, colorResources);
             final ViewTree tree = task.doInBackground();
 
             if (tree == null) {
@@ -2033,8 +2062,8 @@
 
             return new RuntimeAction() {
                 @Override
-                public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
-                        throws ActionException {
+                public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                        ColorResources colorResources) throws ActionException {
                     task.onPostExecute(tree);
                     targetVg.addView(task.mResult, mIndex);
                 }
@@ -2095,7 +2124,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final ViewGroup target = root.findViewById(viewId);
 
             if (target == null) {
@@ -2112,7 +2142,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler) {
+                InteractionHandler handler, ColorResources colorResources) {
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the current view.
             root.createTree();
@@ -2136,8 +2166,8 @@
             }
             return new RuntimeAction() {
                 @Override
-                public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
-                        throws ActionException {
+                public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                        ColorResources colorResources) throws ActionException {
                     if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
                         targetVg.removeAllViews();
                         return;
@@ -2192,7 +2222,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(viewId);
 
             if (target == null || target == root) {
@@ -2207,7 +2238,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler) {
+                InteractionHandler handler, ColorResources colorResources) {
             // In the async implementation, update the view tree so that subsequent calls to
             // findViewById return the correct view.
             root.createTree();
@@ -2226,8 +2257,8 @@
             parent.mChildren.remove(target);
             return new RuntimeAction() {
                 @Override
-                public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
-                        throws ActionException {
+                public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                        ColorResources colorResources) throws ActionException {
                     parentVg.removeView(target.mRoot);
                 }
             };
@@ -2306,7 +2337,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final TextView target = root.findViewById(viewId);
             if (target == null) return;
             if (drawablesLoaded) {
@@ -2337,7 +2369,7 @@
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
-                InteractionHandler handler) {
+                InteractionHandler handler, ColorResources colorResources) {
             final TextView target = root.findViewById(viewId);
             if (target == null) return ACTION_NOOP;
 
@@ -2415,7 +2447,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final TextView target = root.findViewById(viewId);
             if (target == null) return;
             target.setTextSize(units, size);
@@ -2460,7 +2493,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
             target.setPadding(left, top, right, bottom);
@@ -2533,7 +2567,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(viewId);
             if (target == null) {
                 return;
@@ -2646,7 +2681,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -2681,7 +2717,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             // Let's traverse the viewtree and override all textColors!
             Stack<View> viewsToProcess = new Stack<>();
             viewsToProcess.add(root);
@@ -2731,7 +2768,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) {
             final View target = root.findViewById(mViewId);
             if (target == null) return;
 
@@ -2765,7 +2803,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources)
                 throws ActionException {
             final View target = root.findViewById(viewId);
             if (target == null) return;
@@ -2816,8 +2855,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
-                throws ActionException {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) throws ActionException {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -2893,8 +2932,8 @@
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
-                throws ActionException {
+        public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
+                ColorResources colorResources) throws ActionException {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -4662,7 +4701,7 @@
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
         View result = inflateView(context, rvToApply, parent);
-        rvToApply.performApply(result, parent, handler);
+        rvToApply.performApply(result, parent, handler, null);
         return result;
     }
 
@@ -4674,27 +4713,39 @@
 
     /** @hide */
     public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
-            @Nullable InteractionHandler handler,
-            @StyleRes int applyThemeResId,
+            @Nullable InteractionHandler handler, @StyleRes int applyThemeResId,
             @Nullable PointF size) {
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
-        View result = inflateView(context, rvToApply, parent, applyThemeResId);
-        rvToApply.performApply(result, parent, handler);
+        View result = inflateView(context, rvToApply, parent, applyThemeResId, null);
+        rvToApply.performApply(result, parent, handler, null);
+        return result;
+    }
+
+    /** @hide */
+    public View apply(Context context, ViewGroup parent, InteractionHandler handler,
+            @NonNull PointF size, @Nullable ColorResources colorResources) {
+        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
+
+        View result = inflateView(context, rvToApply, parent, 0, colorResources);
+        rvToApply.performApply(result, parent, handler, colorResources);
         return result;
     }
 
     private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
-        return inflateView(context, rv, parent, 0);
+        return inflateView(context, rv, parent, 0, null);
     }
 
     private View inflateView(Context context, RemoteViews rv, ViewGroup parent,
-            @StyleRes int applyThemeResId) {
+            @StyleRes int applyThemeResId, @Nullable ColorResources colorResources) {
         // RemoteViews may be built by an application installed in another
         // user. So build a context that loads resources from that user but
         // still returns the current users userId so settings like data / time formats
         // are loaded without requiring cross user persmissions.
         final Context contextForResources = getContextForResources(context);
+        if (colorResources != null) {
+            colorResources.apply(contextForResources);
+        }
         Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
 
         // If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
@@ -4756,34 +4807,37 @@
      */
     public CancellationSignal applyAsync(
             Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
-        return applyAsync(context, parent, executor, listener, null);
+        return applyAsync(context, parent, executor, listener, null /* handler */);
     }
 
 
     /** @hide */
     public CancellationSignal applyAsync(Context context, ViewGroup parent,
             Executor executor, OnViewAppliedListener listener, InteractionHandler handler) {
-        return applyAsync(context, parent, executor, listener, handler, null);
+        return applyAsync(context, parent, executor, listener, handler, null /* size */);
     }
 
     /** @hide */
     public CancellationSignal applyAsync(Context context, ViewGroup parent,
             Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
             PointF size) {
-        return getAsyncApplyTask(context, parent, listener, handler, size).startTaskOnExecutor(
-                executor);
+        return getAsyncApplyTask(context, parent, listener, handler, size, null /* themeColors */)
+                .startTaskOnExecutor(executor);
+    }
+
+    /** @hide */
+    public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
+            OnViewAppliedListener listener, InteractionHandler handler, PointF size,
+            ColorResources colorResources) {
+        return getAsyncApplyTask(context, parent, listener, handler, size, colorResources)
+                .startTaskOnExecutor(executor);
     }
 
     private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
-            OnViewAppliedListener listener, InteractionHandler handler) {
-        return getAsyncApplyTask(context, parent, listener, handler, null);
-    }
-
-    private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
-            OnViewAppliedListener listener, InteractionHandler handler, PointF size) {
-        return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context,
-                listener,
-                handler, null);
+            OnViewAppliedListener listener, InteractionHandler handler, PointF size,
+            ColorResources colorResources) {
+        return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
+                handler, colorResources, null /* result */);
     }
 
     private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
@@ -4794,6 +4848,7 @@
         final Context mContext;
         final OnViewAppliedListener mListener;
         final InteractionHandler mHandler;
+        final ColorResources mColorResources;
 
         private View mResult;
         private ViewTree mTree;
@@ -4802,11 +4857,12 @@
 
         private AsyncApplyTask(
                 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
-                InteractionHandler handler, View result) {
+                InteractionHandler handler, ColorResources colorResources, View result) {
             mRV = rv;
             mParent = parent;
             mContext = context;
             mListener = listener;
+            mColorResources = colorResources;
             mHandler = handler;
 
             mResult = result;
@@ -4816,7 +4872,7 @@
         protected ViewTree doInBackground(Void... params) {
             try {
                 if (mResult == null) {
-                    mResult = inflateView(mContext, mRV, mParent);
+                    mResult = inflateView(mContext, mRV, mParent, 0, mColorResources);
                 }
 
                 mTree = new ViewTree(mResult);
@@ -4825,7 +4881,8 @@
                     mActions = new Action[count];
                     for (int i = 0; i < count && !isCancelled(); i++) {
                         // TODO: check if isCancelled in nested views.
-                        mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler);
+                        mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler,
+                                mColorResources);
                     }
                 } else {
                     mActions = null;
@@ -4850,7 +4907,7 @@
                         InteractionHandler handler = mHandler == null
                                 ? DEFAULT_INTERACTION_HANDLER : mHandler;
                         for (Action a : mActions) {
-                            a.apply(viewTree.mRoot, mParent, handler);
+                            a.apply(viewTree.mRoot, mParent, handler, mColorResources);
                         }
                     }
                 } catch (Exception e) {
@@ -4894,16 +4951,17 @@
      * the {@link #apply(Context,ViewGroup)} call.
      */
     public void reapply(Context context, View v) {
-        reapply(context, v, null, null);
+        reapply(context, v, null /* handler */);
     }
 
     /** @hide */
     public void reapply(Context context, View v, InteractionHandler handler) {
-        reapply(context, v, handler, null);
+        reapply(context, v, handler, null /* size */, null /* colorResources */);
     }
 
     /** @hide */
-    public void reapply(Context context, View v, InteractionHandler handler, PointF size) {
+    public void reapply(Context context, View v, InteractionHandler handler, PointF size,
+            ColorResources colorResources) {
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
         // In the case that a view has this RemoteViews applied in one orientation or size, is
@@ -4917,7 +4975,7 @@
             }
         }
 
-        rvToApply.performApply(v, (ViewGroup) v.getParent(), handler);
+        rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
     }
 
     /**
@@ -4933,20 +4991,21 @@
      * @return CancellationSignal
      * @hide
      */
-    public CancellationSignal reapplyAsync(
-            Context context, View v, Executor executor, OnViewAppliedListener listener) {
-        return reapplyAsync(context, v, executor, listener, null, null);
+    public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
+            OnViewAppliedListener listener) {
+        return reapplyAsync(context, v, executor, listener, null);
     }
 
     /** @hide */
     public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
             OnViewAppliedListener listener, InteractionHandler handler) {
-        return reapplyAsync(context, v, executor, listener, handler, null);
+        return reapplyAsync(context, v, executor, listener, handler, null, null);
     }
 
     /** @hide */
     public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
-            OnViewAppliedListener listener, InteractionHandler handler, PointF size) {
+            OnViewAppliedListener listener, InteractionHandler handler, PointF size,
+            ColorResources colorResources) {
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
         // In the case that a view has this RemoteViews applied in one orientation, is persisted
@@ -4960,16 +5019,18 @@
         }
 
         return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
-                context, listener, handler, v).startTaskOnExecutor(executor);
+                context, listener, handler, colorResources, v).startTaskOnExecutor(
+                executor);
     }
 
-    private void performApply(View v, ViewGroup parent, InteractionHandler handler) {
+    private void performApply(View v, ViewGroup parent, InteractionHandler handler,
+            ColorResources colorResources) {
         if (mActions != null) {
             handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler;
             final int count = mActions.size();
             for (int i = 0; i < count; i++) {
                 Action a = mActions.get(i);
-                a.apply(v, parent, handler);
+                a.apply(v, parent, handler, colorResources);
             }
         }
     }
@@ -5010,6 +5071,122 @@
     }
 
     /**
+     * Object allowing the modification of a context to overload the system's dynamic colors.
+     *
+     * Only colors from {@link android.R.color#system_primary_0} to
+     * {@link android.R.color#system_neutral_1000} can be overloaded.
+     * @hide
+     */
+    public static final class ColorResources {
+        // Set of valid colors resources.
+        private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_primary_0;
+        private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_neutral_1000;
+        // Size, in bytes, of an entry in the array of colors in an ARSC file.
+        private static final int ARSC_ENTRY_SIZE = 16;
+
+        private ResourcesLoader mLoader;
+
+        private ColorResources(ResourcesLoader loader) {
+            mLoader = loader;
+        }
+
+        /**
+         * Apply the color resources to the given context.
+         *
+         * No resource resolution must have be done on the context given to that method.
+         */
+        public void apply(Context context) {
+            context.getResources().addLoaders(mLoader);
+        }
+
+        private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException {
+            ByteArrayOutputStream content = new ByteArrayOutputStream(2048);
+            byte[] buffer = new byte[4096];
+            while (input.available() > 0) {
+                int read = input.read(buffer);
+                content.write(buffer, 0, read);
+            }
+            return content;
+        }
+
+        /**
+         * Creates the compiled resources content from the asset stored in the APK.
+         *
+         * The asset is a compiled resource with the correct resources name and correct ids, only
+         * the values are incorrect. The last value is at the very end of the file. The resources
+         * are in an array, the array's entries are 16 bytes each. We use this to work out the
+         * location of all the positions of the various resources.
+         */
+        private static byte[] createCompiledResourcesContent(Context context,
+                SparseIntArray colorResources) throws IOException {
+            byte[] content;
+            try (InputStream input = context.getResources().openRawResource(
+                    com.android.internal.R.raw.remote_views_color_resources)) {
+                ByteArrayOutputStream rawContent = readFileContent(input);
+                content = rawContent.toByteArray();
+            }
+            int valuesOffset =
+                    content.length - (LAST_RESOURCE_COLOR_ID & 0xffff) * ARSC_ENTRY_SIZE - 4;
+            if (valuesOffset < 0) {
+                Log.e(LOG_TAG, "ARSC file for theme colors is invalid.");
+                return null;
+            }
+            for (int colorRes = FIRST_RESOURCE_COLOR_ID; colorRes <= LAST_RESOURCE_COLOR_ID;
+                    colorRes++) {
+                // The last 2 bytes are the index in the color array.
+                int index = colorRes & 0xffff;
+                int offset = valuesOffset + index * ARSC_ENTRY_SIZE;
+                int value = colorResources.get(colorRes, context.getColor(colorRes));
+                // Write the 32 bit integer in little endian
+                for (int b = 0; b < 4; b++) {
+                    content[offset + b] = (byte) (value & 0xff);
+                    value >>= 8;
+                }
+            }
+            return content;
+        }
+
+        /**
+         *  Adds a resource loader for theme colors to the given context.
+         *
+         * @param context Context of the view hosting the widget.
+         * @param colorMapping Mapping of resources to color values.
+         *
+         * @hide
+         */
+        public static ColorResources create(Context context, SparseIntArray colorMapping) {
+            try {
+                byte[] contentBytes = createCompiledResourcesContent(context, colorMapping);
+                if (contentBytes == null) {
+                    return null;
+                }
+                FileDescriptor arscFile = null;
+                try {
+                    arscFile = Os.memfd_create("remote_views_theme_colors.arsc", 0 /* flags */);
+                    // Note: This must not be closed through the OutputStream.
+                    try (OutputStream pipeWriter = new FileOutputStream(arscFile)) {
+                        pipeWriter.write(contentBytes);
+
+                        try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(arscFile)) {
+                            ResourcesLoader colorsLoader = new ResourcesLoader();
+                            colorsLoader.addProvider(ResourcesProvider
+                                    .loadFromTable(pfd, null /* assetsProvider */));
+                            return new ColorResources(colorsLoader);
+                        }
+                    }
+                } finally {
+                    if (arscFile != null) {
+                        Os.close(arscFile);
+                    }
+                }
+            } catch (Exception ex) {
+                Log.e(LOG_TAG, "Failed to setup the context for theme colors", ex);
+            }
+            return null;
+        }
+    }
+
+    /**
      * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
      *
      * @hide
diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java
index b80fe48..827d033 100644
--- a/core/java/android/widget/RemoteViewsListAdapter.java
+++ b/core/java/android/widget/RemoteViewsListAdapter.java
@@ -31,12 +31,14 @@
     private ArrayList<RemoteViews> mRemoteViewsList;
     private ArrayList<Integer> mViewTypes = new ArrayList<Integer>();
     private int mViewTypeCount;
+    private RemoteViews.ColorResources mColorResources;
 
     public RemoteViewsListAdapter(Context context, ArrayList<RemoteViews> remoteViews,
-            int viewTypeCount) {
+            int viewTypeCount, RemoteViews.ColorResources colorResources) {
         mContext = context;
         mRemoteViewsList = remoteViews;
         mViewTypeCount = viewTypeCount;
+        mColorResources = colorResources;
         init();
     }
 
@@ -90,9 +92,10 @@
             if (convertView != null && rv != null &&
                     convertView.getId() == rv.getLayoutId()) {
                 v = convertView;
-                rv.reapply(mContext, v);
+                rv.reapply(mContext, v, null /* handler */, null /* size */, mColorResources);
             } else {
-                v = rv.apply(mContext, parent);
+                v = rv.apply(mContext, parent, null /* handler */, null /* size */,
+                        mColorResources);
             }
             return v;
         } else {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 64d09de..65f3da7 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -335,6 +335,27 @@
     }
 
     /**
+     * Returns the {@link EdgeEffect#getType()} for the edge effects.
+     * @return the {@link EdgeEffect#getType()} for the edge effects.
+     * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+     */
+    @EdgeEffect.EdgeEffectType
+    public int getEdgeEffectType() {
+        return mEdgeGlowTop.getType();
+    }
+
+    /**
+     * Sets the {@link EdgeEffect#setType(int)} for the edge effects.
+     * @param type The edge effect type to use for the edge effects.
+     * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+     */
+    public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
+        mEdgeGlowTop.setType(type);
+        mEdgeGlowBottom.setType(type);
+        invalidate();
+    }
+
+    /**
      * @return The maximum amount this scroll view will scroll in response to
      *   an arrow event.
      */
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index e22a5eb..acf9882 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -58,9 +58,9 @@
 
     /** Needed for AIDL out parameters. */
     public void readFromParcel(Parcel in) {
-        frame.set(Rect.CREATOR.createFromParcel(in));
-        displayFrame.set(Rect.CREATOR.createFromParcel(in));
-        backdropFrame.set(Rect.CREATOR.createFromParcel(in));
+        frame.readFromParcel(in);
+        displayFrame.readFromParcel(in);
+        backdropFrame.readFromParcel(in);
     }
 
     @Override
diff --git a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
index 96dac56..402d7fe 100644
--- a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
+++ b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
@@ -19,6 +19,7 @@
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UiThread;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -31,12 +32,14 @@
 import android.graphics.Rect;
 import android.graphics.RenderNode;
 import android.graphics.drawable.Drawable;
-import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
-import android.view.SurfaceControl;
+import android.util.LongSparseArray;
 import android.view.ViewRootImpl;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state
@@ -52,26 +55,40 @@
     private final Paint mPaint = new Paint();
     private final Path mRectPath = new Path();
     private final float[] mTmpRadii = new float[8];
-    private final SurfaceControl.BlurRegion mBlurRegion = new SurfaceControl.BlurRegion();
 
-    // This will be called from a thread pool.
-    private final RenderNode.PositionUpdateListener mPositionUpdateListener =
+    private boolean mVisible = true;
+
+    // Confined to UiThread. The values are copied into a BlurRegion, which lives on
+    // RenderThread to avoid interference with UiThread updates.
+    private int mBlurRadius;
+    private float mCornerRadiusTL;
+    private float mCornerRadiusTR;
+    private float mCornerRadiusBL;
+    private float mCornerRadiusBR;
+    private float mAlpha = 1;
+
+    // Do not update from UiThread. This holds the latest position for this drawable. It is used
+    // by the Aggregator from RenderThread to get the final position of the blur region sent to SF
+    private final Rect mRect = new Rect();
+    // This is called from a thread pool. The callbacks might come out of order w.r.t. the frame
+    // number, so we send a Runnable holding the actual update to the Aggregator. The Aggregator
+    // can apply the update on RenderThread when processing that same frame.
+    @VisibleForTesting
+    public final RenderNode.PositionUpdateListener mPositionUpdateListener =
             new RenderNode.PositionUpdateListener() {
             @Override
             public void positionChanged(long frameNumber, int left, int top, int right,
                     int bottom) {
-                synchronized (mAggregator) {
-                    mBlurRegion.rect.set(left, top, right, bottom);
-                    mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
-                }
+                mAggregator.onRenderNodePositionChanged(frameNumber, () -> {
+                    mRect.set(left, top, right, bottom);
+                });
             }
 
             @Override
             public void positionLost(long frameNumber) {
-                synchronized (mAggregator) {
-                    mBlurRegion.rect.setEmpty();
-                    mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
-                }
+                mAggregator.onRenderNodePositionChanged(frameNumber, () -> {
+                    mRect.setEmpty();
+                });
             }
         };
 
@@ -79,6 +96,7 @@
         mAggregator = aggregator;
         mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
         mPaint.setColor(Color.TRANSPARENT);
+        mPaint.setAntiAlias(true);
         mRenderNode = new RenderNode("BackgroundBlurDrawable");
         mRenderNode.addPositionUpdateListener(mPositionUpdateListener);
     }
@@ -104,23 +122,30 @@
     public boolean setVisible(boolean visible, boolean restart) {
         boolean changed = super.setVisible(visible, restart);
         if (changed) {
-            mBlurRegion.visible = visible;
+            mVisible = visible;
+            mAggregator.onBlurDrawableUpdated(this);
         }
         return changed;
     }
 
     @Override
     public void setAlpha(int alpha) {
-        mBlurRegion.alpha = alpha / 255f;
-        invalidateSelf();
+        if (mAlpha != alpha / 255f) {
+            mAlpha = alpha / 255f;
+            invalidateSelf();
+            mAggregator.onBlurDrawableUpdated(this);
+        }
     }
 
     /**
      * Blur radius in pixels.
      */
     public void setBlurRadius(int blurRadius) {
-        mBlurRegion.blurRadius = blurRadius;
-        invalidateSelf();
+        if (mBlurRadius != blurRadius) {
+            mBlurRadius = blurRadius;
+            invalidateSelf();
+            mAggregator.onBlurDrawableUpdated(this);
+        }
     }
 
     /**
@@ -139,14 +164,18 @@
      */
     public void setCornerRadius(float cornerRadiusTL, float cornerRadiusTR, float cornerRadiusBL,
             float cornerRadiusBR) {
-        synchronized (mAggregator) {
-            mBlurRegion.cornerRadiusTL = cornerRadiusTL;
-            mBlurRegion.cornerRadiusTR = cornerRadiusTR;
-            mBlurRegion.cornerRadiusBL = cornerRadiusBL;
-            mBlurRegion.cornerRadiusBR = cornerRadiusBR;
+        if (mCornerRadiusTL != cornerRadiusTL
+                || mCornerRadiusTR != cornerRadiusTR
+                || mCornerRadiusBL != cornerRadiusBL
+                || mCornerRadiusBR != cornerRadiusBR) {
+            mCornerRadiusTL = cornerRadiusTL;
+            mCornerRadiusTR = cornerRadiusTR;
+            mCornerRadiusBL = cornerRadiusBL;
+            mCornerRadiusBR = cornerRadiusBR;
+            updatePath();
+            invalidateSelf();
+            mAggregator.onBlurDrawableUpdated(this);
         }
-        updatePath();
-        invalidateSelf();
     }
 
     @Override
@@ -157,12 +186,10 @@
     }
 
     private void updatePath() {
-        synchronized (mAggregator) {
-            mTmpRadii[0] = mTmpRadii[1] = mBlurRegion.cornerRadiusTL;
-            mTmpRadii[2] = mTmpRadii[3] = mBlurRegion.cornerRadiusTR;
-            mTmpRadii[4] = mTmpRadii[5] = mBlurRegion.cornerRadiusBL;
-            mTmpRadii[6] = mTmpRadii[7] = mBlurRegion.cornerRadiusBR;
-        }
+        mTmpRadii[0] = mTmpRadii[1] = mCornerRadiusTL;
+        mTmpRadii[2] = mTmpRadii[3] = mCornerRadiusTR;
+        mTmpRadii[4] = mTmpRadii[5] = mCornerRadiusBL;
+        mTmpRadii[6] = mTmpRadii[7] = mCornerRadiusBR;
         mRectPath.reset();
         if (getAlpha() == 0 || !isVisible()) {
             return;
@@ -182,17 +209,32 @@
         return PixelFormat.TRANSLUCENT;
     }
 
+    @Override
+    public String toString() {
+        return "BackgroundBlurDrawable{"
+            + "blurRadius=" + mBlurRadius
+            + ", corners={" + mCornerRadiusTL
+            + "," + mCornerRadiusTR
+            + "," + mCornerRadiusBL
+            + "," + mCornerRadiusBR
+            + "}, alpha=" + mAlpha
+            + ", visible=" + mVisible
+            + "}";
+    }
+
     /**
      * Responsible for keeping track of all blur regions of a {@link ViewRootImpl} and posting a
      * message when it's time to propagate them.
      */
     public static final class Aggregator {
-
-        private final ArrayMap<BackgroundBlurDrawable, SurfaceControl.BlurRegion> mBlurRegions =
-                new ArrayMap<>();
+        private final Object mRtLock = new Object();
+        // Maintains a list of all *visible* blur drawables. Confined to  UI thread
+        private final ArraySet<BackgroundBlurDrawable> mDrawables = new ArraySet();
+        @GuardedBy("mRtLock")
+        private final LongSparseArray<ArraySet<Runnable>> mFrameRtUpdates = new LongSparseArray();
         private final ViewRootImpl mViewRoot;
-        private float[][] mTmpBlurRegionsArray;
-        private boolean mNeedsUpdate;
+        private BlurRegion[] mTmpBlurRegionsForFrame = new BlurRegion[0];
+        private boolean mHasUiUpdates;
 
         public Aggregator(ViewRootImpl viewRoot) {
             mViewRoot = viewRoot;
@@ -209,60 +251,191 @@
         }
 
         /**
-         * Called from RenderThread only, already locked.
-         * @param drawable
-         * @param blurRegion
+         * Called when a BackgroundBlurDrawable has been updated
          */
-        void onBlurRegionUpdated(BackgroundBlurDrawable drawable,
-                SurfaceControl.BlurRegion blurRegion) {
-            if (blurRegion.rect.isEmpty() || blurRegion.alpha == 0 || blurRegion.blurRadius == 0
-                    || !blurRegion.visible) {
-                mBlurRegions.remove(drawable);
-                mNeedsUpdate = true;
-                if (DEBUG) {
-                    Log.d(TAG, "Remove " + blurRegion);
+        @UiThread
+        void onBlurDrawableUpdated(BackgroundBlurDrawable drawable) {
+            final boolean shouldBeDrawn =
+                    drawable.mAlpha != 0 && drawable.mBlurRadius > 0 && drawable.mVisible;
+            final boolean isDrawn = mDrawables.contains(drawable);
+            if (shouldBeDrawn) {
+                mHasUiUpdates = true;
+                if (!isDrawn) {
+                    mDrawables.add(drawable);
+                    if (DEBUG) {
+                        Log.d(TAG, "Add " + drawable);
+                    }
+                } else {
+                    if (DEBUG) {
+                        Log.d(TAG, "Update " + drawable);
+                    }
                 }
-            } else {
-                mBlurRegions.put(drawable, blurRegion);
-                mNeedsUpdate = true;
+            } else if (!shouldBeDrawn && isDrawn) {
+                mHasUiUpdates = true;
+                mDrawables.remove(drawable);
                 if (DEBUG) {
-                    Log.d(TAG, "Update " + blurRegion);
+                    Log.d(TAG, "Remove " + drawable);
                 }
             }
         }
 
+        // Called from a thread pool
+        void onRenderNodePositionChanged(long frameNumber, Runnable update) {
+            // One of the blur region's position has changed, so we have to send an updated list
+            // of blur regions to SurfaceFlinger for this frame.
+            synchronized (mRtLock) {
+                ArraySet<Runnable> frameRtUpdates = mFrameRtUpdates.get(frameNumber);
+                if (frameRtUpdates == null) {
+                    frameRtUpdates = new ArraySet<>();
+                    mFrameRtUpdates.put(frameNumber, frameRtUpdates);
+                }
+                frameRtUpdates.add(update);
+            }
+        }
+
         /**
-         * If there are any blur regions visible on the screen at the moment.
+         * @return true if there are any updates that need to be sent to SF
          */
+        @UiThread
+        public boolean hasUpdates() {
+            return mHasUiUpdates;
+        }
+
+        /**
+         * @return true if there are any visible blur regions
+         */
+        @UiThread
         public boolean hasRegions() {
-            return mBlurRegions.size() > 0;
+            return mDrawables.size() > 0;
         }
 
         /**
-         * Dispatch blur updates, if there were any.
-         * @param frameNumber Frame where the update should happen.
+         * @return an array of BlurRegions, which are holding a copy of the information in
+         *         all the currently visible BackgroundBlurDrawables
          */
-        public void dispatchBlurTransactionIfNeeded(long frameNumber) {
-            synchronized (this) {
-                if (!mNeedsUpdate) {
-                    return;
+        @UiThread
+        public BlurRegion[] getBlurRegionsCopyForRT() {
+            if (mHasUiUpdates) {
+                mTmpBlurRegionsForFrame = new BlurRegion[mDrawables.size()];
+                for (int i = 0; i < mDrawables.size(); i++) {
+                    mTmpBlurRegionsForFrame[i] = new BlurRegion(mDrawables.valueAt(i));
                 }
-                mNeedsUpdate = false;
-
-                if (mTmpBlurRegionsArray == null
-                        || mTmpBlurRegionsArray.length != mBlurRegions.size()) {
-                    mTmpBlurRegionsArray = new float[mBlurRegions.size()][];
-                }
-                if (DEBUG) {
-                    Log.d(TAG, "onBlurRegionUpdated will dispatch " + mTmpBlurRegionsArray.length
-                            + " regions for frame " + frameNumber);
-                }
-                for (int i = 0; i < mTmpBlurRegionsArray.length; i++) {
-                    mTmpBlurRegionsArray[i] = mBlurRegions.valueAt(i).toFloatArray();
-                }
-
-                mViewRoot.dispatchBlurRegions(mTmpBlurRegionsArray, frameNumber);
+                mHasUiUpdates = false;
             }
+
+            return mTmpBlurRegionsForFrame;
+        }
+
+        /**
+         * Called on RenderThread.
+         *
+         * @return all blur regions if there are any ui or position updates for this frame,
+         *         null otherwise
+         */
+        @VisibleForTesting
+        public float[][] getBlurRegionsToDispatchToSf(long frameNumber,
+                BlurRegion[] blurRegionsForFrame, boolean hasUiUpdatesForFrame) {
+            synchronized (mRtLock) {
+                if (!hasUiUpdatesForFrame && (mFrameRtUpdates.size() == 0
+                            || mFrameRtUpdates.keyAt(0) > frameNumber)) {
+                    return null;
+                }
+
+                // mFrameRtUpdates holds position updates coming from a thread pool span from
+                // RenderThread. At this point, all position updates for frame frameNumber should
+                // have been added to mFrameRtUpdates.
+                // Here, we apply all updates for frames <= frameNumber in case some previous update
+                // has been missed. This also protects mFrameRtUpdates from memory leaks.
+                while (mFrameRtUpdates.size() != 0 && mFrameRtUpdates.keyAt(0) <= frameNumber) {
+                    final ArraySet<Runnable> frameUpdates = mFrameRtUpdates.valueAt(0);
+                    mFrameRtUpdates.removeAt(0);
+                    for (int i = 0; i < frameUpdates.size(); i++) {
+                        frameUpdates.valueAt(i).run();
+                    }
+                }
+            }
+
+            if (DEBUG) {
+                Log.d(TAG, "Dispatching " + blurRegionsForFrame.length + " blur regions:");
+            }
+
+            final float[][] blurRegionsArray = new float[blurRegionsForFrame.length][];
+            for (int i = 0; i < blurRegionsArray.length; i++) {
+                blurRegionsArray[i] = blurRegionsForFrame[i].toFloatArray();
+                if (DEBUG) {
+                    Log.d(TAG, blurRegionsForFrame[i].toString());
+                }
+            }
+            return blurRegionsArray;
+        }
+
+        /**
+         * Called on RenderThread in FrameDrawingCallback.
+         * Dispatch all blur regions if there are any ui or position updates.
+         */
+        public void dispatchBlurTransactionIfNeeded(long frameNumber,
+                BlurRegion[] blurRegionsForFrame, boolean hasUiUpdatesForFrame) {
+            final float[][] blurRegionsArray = getBlurRegionsToDispatchToSf(frameNumber,
+                    blurRegionsForFrame, hasUiUpdatesForFrame);
+            if (blurRegionsArray != null) {
+                mViewRoot.dispatchBlurRegions(blurRegionsArray, frameNumber);
+            }
+        }
+
+    }
+
+    /**
+     * Wrapper for sending blur data to SurfaceFlinger
+     * Confined to RenderThread.
+     */
+    public static final class BlurRegion {
+        public final int blurRadius;
+        public final float cornerRadiusTL;
+        public final float cornerRadiusTR;
+        public final float cornerRadiusBL;
+        public final float cornerRadiusBR;
+        public final float alpha;
+        public final Rect rect;
+
+        BlurRegion(BackgroundBlurDrawable drawable) {
+            alpha = drawable.mAlpha;
+            blurRadius = drawable.mBlurRadius;
+            cornerRadiusTL = drawable.mCornerRadiusTL;
+            cornerRadiusTR = drawable.mCornerRadiusTR;
+            cornerRadiusBL = drawable.mCornerRadiusBL;
+            cornerRadiusBR = drawable.mCornerRadiusBR;
+            rect = drawable.mRect;
+        }
+
+        /**
+         * Serializes this class into a float array that's more JNI friendly.
+         */
+        float[] toFloatArray() {
+            final float[] floatArray = new float[10];
+            floatArray[0] = blurRadius;
+            floatArray[1] = alpha;
+            floatArray[2] = rect.left;
+            floatArray[3] = rect.top;
+            floatArray[4] = rect.right;
+            floatArray[5] = rect.bottom;
+            floatArray[6] = cornerRadiusTL;
+            floatArray[7] = cornerRadiusTR;
+            floatArray[8] = cornerRadiusBL;
+            floatArray[9] = cornerRadiusBR;
+            return floatArray;
+        }
+
+        @Override
+        public String toString() {
+            return "BlurRegion{"
+                    + "blurRadius=" + blurRadius
+                    + ", corners={" + cornerRadiusTL
+                    + "," + cornerRadiusTR
+                    + "," + cornerRadiusBL
+                    + "," + cornerRadiusBR
+                    + "}, alpha=" + alpha
+                    + ", rect=" + rect
+                    + "}";
         }
     }
 }
diff --git a/core/java/com/android/internal/graphics/palette/CelebiQuantizer.java b/core/java/com/android/internal/graphics/palette/CelebiQuantizer.java
new file mode 100644
index 0000000..de6bf20
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/CelebiQuantizer.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.graphics.palette;
+
+import java.util.List;
+
+/**
+ * An implementation of Celebi's WSM quantizer, or, a Kmeans quantizer that starts with centroids
+ * from a Wu quantizer to ensure 100% reproducible and quality results, and has some optimizations
+ * to the Kmeans algorithm.
+ *
+ * See Celebi 2011, “Improving the Performance of K-Means for Color Quantization”
+ */
+public class CelebiQuantizer implements Quantizer {
+    private List<Palette.Swatch> mSwatches;
+
+    public CelebiQuantizer() { }
+
+    @Override
+    public void quantize(int[] pixels, int maxColors) {
+        WuQuantizer wu = new WuQuantizer(pixels, maxColors);
+        wu.quantize(pixels, maxColors);
+        List<Palette.Swatch> wuSwatches = wu.getQuantizedColors();
+        LABCentroid labCentroidProvider = new LABCentroid();
+        WSMeansQuantizer kmeans =
+                new WSMeansQuantizer(WSMeansQuantizer.createStartingCentroids(labCentroidProvider,
+                        wuSwatches), labCentroidProvider, pixels, maxColors);
+        kmeans.quantize(pixels, maxColors);
+        mSwatches = kmeans.getQuantizedColors();
+    }
+
+    @Override
+    public List<Palette.Swatch> getQuantizedColors() {
+        return mSwatches;
+    }
+}
diff --git a/core/java/com/android/internal/graphics/palette/CentroidProvider.java b/core/java/com/android/internal/graphics/palette/CentroidProvider.java
new file mode 100644
index 0000000..5fcfcba
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/CentroidProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.graphics.palette;
+
+import android.annotation.ColorInt;
+
+interface CentroidProvider {
+    /**
+     * @return 3 dimensions representing the color
+     */
+    float[] getCentroid(@ColorInt int color);
+
+    /**
+     * @param centroid 3 dimensions representing the color
+     * @return 32-bit ARGB representation
+     */
+    @ColorInt
+    int getColor(float[] centroid);
+
+    /**
+     * Distance between two centroids.
+     */
+    float distance(float[] a, float[] b);
+}
diff --git a/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java
index 9ac753b..7779494 100644
--- a/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java
+++ b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java
@@ -35,6 +35,8 @@
 import android.graphics.Color;
 import android.util.TimingLogger;
 
+import com.android.internal.graphics.palette.Palette.Swatch;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,9 +44,6 @@
 import java.util.List;
 import java.util.PriorityQueue;
 
-import com.android.internal.graphics.ColorUtils;
-import com.android.internal.graphics.palette.Palette.Swatch;
-
 /**
  * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/
  * graphics/ColorCutQuantizer.java
@@ -77,20 +76,17 @@
     int[] mHistogram;
     List<Swatch> mQuantizedColors;
     TimingLogger mTimingLogger;
-    Palette.Filter[] mFilters;
 
     private final float[] mTempHsl = new float[3];
 
     /**
      * Execute color quantization.
      *
-     * @param pixels histogram representing an image's pixel data
+     * @param pixels    histogram representing an image's pixel data
      * @param maxColors The maximum number of colors that should be in the result palette.
-     * @param filters Set of filters to use in the quantization stage
      */
-    public void quantize(final int[] pixels, final int maxColors, final Palette.Filter[] filters) {
+    public void quantize(final int[] pixels, final int maxColors) {
         mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null;
-        mFilters = filters;
 
         final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)];
         for (int i = 0; i < pixels.length; i++) {
@@ -108,10 +104,6 @@
         // Now let's count the number of distinct colors
         int distinctColorCount = 0;
         for (int color = 0; color < hist.length; color++) {
-            if (hist[color] > 0 && shouldIgnoreColor(color)) {
-                // If we should ignore the color, set the population to 0
-                hist[color] = 0;
-            }
             if (hist[color] > 0) {
                 // If the color has population, increase the distinct color count
                 distinctColorCount++;
@@ -186,7 +178,7 @@
      * and splitting them. Once split, the new box and the remaining box are offered back to the
      * queue.
      *
-     * @param queue {@link java.util.PriorityQueue} to poll for boxes
+     * @param queue   {@link java.util.PriorityQueue} to poll for boxes
      * @param maxSize Maximum amount of boxes to split
      */
     private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) {
@@ -216,11 +208,7 @@
         ArrayList<Swatch> colors = new ArrayList<>(vboxes.size());
         for (Vbox vbox : vboxes) {
             Swatch swatch = vbox.getAverageColor();
-            if (!shouldIgnoreColor(swatch)) {
-                // As we're averaging a color box, we can still get colors which we do not want, so
-                // we check again here
-                colors.add(swatch);
-            }
+            colors.add(swatch);
         }
         return colors;
     }
@@ -230,7 +218,7 @@
      */
     private class Vbox {
         // lower and upper index are inclusive
-        private int mLowerIndex;
+        private final int mLowerIndex;
         private int mUpperIndex;
         // Population of colors within this box
         private int mPopulation;
@@ -373,7 +361,7 @@
             modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex);
 
             final int midPoint = mPopulation / 2;
-            for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++)  {
+            for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++) {
                 count += hist[colors[i]];
                 if (count >= midPoint) {
                     // we never want to split on the upperIndex, as this will result in the same
@@ -447,27 +435,6 @@
         }
     }
 
-    private boolean shouldIgnoreColor(int color565) {
-        final int rgb = approximateToRgb888(color565);
-        ColorUtils.colorToHSL(rgb, mTempHsl);
-        return shouldIgnoreColor(rgb, mTempHsl);
-    }
-
-    private boolean shouldIgnoreColor(Swatch color) {
-        return shouldIgnoreColor(color.getRgb(), color.getHsl());
-    }
-
-    private boolean shouldIgnoreColor(int rgb, float[] hsl) {
-        if (mFilters != null && mFilters.length > 0) {
-            for (int i = 0, count = mFilters.length; i < count; i++) {
-                if (!mFilters[i].isAllowed(rgb, hsl)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     /**
      * Comparator which sorts {@link Vbox} instances based on their volume, in descending order
      */
@@ -498,7 +465,8 @@
     }
 
     private static int approximateToRgb888(int color) {
-        return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color));
+        return approximateToRgb888(quantizedRed(color), quantizedGreen(color),
+                quantizedBlue(color));
     }
 
     /**
diff --git a/core/java/com/android/internal/graphics/palette/Contrast.java b/core/java/com/android/internal/graphics/palette/Contrast.java
new file mode 100644
index 0000000..3dd1b8d
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/Contrast.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.graphics.palette;
+
+/**
+ * Helper methods for determining contrast between two colors, either via the colors themselves
+ * or components in different color spaces.
+ */
+public class Contrast {
+    /**
+     *
+     * @param y Y in XYZ that contrasts with the returned Y value
+     * @param contrast contrast ratio between color argument and returned Y value. Must be >= 1
+     *    or an exception will be thrown
+     * @return the lower Y coordinate in XYZ space that contrasts with color, or -1 if reaching
+     *    no Y coordinate reaches contrast with color.
+     */
+    public static float lighterY(float y, float contrast) {
+        assert (contrast >= 1);
+        float answer = -5 + contrast * (5 + y);
+        if (answer > 100.0) {
+            return -1;
+        }
+        return answer;
+    }
+
+
+    /**
+     * @param y Y in XYZ that contrasts with the returned Y value
+     * @param contrast contrast ratio between color argument and returned Y value. Must be >= 1
+     *    or an exception will be thrown
+     * @return the lower Y coordinate in XYZ space that contrasts with color, or -1 if reaching
+     *    no Y coordinate reaches contrast with color.
+     */
+    public static float darkerY(float y, float contrast) {
+        assert (contrast >= 1);
+        float answer = (5 - 5 * contrast + y) / contrast;
+        if (answer < 0.0) {
+            return -1;
+        }
+        return answer;
+    }
+
+    /**
+     * Convert L* in L*a*b* to Y in XYZ.
+     *
+     * @param lstar L* in L*a*b*
+     * @return Y in XYZ
+     */
+    public static float lstarToY(float lstar) {
+        // http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
+        float ke = 8.0f;
+        if (lstar > ke) {
+            return (float) (Math.pow(((lstar + 16.0) / 116.0), 3) * 100.0);
+        } else {
+            return (float) (lstar / (24389 / 27) * 100.0);
+        }
+    }
+
+    /**
+     * Convert Y in XYZ to L* in L*a*b*.
+     *
+     * @param y Y in XYZ
+     * @return L* in L*a*b*
+     */
+    public static float yToLstar(float y) {
+        y = y / 100.0f;
+        float e = 216.0f / 24389.0f;
+        float y_intermediate;
+        if (y <= e) {
+            y_intermediate = (24389.f / 27.f) * y;
+            // If y < e, can skip consecutive steps of / 116 + 16 followed by * 116 - 16.
+            return y_intermediate;
+        } else {
+            y_intermediate = (float) Math.cbrt(y);
+        }
+        return 116.f * y_intermediate - 16.f;
+    }
+
+
+    /**
+     * @return Contrast ratio between two Y values in XYZ space.
+     */
+    public static float contrastYs(float y1, float y2) {
+        final float lighter = Math.max(y1, y2);
+        final float darker = (lighter == y1) ? y2 : y1;
+        return (lighter + 5) / (darker + 5);
+    }
+}
diff --git a/core/java/com/android/internal/graphics/palette/LABCentroid.java b/core/java/com/android/internal/graphics/palette/LABCentroid.java
new file mode 100644
index 0000000..98d5d26
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/LABCentroid.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.graphics.palette;
+
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+
+/**
+ *  Allows quantizers to operate in the L*a*b* colorspace.
+ *  L*a*b* is a good choice for measuring distance between colors.
+ *  Better spaces, and better distance calculations even in L*a*b* exist, but measuring distance
+ *  in L*a*b* space, also known as deltaE, is a universally accepted standard across industries
+ *  and worldwide.
+ */
+public class LABCentroid implements CentroidProvider {
+    final ColorSpace.Connector mRgbToLab;
+    final ColorSpace.Connector mLabToRgb;
+
+    public LABCentroid() {
+        mRgbToLab = ColorSpace.connect(
+                ColorSpace.get(ColorSpace.Named.SRGB),
+                ColorSpace.get(ColorSpace.Named.CIE_LAB));
+        mLabToRgb = ColorSpace.connect(ColorSpace.get(ColorSpace.Named.CIE_LAB),
+                ColorSpace.get(ColorSpace.Named.SRGB));
+    }
+
+    @Override
+    public float[] getCentroid(int color) {
+        float r = Color.red(color) / 255.f;
+        float g =  Color.green(color) / 255.f;
+        float b = Color.blue(color) / 255.f;
+
+        float[] transform = mRgbToLab.transform(r, g, b);
+        return transform;
+    }
+
+    @Override
+    public int getColor(float[] centroid) {
+        float[] rgb = mLabToRgb.transform(centroid);
+        int color = Color.rgb(rgb[0], rgb[1], rgb[2]);
+        return color;
+    }
+
+    @Override
+    public float distance(float[] a, float[] b) {
+        // Standard v1 CIELAB deltaE formula, 1976 - easily improved upon, however,
+        // improvements do not significantly impact the Palette algorithm's results.
+        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));
+    }
+}
diff --git a/core/java/com/android/internal/graphics/palette/Mean.java b/core/java/com/android/internal/graphics/palette/Mean.java
new file mode 100644
index 0000000..894f91b
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/Mean.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.graphics.palette;
+
+import java.util.Random;
+
+/**
+ * Represents a centroid in Kmeans algorithms.
+ */
+public class Mean {
+    private static final Random RANDOM = new Random(0);
+
+    public float[] center;
+
+    /**
+     * Constructor.
+     *
+     * @param upperBound maximum value of a dimension in the space Kmeans is optimizing in
+     */
+    Mean(int upperBound) {
+        center =
+                new float[]{
+                        RANDOM.nextInt(upperBound + 1), RANDOM.nextInt(upperBound + 1),
+                        RANDOM.nextInt(upperBound + 1)
+                };
+    }
+
+    Mean(float[] center) {
+        this.center = center;
+    }
+}
diff --git a/core/java/com/android/internal/graphics/palette/MeanBucket.java b/core/java/com/android/internal/graphics/palette/MeanBucket.java
new file mode 100644
index 0000000..ae8858a
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/MeanBucket.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.graphics.palette;
+
+import java.util.HashSet;
+import java.util.Set;
+
+class MeanBucket {
+    float[] mTotal = {0.f, 0.f, 0.f};
+    int mCount = 0;
+    Set<Integer> mColors = new HashSet<>();
+
+    void add(float[] colorAsDoubles, int color, int colorCount) {
+        assert (colorAsDoubles.length == 3);
+        mColors.add(color);
+        mTotal[0] += (colorAsDoubles[0] * colorCount);
+        mTotal[1] += (colorAsDoubles[1] * colorCount);
+        mTotal[2] += (colorAsDoubles[2] * colorCount);
+        mCount += colorCount;
+    }
+
+    float[] getCentroid() {
+        if (mCount == 0) {
+            return null;
+        }
+        return new float[]{mTotal[0] / mCount, mTotal[1] / mCount, mTotal[2] / mCount};
+    }
+}
diff --git a/core/java/com/android/internal/graphics/palette/Palette.java b/core/java/com/android/internal/graphics/palette/Palette.java
index a4f9a59..8b1137d 100644
--- a/core/java/com/android/internal/graphics/palette/Palette.java
+++ b/core/java/com/android/internal/graphics/palette/Palette.java
@@ -19,48 +19,24 @@
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.Px;
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Rect;
-import android.os.AsyncTask;
-import android.util.ArrayMap;
 import android.util.Log;
-import android.util.SparseBooleanArray;
-import android.util.TimingLogger;
 
-import com.android.internal.graphics.ColorUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 
 
 /**
- * Copied from: /frameworks/support/v7/palette/src/main/java/android/support/v7/
- * graphics/Palette.java
- *
  * A helper class to extract prominent colors from an image.
- * <p>
- * A number of colors with different profiles are extracted from the image:
- * <ul>
- *     <li>Vibrant</li>
- *     <li>Vibrant Dark</li>
- *     <li>Vibrant Light</li>
- *     <li>Muted</li>
- *     <li>Muted Dark</li>
- *     <li>Muted Light</li>
- * </ul>
- * These can be retrieved from the appropriate getter method.
  *
- * <p>
- * Instances are created with a {@link Palette.Builder} which supports several options to tweak the
+ * <p>Instances are created with a {@link Builder} which supports several options to tweak the
  * generated Palette. See that class' documentation for more information.
- * <p>
- * Generation should always be completed on a background thread, ideally the one in
- * which you load your image on. {@link Palette.Builder} supports both synchronous and asynchronous
- * generation:
+ *
+ * <p>Generation should always be completed on a background thread, ideally the one in which you
+ * load your image on. {@link Builder} supports both synchronous and asynchronous generation:
  *
  * <pre>
  * // Synchronous
@@ -85,346 +61,59 @@
         /**
          * Called when the {@link Palette} has been generated.
          */
-        void onGenerated(Palette palette);
+        void onGenerated(@Nullable Palette palette);
     }
 
     static final int DEFAULT_RESIZE_BITMAP_AREA = 112 * 112;
     static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16;
-
-    static final float MIN_CONTRAST_TITLE_TEXT = 3.0f;
-    static final float MIN_CONTRAST_BODY_TEXT = 4.5f;
-
     static final String LOG_TAG = "Palette";
-    static final boolean LOG_TIMINGS = false;
 
-    /**
-     * Start generating a {@link Palette} with the returned {@link Palette.Builder} instance.
-     */
-    public static Palette.Builder from(Bitmap bitmap) {
-        return new Palette.Builder(bitmap);
+    /** Start generating a {@link Palette} with the returned {@link Builder} instance. */
+    @NonNull
+    public static Builder from(@NonNull Bitmap bitmap, @NonNull Quantizer quantizer) {
+        return new Builder(bitmap, quantizer);
     }
 
     /**
      * Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches.
-     * This is useful for testing, or if you want to resurrect a {@link Palette} instance from a
-     * list of swatches. Will return null if the {@code swatches} is null.
+     * This
+     * is useful for testing, or if you want to resurrect a {@link Palette} instance from a list of
+     * swatches. Will return null if the {@code swatches} is null.
      */
-    public static Palette from(List<Palette.Swatch> swatches) {
-        return new Palette.Builder(swatches).generate();
+    @NonNull
+    public static Palette from(@NonNull List<Swatch> swatches) {
+        return new Builder(swatches).generate();
     }
 
-    /**
-     * @deprecated Use {@link Palette.Builder} to generate the Palette.
-     */
-    @Deprecated
-    public static Palette generate(Bitmap bitmap) {
-        return from(bitmap).generate();
-    }
+    private final List<Swatch> mSwatches;
 
-    /**
-     * @deprecated Use {@link Palette.Builder} to generate the Palette.
-     */
-    @Deprecated
-    public static Palette generate(Bitmap bitmap, int numColors) {
-        return from(bitmap).maximumColorCount(numColors).generate();
-    }
 
-    /**
-     * @deprecated Use {@link Palette.Builder} to generate the Palette.
-     */
-    @Deprecated
-    public static AsyncTask<Bitmap, Void, Palette> generateAsync(
-            Bitmap bitmap, Palette.PaletteAsyncListener listener) {
-        return from(bitmap).generate(listener);
-    }
+    @Nullable
+    private final Swatch mDominantSwatch;
 
-    /**
-     * @deprecated Use {@link Palette.Builder} to generate the Palette.
-     */
-    @Deprecated
-    public static AsyncTask<Bitmap, Void, Palette> generateAsync(
-            final Bitmap bitmap, final int numColors, final Palette.PaletteAsyncListener listener) {
-        return from(bitmap).maximumColorCount(numColors).generate(listener);
-    }
-
-    private final List<Palette.Swatch> mSwatches;
-    private final List<Target> mTargets;
-
-    private final Map<Target, Palette.Swatch> mSelectedSwatches;
-    private final SparseBooleanArray mUsedColors;
-
-    private final Palette.Swatch mDominantSwatch;
-
-    Palette(List<Palette.Swatch> swatches, List<Target> targets) {
+    Palette(List<Swatch> swatches) {
         mSwatches = swatches;
-        mTargets = targets;
-
-        mUsedColors = new SparseBooleanArray();
-        mSelectedSwatches = new ArrayMap<>();
-
         mDominantSwatch = findDominantSwatch();
     }
 
-    /**
-     * Returns all of the swatches which make up the palette.
-     */
+    /** Returns all of the swatches which make up the palette. */
     @NonNull
-    public List<Palette.Swatch> getSwatches() {
+    public List<Swatch> getSwatches() {
         return Collections.unmodifiableList(mSwatches);
     }
 
-    /**
-     * Returns the targets used to generate this palette.
-     */
-    @NonNull
-    public List<Target> getTargets() {
-        return Collections.unmodifiableList(mTargets);
-    }
-
-    /**
-     * Returns the most vibrant swatch in the palette. Might be null.
-     *
-     * @see Target#VIBRANT
-     */
+    /** Returns the swatch with the highest population, or null if there are no swatches. */
     @Nullable
-    public Palette.Swatch getVibrantSwatch() {
-        return getSwatchForTarget(Target.VIBRANT);
-    }
-
-    /**
-     * Returns a light and vibrant swatch from the palette. Might be null.
-     *
-     * @see Target#LIGHT_VIBRANT
-     */
-    @Nullable
-    public Palette.Swatch getLightVibrantSwatch() {
-        return getSwatchForTarget(Target.LIGHT_VIBRANT);
-    }
-
-    /**
-     * Returns a dark and vibrant swatch from the palette. Might be null.
-     *
-     * @see Target#DARK_VIBRANT
-     */
-    @Nullable
-    public Palette.Swatch getDarkVibrantSwatch() {
-        return getSwatchForTarget(Target.DARK_VIBRANT);
-    }
-
-    /**
-     * Returns a muted swatch from the palette. Might be null.
-     *
-     * @see Target#MUTED
-     */
-    @Nullable
-    public Palette.Swatch getMutedSwatch() {
-        return getSwatchForTarget(Target.MUTED);
-    }
-
-    /**
-     * Returns a muted and light swatch from the palette. Might be null.
-     *
-     * @see Target#LIGHT_MUTED
-     */
-    @Nullable
-    public Palette.Swatch getLightMutedSwatch() {
-        return getSwatchForTarget(Target.LIGHT_MUTED);
-    }
-
-    /**
-     * Returns a muted and dark swatch from the palette. Might be null.
-     *
-     * @see Target#DARK_MUTED
-     */
-    @Nullable
-    public Palette.Swatch getDarkMutedSwatch() {
-        return getSwatchForTarget(Target.DARK_MUTED);
-    }
-
-    /**
-     * Returns the most vibrant color in the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getVibrantSwatch()
-     */
-    @ColorInt
-    public int getVibrantColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.VIBRANT, defaultColor);
-    }
-
-    /**
-     * Returns a light and vibrant color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getLightVibrantSwatch()
-     */
-    @ColorInt
-    public int getLightVibrantColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.LIGHT_VIBRANT, defaultColor);
-    }
-
-    /**
-     * Returns a dark and vibrant color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getDarkVibrantSwatch()
-     */
-    @ColorInt
-    public int getDarkVibrantColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.DARK_VIBRANT, defaultColor);
-    }
-
-    /**
-     * Returns a muted color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getMutedSwatch()
-     */
-    @ColorInt
-    public int getMutedColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.MUTED, defaultColor);
-    }
-
-    /**
-     * Returns a muted and light color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getLightMutedSwatch()
-     */
-    @ColorInt
-    public int getLightMutedColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.LIGHT_MUTED, defaultColor);
-    }
-
-    /**
-     * Returns a muted and dark color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getDarkMutedSwatch()
-     */
-    @ColorInt
-    public int getDarkMutedColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.DARK_MUTED, defaultColor);
-    }
-
-    /**
-     * Returns the selected swatch for the given target from the palette, or {@code null} if one
-     * could not be found.
-     */
-    @Nullable
-    public Palette.Swatch getSwatchForTarget(@NonNull final Target target) {
-        return mSelectedSwatches.get(target);
-    }
-
-    /**
-     * Returns the selected color for the given target from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     */
-    @ColorInt
-    public int getColorForTarget(@NonNull final Target target, @ColorInt final int defaultColor) {
-        Palette.Swatch swatch = getSwatchForTarget(target);
-        return swatch != null ? swatch.getRgb() : defaultColor;
-    }
-
-    /**
-     * Returns the dominant swatch from the palette.
-     *
-     * <p>The dominant swatch is defined as the swatch with the greatest population (frequency)
-     * within the palette.</p>
-     */
-    @Nullable
-    public Palette.Swatch getDominantSwatch() {
+    public Swatch getDominantSwatch() {
         return mDominantSwatch;
     }
 
-    /**
-     * Returns the color of the dominant swatch from the palette, as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getDominantSwatch()
-     */
-    @ColorInt
-    public int getDominantColor(@ColorInt int defaultColor) {
-        return mDominantSwatch != null ? mDominantSwatch.getRgb() : defaultColor;
-    }
-
-    void generate() {
-        // We need to make sure that the scored targets are generated first. This is so that
-        // inherited targets have something to inherit from
-        for (int i = 0, count = mTargets.size(); i < count; i++) {
-            final Target target = mTargets.get(i);
-            target.normalizeWeights();
-            mSelectedSwatches.put(target, generateScoredTarget(target));
-        }
-        // We now clear out the used colors
-        mUsedColors.clear();
-    }
-
-    private Palette.Swatch generateScoredTarget(final Target target) {
-        final Palette.Swatch maxScoreSwatch = getMaxScoredSwatchForTarget(target);
-        if (maxScoreSwatch != null && target.isExclusive()) {
-            // If we have a swatch, and the target is exclusive, add the color to the used list
-            mUsedColors.append(maxScoreSwatch.getRgb(), true);
-        }
-        return maxScoreSwatch;
-    }
-
-    private Palette.Swatch getMaxScoredSwatchForTarget(final Target target) {
-        float maxScore = 0;
-        Palette.Swatch maxScoreSwatch = null;
-        for (int i = 0, count = mSwatches.size(); i < count; i++) {
-            final Palette.Swatch swatch = mSwatches.get(i);
-            if (shouldBeScoredForTarget(swatch, target)) {
-                final float score = generateScore(swatch, target);
-                if (maxScoreSwatch == null || score > maxScore) {
-                    maxScoreSwatch = swatch;
-                    maxScore = score;
-                }
-            }
-        }
-        return maxScoreSwatch;
-    }
-
-    private boolean shouldBeScoredForTarget(final Palette.Swatch swatch, final Target target) {
-        // Check whether the HSL values are within the correct ranges, and this color hasn't
-        // been used yet.
-        final float hsl[] = swatch.getHsl();
-        return hsl[1] >= target.getMinimumSaturation() && hsl[1] <= target.getMaximumSaturation()
-                && hsl[2] >= target.getMinimumLightness() && hsl[2] <= target.getMaximumLightness()
-                && !mUsedColors.get(swatch.getRgb());
-    }
-
-    private float generateScore(Palette.Swatch swatch, Target target) {
-        final float[] hsl = swatch.getHsl();
-
-        float saturationScore = 0;
-        float luminanceScore = 0;
-        float populationScore = 0;
-
-        final int maxPopulation = mDominantSwatch != null ? mDominantSwatch.getPopulation() : 1;
-
-        if (target.getSaturationWeight() > 0) {
-            saturationScore = target.getSaturationWeight()
-                    * (1f - Math.abs(hsl[1] - target.getTargetSaturation()));
-        }
-        if (target.getLightnessWeight() > 0) {
-            luminanceScore = target.getLightnessWeight()
-                    * (1f - Math.abs(hsl[2] - target.getTargetLightness()));
-        }
-        if (target.getPopulationWeight() > 0) {
-            populationScore = target.getPopulationWeight()
-                    * (swatch.getPopulation() / (float) maxPopulation);
-        }
-
-        return saturationScore + luminanceScore + populationScore;
-    }
-
-    private Palette.Swatch findDominantSwatch() {
+    @Nullable
+    private Swatch findDominantSwatch() {
         int maxPop = Integer.MIN_VALUE;
-        Palette.Swatch maxSwatch = null;
+        Swatch maxSwatch = null;
         for (int i = 0, count = mSwatches.size(); i < count; i++) {
-            Palette.Swatch swatch = mSwatches.get(i);
+            Swatch swatch = mSwatches.get(i);
             if (swatch.getPopulation() > maxPop) {
                 maxSwatch = swatch;
                 maxPop = swatch.getPopulation();
@@ -433,148 +122,42 @@
         return maxSwatch;
     }
 
-    private static float[] copyHslValues(Palette.Swatch color) {
-        final float[] newHsl = new float[3];
-        System.arraycopy(color.getHsl(), 0, newHsl, 0, 3);
-        return newHsl;
-    }
-
     /**
      * Represents a color swatch generated from an image's palette. The RGB color can be retrieved
-     * by calling {@link #getRgb()}.
+     * by
+     * calling {@link #getInt()}.
      */
-    public static final class Swatch {
-        private final int mRed, mGreen, mBlue;
-        private final int mRgb;
+    public static class Swatch {
+        private final Color mColor;
         private final int mPopulation;
 
-        private boolean mGeneratedTextColors;
-        private int mTitleTextColor;
-        private int mBodyTextColor;
 
-        private float[] mHsl;
-
-        public Swatch(@ColorInt int color, int population) {
-            mRed = Color.red(color);
-            mGreen = Color.green(color);
-            mBlue = Color.blue(color);
-            mRgb = color;
+        public Swatch(@ColorInt int colorInt, int population) {
+            mColor = Color.valueOf(colorInt);
             mPopulation = population;
         }
 
-        Swatch(int red, int green, int blue, int population) {
-            mRed = red;
-            mGreen = green;
-            mBlue = blue;
-            mRgb = Color.rgb(red, green, blue);
-            mPopulation = population;
-        }
-
-        Swatch(float[] hsl, int population) {
-            this(ColorUtils.HSLToColor(hsl), population);
-            mHsl = hsl;
-        }
-
-        /**
-         * @return this swatch's RGB color value
-         */
+        /** @return this swatch's RGB color value */
         @ColorInt
-        public int getRgb() {
-            return mRgb;
+        public int getInt() {
+            return mColor.toArgb();
         }
 
-        /**
-         * Return this swatch's HSL values.
-         *     hsv[0] is Hue [0 .. 360)
-         *     hsv[1] is Saturation [0...1]
-         *     hsv[2] is Lightness [0...1]
-         */
-        public float[] getHsl() {
-            if (mHsl == null) {
-                mHsl = new float[3];
-            }
-            ColorUtils.RGBToHSL(mRed, mGreen, mBlue, mHsl);
-            return mHsl;
-        }
-
-        /**
-         * @return the number of pixels represented by this swatch
-         */
+        /** @return the number of pixels represented by this swatch */
         public int getPopulation() {
             return mPopulation;
         }
 
-        /**
-         * Returns an appropriate color to use for any 'title' text which is displayed over this
-         * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast.
-         */
-        @ColorInt
-        public int getTitleTextColor() {
-            ensureTextColorsGenerated();
-            return mTitleTextColor;
-        }
-
-        /**
-         * Returns an appropriate color to use for any 'body' text which is displayed over this
-         * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast.
-         */
-        @ColorInt
-        public int getBodyTextColor() {
-            ensureTextColorsGenerated();
-            return mBodyTextColor;
-        }
-
-        private void ensureTextColorsGenerated() {
-            if (!mGeneratedTextColors) {
-                // First check white, as most colors will be dark
-                final int lightBodyAlpha = ColorUtils.calculateMinimumAlpha(
-                        Color.WHITE, mRgb, MIN_CONTRAST_BODY_TEXT);
-                final int lightTitleAlpha = ColorUtils.calculateMinimumAlpha(
-                        Color.WHITE, mRgb, MIN_CONTRAST_TITLE_TEXT);
-
-                if (lightBodyAlpha != -1 && lightTitleAlpha != -1) {
-                    // If we found valid light values, use them and return
-                    mBodyTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha);
-                    mTitleTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha);
-                    mGeneratedTextColors = true;
-                    return;
-                }
-
-                final int darkBodyAlpha = ColorUtils.calculateMinimumAlpha(
-                        Color.BLACK, mRgb, MIN_CONTRAST_BODY_TEXT);
-                final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha(
-                        Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT);
-
-                if (darkBodyAlpha != -1 && darkTitleAlpha != -1) {
-                    // If we found valid dark values, use them and return
-                    mBodyTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
-                    mTitleTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
-                    mGeneratedTextColors = true;
-                    return;
-                }
-
-                // If we reach here then we can not find title and body values which use the same
-                // lightness, we need to use mismatched values
-                mBodyTextColor = lightBodyAlpha != -1
-                        ? ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha)
-                        : ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
-                mTitleTextColor = lightTitleAlpha != -1
-                        ? ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha)
-                        : ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
-                mGeneratedTextColors = true;
-            }
-        }
-
         @Override
         public String toString() {
             return new StringBuilder(getClass().getSimpleName())
-                    .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']')
-                    .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']')
-                    .append(" [Population: ").append(mPopulation).append(']')
-                    .append(" [Title Text: #").append(Integer.toHexString(getTitleTextColor()))
+                    .append(" [")
+                    .append(mColor)
                     .append(']')
-                    .append(" [Body Text: #").append(Integer.toHexString(getBodyTextColor()))
-                    .append(']').toString();
+                    .append(" [Population: ")
+                    .append(mPopulation)
+                    .append(']')
+                    .toString();
         }
 
         @Override
@@ -586,243 +169,168 @@
                 return false;
             }
 
-            Palette.Swatch
-                    swatch = (Palette.Swatch) o;
-            return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb;
+            Swatch swatch = (Swatch) o;
+            return mPopulation == swatch.mPopulation && mColor.toArgb() == swatch.mColor.toArgb();
         }
 
         @Override
         public int hashCode() {
-            return 31 * mRgb + mPopulation;
+            return 31 * mColor.toArgb() + mPopulation;
         }
     }
 
-    /**
-     * Builder class for generating {@link Palette} instances.
-     */
-    public static final class Builder {
-        private final List<Palette.Swatch> mSwatches;
+    /** Builder class for generating {@link Palette} instances. */
+    public static class Builder {
+        @Nullable
+        private final List<Swatch> mSwatches;
+        @Nullable
         private final Bitmap mBitmap;
+        @Nullable
+        private Quantizer mQuantizer = new ColorCutQuantizer();
 
-        private final List<Target> mTargets = new ArrayList<>();
 
         private int mMaxColors = DEFAULT_CALCULATE_NUMBER_COLORS;
         private int mResizeArea = DEFAULT_RESIZE_BITMAP_AREA;
         private int mResizeMaxDimension = -1;
 
-        private final List<Palette.Filter> mFilters = new ArrayList<>();
+        @Nullable
         private Rect mRegion;
 
-        private Quantizer mQuantizer;
-
-        /**
-         * Construct a new {@link Palette.Builder} using a source {@link Bitmap}
-         */
-        public Builder(Bitmap bitmap) {
+        /** Construct a new {@link Builder} using a source {@link Bitmap} */
+        public Builder(@NonNull Bitmap bitmap, @NonNull Quantizer quantizer) {
             if (bitmap == null || bitmap.isRecycled()) {
                 throw new IllegalArgumentException("Bitmap is not valid");
             }
-            mFilters.add(DEFAULT_FILTER);
-            mBitmap = bitmap;
             mSwatches = null;
-
-            // Add the default targets
-            mTargets.add(Target.LIGHT_VIBRANT);
-            mTargets.add(Target.VIBRANT);
-            mTargets.add(Target.DARK_VIBRANT);
-            mTargets.add(Target.LIGHT_MUTED);
-            mTargets.add(Target.MUTED);
-            mTargets.add(Target.DARK_MUTED);
+            mBitmap = bitmap;
+            mQuantizer = quantizer == null ? new ColorCutQuantizer() : quantizer;
         }
 
         /**
-         * Construct a new {@link Palette.Builder} using a list of {@link Palette.Swatch} instances.
-         * Typically only used for testing.
+         * Construct a new {@link Builder} using a list of {@link Swatch} instances. Typically only
+         * used
+         * for testing.
          */
-        public Builder(List<Palette.Swatch> swatches) {
+        public Builder(@NonNull List<Swatch> swatches) {
             if (swatches == null || swatches.isEmpty()) {
                 throw new IllegalArgumentException("List of Swatches is not valid");
             }
-            mFilters.add(DEFAULT_FILTER);
             mSwatches = swatches;
             mBitmap = null;
+            mQuantizer = null;
         }
 
         /**
-         * Set the maximum number of colors to use in the quantization step when using a
-         * {@link android.graphics.Bitmap} as the source.
-         * <p>
-         * Good values for depend on the source image type. For landscapes, good values are in
-         * the range 10-16. For images which are largely made up of people's faces then this
-         * value should be increased to ~24.
+         * Set the maximum number of colors to use in the quantization step when using a {@link
+         * android.graphics.Bitmap} as the source.
+         *
+         * <p>Good values for depend on the source image type. For landscapes, good values are in
+         * the
+         * range 10-16. For images which are largely made up of people's faces then this value
+         * should be
+         * increased to ~24.
          */
         @NonNull
-        public Palette.Builder maximumColorCount(int colors) {
+        public Builder maximumColorCount(int colors) {
             mMaxColors = colors;
             return this;
         }
 
         /**
-         * Set the resize value when using a {@link android.graphics.Bitmap} as the source.
-         * If the bitmap's largest dimension is greater than the value specified, then the bitmap
-         * will be resized so that its largest dimension matches {@code maxDimension}. If the
-         * bitmap is smaller or equal, the original is used as-is.
-         *
-         * @deprecated Using {@link #resizeBitmapArea(int)} is preferred since it can handle
-         * abnormal aspect ratios more gracefully.
+         * Set the resize value when using a {@link android.graphics.Bitmap} as the source. If the
+         * bitmap's largest dimension is greater than the value specified, then the bitmap will be
+         * resized so that its largest dimension matches {@code maxDimension}. If the bitmap is
+         * smaller
+         * or equal, the original is used as-is.
          *
          * @param maxDimension the number of pixels that the max dimension should be scaled down to,
-         *                     or any value <= 0 to disable resizing.
+         *                     or
+         *                     any value <= 0 to disable resizing.
+         * @deprecated Using {@link #resizeBitmapArea(int)} is preferred since it can handle
+         * abnormal
+         * aspect ratios more gracefully.
          */
         @NonNull
         @Deprecated
-        public Palette.Builder resizeBitmapSize(final int maxDimension) {
+        public Builder resizeBitmapSize(int maxDimension) {
             mResizeMaxDimension = maxDimension;
             mResizeArea = -1;
             return this;
         }
 
         /**
-         * Set the resize value when using a {@link android.graphics.Bitmap} as the source.
-         * If the bitmap's area is greater than the value specified, then the bitmap
-         * will be resized so that its area matches {@code area}. If the
-         * bitmap is smaller or equal, the original is used as-is.
-         * <p>
-         * This value has a large effect on the processing time. The larger the resized image is,
-         * the greater time it will take to generate the palette. The smaller the image is, the
-         * more detail is lost in the resulting image and thus less precision for color selection.
+         * Set the resize value when using a {@link android.graphics.Bitmap} as the source. If the
+         * bitmap's area is greater than the value specified, then the bitmap will be resized so
+         * that
+         * its area matches {@code area}. If the bitmap is smaller or equal, the original is used
+         * as-is.
+         *
+         * <p>This value has a large effect on the processing time. The larger the resized image is,
+         * the
+         * greater time it will take to generate the palette. The smaller the image is, the more
+         * detail
+         * is lost in the resulting image and thus less precision for color selection.
          *
          * @param area the number of pixels that the intermediary scaled down Bitmap should cover,
-         *             or any value <= 0 to disable resizing.
+         *             or
+         *             any value <= 0 to disable resizing.
          */
         @NonNull
-        public Palette.Builder resizeBitmapArea(final int area) {
+        public Builder resizeBitmapArea(int area) {
             mResizeArea = area;
             mResizeMaxDimension = -1;
             return this;
         }
 
         /**
-         * Clear all added filters. This includes any default filters added automatically by
-         * {@link Palette}.
-         */
-        @NonNull
-        public Palette.Builder clearFilters() {
-            mFilters.clear();
-            return this;
-        }
-
-        /**
-         * Add a filter to be able to have fine grained control over which colors are
-         * allowed in the resulting palette.
-         *
-         * @param filter filter to add.
-         */
-        @NonNull
-        public Palette.Builder addFilter(
-                Palette.Filter filter) {
-            if (filter != null) {
-                mFilters.add(filter);
-            }
-            return this;
-        }
-
-        /**
-         * Set a specific quantization algorithm. {@link ColorCutQuantizer} will
-         * be used if unspecified.
-         *
-         * @param quantizer Quantizer implementation.
-         */
-        @NonNull
-        public Palette.Builder setQuantizer(Quantizer quantizer) {
-            mQuantizer = quantizer;
-            return this;
-        }
-
-        /**
          * Set a region of the bitmap to be used exclusively when calculating the palette.
-         * <p>This only works when the original input is a {@link Bitmap}.</p>
          *
-         * @param left The left side of the rectangle used for the region.
-         * @param top The top of the rectangle used for the region.
-         * @param right The right side of the rectangle used for the region.
+         * <p>This only works when the original input is a {@link Bitmap}.
+         *
+         * @param left   The left side of the rectangle used for the region.
+         * @param top    The top of the rectangle used for the region.
+         * @param right  The right side of the rectangle used for the region.
          * @param bottom The bottom of the rectangle used for the region.
          */
         @NonNull
-        public Palette.Builder setRegion(int left, int top, int right, int bottom) {
+        public Builder setRegion(@Px int left, @Px int top, @Px int right, @Px int bottom) {
             if (mBitmap != null) {
                 if (mRegion == null) mRegion = new Rect();
                 // Set the Rect to be initially the whole Bitmap
                 mRegion.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
                 // Now just get the intersection with the region
                 if (!mRegion.intersect(left, top, right, bottom)) {
-                    throw new IllegalArgumentException("The given region must intersect with "
-                            + "the Bitmap's dimensions.");
+                    throw new IllegalArgumentException(
+                            "The given region must intersect with " + "the Bitmap's dimensions.");
                 }
             }
             return this;
         }
 
-        /**
-         * Clear any previously region set via {@link #setRegion(int, int, int, int)}.
-         */
+        /** Clear any previously region set via {@link #setRegion(int, int, int, int)}. */
         @NonNull
-        public Palette.Builder clearRegion() {
+        public Builder clearRegion() {
             mRegion = null;
             return this;
         }
 
-        /**
-         * Add a target profile to be generated in the palette.
-         *
-         * <p>You can retrieve the result via {@link Palette#getSwatchForTarget(Target)}.</p>
-         */
-        @NonNull
-        public Palette.Builder addTarget(@NonNull final Target target) {
-            if (!mTargets.contains(target)) {
-                mTargets.add(target);
-            }
-            return this;
-        }
 
-        /**
-         * Clear all added targets. This includes any default targets added automatically by
-         * {@link Palette}.
-         */
-        @NonNull
-        public Palette.Builder clearTargets() {
-            if (mTargets != null) {
-                mTargets.clear();
-            }
-            return this;
-        }
-
-        /**
-         * Generate and return the {@link Palette} synchronously.
-         */
+        /** Generate and return the {@link Palette} synchronously. */
         @NonNull
         public Palette generate() {
-            final TimingLogger logger = LOG_TIMINGS
-                    ? new TimingLogger(LOG_TAG, "Generation")
-                    : null;
-
-            List<Palette.Swatch> swatches;
+            List<Swatch> swatches;
 
             if (mBitmap != null) {
                 // We have a Bitmap so we need to use quantization to reduce the number of colors
 
                 // First we'll scale down the bitmap if needed
-                final Bitmap bitmap = scaleBitmapDown(mBitmap);
+                Bitmap bitmap = scaleBitmapDown(mBitmap);
 
-                if (logger != null) {
-                    logger.addSplit("Processed Bitmap");
-                }
-
-                final Rect region = mRegion;
+                Rect region = mRegion;
                 if (bitmap != mBitmap && region != null) {
                     // If we have a scaled bitmap and a selected region, we need to scale down the
                     // region to match the new scale
-                    final double scale = bitmap.getWidth() / (double) mBitmap.getWidth();
+                    double scale = bitmap.getWidth() / (double) mBitmap.getWidth();
                     region.left = (int) Math.floor(region.left * scale);
                     region.top = (int) Math.floor(region.top * scale);
                     region.right = Math.min((int) Math.ceil(region.right * scale),
@@ -832,54 +340,47 @@
                 }
 
                 // Now generate a quantizer from the Bitmap
-                if (mQuantizer == null) {
-                    mQuantizer = new ColorCutQuantizer();
-                }
-                mQuantizer.quantize(getPixelsFromBitmap(bitmap),
-                            mMaxColors, mFilters.isEmpty() ? null :
-                            mFilters.toArray(new Palette.Filter[mFilters.size()]));
 
+                mQuantizer.quantize(
+                        getPixelsFromBitmap(bitmap),
+                        mMaxColors);
                 // If created a new bitmap, recycle it
                 if (bitmap != mBitmap) {
                     bitmap.recycle();
                 }
-
                 swatches = mQuantizer.getQuantizedColors();
-
-                if (logger != null) {
-                    logger.addSplit("Color quantization completed");
-                }
-            } else {
+            } else if (mSwatches != null) {
                 // Else we're using the provided swatches
                 swatches = mSwatches;
+            } else {
+                // The constructors enforce either a bitmap or swatches are present.
+                throw new AssertionError();
             }
 
             // Now create a Palette instance
-            final Palette p = new Palette(swatches, mTargets);
+            Palette p = new Palette(swatches);
             // And make it generate itself
-            p.generate();
-
-            if (logger != null) {
-                logger.addSplit("Created Palette");
-                logger.dumpToLog();
-            }
 
             return p;
         }
 
         /**
-         * Generate the {@link Palette} asynchronously. The provided listener's
-         * {@link Palette.PaletteAsyncListener#onGenerated} method will be called with the palette when
-         * generated.
+         * Generate the {@link Palette} asynchronously. The provided listener's {@link
+         * PaletteAsyncListener#onGenerated} method will be called with the palette when generated.
+         *
+         * @deprecated Use the standard <code>java.util.concurrent</code> or <a
+         * href="https://developer.android.com/topic/libraries/architecture/coroutines">Kotlin
+         * concurrency utilities</a> to call {@link #generate()} instead.
          */
         @NonNull
-        public AsyncTask<Bitmap, Void, Palette> generate(final Palette.PaletteAsyncListener listener) {
-            if (listener == null) {
-                throw new IllegalArgumentException("listener can not be null");
-            }
+        @Deprecated
+        public android.os.AsyncTask<Bitmap, Void, Palette> generate(
+                @NonNull PaletteAsyncListener listener) {
+            assert (listener != null);
 
-            return new AsyncTask<Bitmap, Void, Palette>() {
+            return new android.os.AsyncTask<Bitmap, Void, Palette>() {
                 @Override
+                @Nullable
                 protected Palette doInBackground(Bitmap... params) {
                     try {
                         return generate();
@@ -890,16 +391,16 @@
                 }
 
                 @Override
-                protected void onPostExecute(Palette colorExtractor) {
+                protected void onPostExecute(@Nullable Palette colorExtractor) {
                     listener.onGenerated(colorExtractor);
                 }
-            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap);
+            }.executeOnExecutor(android.os.AsyncTask.THREAD_POOL_EXECUTOR, mBitmap);
         }
 
         private int[] getPixelsFromBitmap(Bitmap bitmap) {
-            final int bitmapWidth = bitmap.getWidth();
-            final int bitmapHeight = bitmap.getHeight();
-            final int[] pixels = new int[bitmapWidth * bitmapHeight];
+            int bitmapWidth = bitmap.getWidth();
+            int bitmapHeight = bitmap.getHeight();
+            int[] pixels = new int[bitmapWidth * bitmapHeight];
             bitmap.getPixels(pixels, 0, bitmapWidth, 0, 0, bitmapWidth, bitmapHeight);
 
             if (mRegion == null) {
@@ -908,32 +409,34 @@
             } else {
                 // If we do have a region, lets create a subset array containing only the region's
                 // pixels
-                final int regionWidth = mRegion.width();
-                final int regionHeight = mRegion.height();
+                int regionWidth = mRegion.width();
+                int regionHeight = mRegion.height();
                 // pixels contains all of the pixels, so we need to iterate through each row and
                 // copy the regions pixels into a new smaller array
-                final int[] subsetPixels = new int[regionWidth * regionHeight];
+                int[] subsetPixels = new int[regionWidth * regionHeight];
                 for (int row = 0; row < regionHeight; row++) {
-                    System.arraycopy(pixels, ((row + mRegion.top) * bitmapWidth) + mRegion.left,
-                            subsetPixels, row * regionWidth, regionWidth);
+                    System.arraycopy(
+                            pixels,
+                            ((row + mRegion.top) * bitmapWidth) + mRegion.left,
+                            subsetPixels,
+                            row * regionWidth,
+                            regionWidth);
                 }
                 return subsetPixels;
             }
         }
 
-        /**
-         * Scale the bitmap down as needed.
-         */
-        private Bitmap scaleBitmapDown(final Bitmap bitmap) {
+        /** Scale the bitmap down as needed. */
+        private Bitmap scaleBitmapDown(Bitmap bitmap) {
             double scaleRatio = -1;
 
             if (mResizeArea > 0) {
-                final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
+                int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
                 if (bitmapArea > mResizeArea) {
                     scaleRatio = Math.sqrt(mResizeArea / (double) bitmapArea);
                 }
             } else if (mResizeMaxDimension > 0) {
-                final int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
+                int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
                 if (maxDimension > mResizeMaxDimension) {
                     scaleRatio = mResizeMaxDimension / (double) maxDimension;
                 }
@@ -944,11 +447,13 @@
                 return bitmap;
             }
 
-            return Bitmap.createScaledBitmap(bitmap,
+            return Bitmap.createScaledBitmap(
+                    bitmap,
                     (int) Math.ceil(bitmap.getWidth() * scaleRatio),
                     (int) Math.ceil(bitmap.getHeight() * scaleRatio),
                     false);
         }
+
     }
 
     /**
@@ -961,9 +466,7 @@
          *
          * @param rgb the color in RGB888.
          * @param hsl HSL representation of the color.
-         *
          * @return true if the color is allowed, false if not.
-         *
          * @see Palette.Builder#addFilter(Palette.Filter)
          */
         boolean isAllowed(int rgb, float[] hsl);
@@ -1004,3 +507,4 @@
         }
     };
 }
+
diff --git a/core/java/com/android/internal/graphics/palette/Quantizer.java b/core/java/com/android/internal/graphics/palette/Quantizer.java
index db60f2e..a219ea3 100644
--- a/core/java/com/android/internal/graphics/palette/Quantizer.java
+++ b/core/java/com/android/internal/graphics/palette/Quantizer.java
@@ -22,6 +22,15 @@
  * Definition of an algorithm that receives pixels and outputs a list of colors.
  */
 public interface Quantizer {
-    void quantize(final int[] pixels, final int maxColors, final Palette.Filter[] filters);
+    /**
+     * Create colors representative of the colors present in pixels.
+     * @param pixels Set of ARGB representation of a color.
+     * @param maxColors number of colors to generate
+     */
+    void quantize(int[] pixels, int maxColors);
+
+    /**
+     * List of colors generated by previous call to quantize.
+     */
     List<Palette.Swatch> getQuantizedColors();
 }
diff --git a/core/java/com/android/internal/graphics/palette/Target.java b/core/java/com/android/internal/graphics/palette/Target.java
index 0540d80..96e7faa 100644
--- a/core/java/com/android/internal/graphics/palette/Target.java
+++ b/core/java/com/android/internal/graphics/palette/Target.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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.
@@ -16,368 +16,234 @@
 
 package com.android.internal.graphics.palette;
 
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
 import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
 
 /**
- * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/graphics/Target.java
- *
- * A class which allows custom selection of colors in a {@link Palette}'s generation. Instances
- * can be created via the {@link android.support.v7.graphics.Target.Builder} class.
- *
- * <p>To use the target, use the {@link Palette.Builder#addTarget(Target)} API when building a
- * Palette.</p>
+ * A class which allows custom selection of colors in a {@link Palette}'s generation. Instances can
+ * be created via the {@link Builder} class.
  */
+
 public final class Target {
+    private static final float WEIGHT_CHROMA = 0.5f;
+    private static final float WEIGHT_RELATIVE_LUMINANCE = 0.5f;
+    private static final float WEIGHT_POPULATION = 0.3f;
+    private static final float WEIGHT_HUE = 0.2f;
 
-    private static final float TARGET_DARK_LUMA = 0.26f;
-    private static final float MAX_DARK_LUMA = 0.45f;
+    // Arbitrarily chosen, except max - CAM16 chroma has a ceiling of 130, based on unit testing.
+    private static final float DEFAULT_CHROMA_MIN = 0.f;
+    private static final float DEFAULT_CHROMA_MAX = 130.f;
+    private static final float DEFAULT_CHROMA_TARGET = 30.f;
 
-    private static final float MIN_LIGHT_LUMA = 0.55f;
-    private static final float TARGET_LIGHT_LUMA = 0.74f;
-
-    private static final float MIN_NORMAL_LUMA = 0.3f;
-    private static final float TARGET_NORMAL_LUMA = 0.5f;
-    private static final float MAX_NORMAL_LUMA = 0.7f;
-
-    private static final float TARGET_MUTED_SATURATION = 0.3f;
-    private static final float MAX_MUTED_SATURATION = 0.4f;
-
-    private static final float TARGET_VIBRANT_SATURATION = 1f;
-    private static final float MIN_VIBRANT_SATURATION = 0.35f;
-
-    private static final float WEIGHT_SATURATION = 0.24f;
-    private static final float WEIGHT_LUMA = 0.52f;
-    private static final float WEIGHT_POPULATION = 0.24f;
-
-    static final int INDEX_MIN = 0;
-    static final int INDEX_TARGET = 1;
-    static final int INDEX_MAX = 2;
-
-    static final int INDEX_WEIGHT_SAT = 0;
-    static final int INDEX_WEIGHT_LUMA = 1;
-    static final int INDEX_WEIGHT_POP = 2;
-
-    /**
-     * A target which has the characteristics of a vibrant color which is light in luminance.
-     */
-    public static final Target LIGHT_VIBRANT;
-
-    /**
-     * A target which has the characteristics of a vibrant color which is neither light or dark.
-     */
-    public static final Target VIBRANT;
-
-    /**
-     * A target which has the characteristics of a vibrant color which is dark in luminance.
-     */
-    public static final Target DARK_VIBRANT;
-
-    /**
-     * A target which has the characteristics of a muted color which is light in luminance.
-     */
-    public static final Target LIGHT_MUTED;
-
-    /**
-     * A target which has the characteristics of a muted color which is neither light or dark.
-     */
-    public static final Target MUTED;
-
-    /**
-     * A target which has the characteristics of a muted color which is dark in luminance.
-     */
-    public static final Target DARK_MUTED;
-
-    static {
-        LIGHT_VIBRANT = new Target();
-        setDefaultLightLightnessValues(LIGHT_VIBRANT);
-        setDefaultVibrantSaturationValues(LIGHT_VIBRANT);
-
-        VIBRANT = new Target();
-        setDefaultNormalLightnessValues(VIBRANT);
-        setDefaultVibrantSaturationValues(VIBRANT);
-
-        DARK_VIBRANT = new Target();
-        setDefaultDarkLightnessValues(DARK_VIBRANT);
-        setDefaultVibrantSaturationValues(DARK_VIBRANT);
-
-        LIGHT_MUTED = new Target();
-        setDefaultLightLightnessValues(LIGHT_MUTED);
-        setDefaultMutedSaturationValues(LIGHT_MUTED);
-
-        MUTED = new Target();
-        setDefaultNormalLightnessValues(MUTED);
-        setDefaultMutedSaturationValues(MUTED);
-
-        DARK_MUTED = new Target();
-        setDefaultDarkLightnessValues(DARK_MUTED);
-        setDefaultMutedSaturationValues(DARK_MUTED);
-    }
-
-    final float[] mSaturationTargets = new float[3];
-    final float[] mLightnessTargets = new float[3];
-    final float[] mWeights = new float[3];
-    boolean mIsExclusive = true; // default to true
+    private float mTargetRelativeLuminance = -1.0f;
+    private float mChromaWeight;
+    private float mChromaTarget;
+    private float mChromaMin;
+    private float mChromaMax;
+    private float mRelativeLuminanceWeight;
+    private float mPopulationWeight;
+    private float mHueWeight;
+    private float mTargetHue;
 
     Target() {
-        setTargetDefaultValues(mSaturationTargets);
-        setTargetDefaultValues(mLightnessTargets);
-        setDefaultWeights();
+        mChromaMax = DEFAULT_CHROMA_MAX;
+        mChromaMin = DEFAULT_CHROMA_MIN;
+        mChromaTarget = DEFAULT_CHROMA_TARGET;
+        mChromaWeight =   WEIGHT_CHROMA;
+        mRelativeLuminanceWeight = WEIGHT_RELATIVE_LUMINANCE;
+        mPopulationWeight = WEIGHT_POPULATION;
+        mHueWeight = WEIGHT_HUE;
     }
 
-    Target(Target from) {
-        System.arraycopy(from.mSaturationTargets, 0, mSaturationTargets, 0,
-                mSaturationTargets.length);
-        System.arraycopy(from.mLightnessTargets, 0, mLightnessTargets, 0,
-                mLightnessTargets.length);
-        System.arraycopy(from.mWeights, 0, mWeights, 0, mWeights.length);
+    Target(@NonNull Target from) {
+        mTargetRelativeLuminance = from.mTargetRelativeLuminance;
+        mChromaWeight = from.mChromaWeight;
+        mRelativeLuminanceWeight = from.mRelativeLuminanceWeight;
+        mPopulationWeight = from.mPopulationWeight;
+        mHueWeight = from.mHueWeight;
+        mChromaTarget = from.mChromaTarget;
+        mChromaMin = from.mChromaMin;
+        mChromaMax = from.mChromaMax;
+    }
+
+    /** The relative luminance value for this target. */
+    @FloatRange(from = 0, to = 100)
+    public float getTargetRelativeLuminance() {
+        return mTargetRelativeLuminance;
+    }
+
+    /** The relative luminance value for this target. */
+    @FloatRange(from = 0, to = 100)
+    public float getTargetPerceptualLuminance() {
+        return Contrast.yToLstar(mTargetRelativeLuminance);
+    }
+
+    /** The minimum chroma value for this target. */
+    @FloatRange(from = 0, to = 100)
+    public float getMinimumChroma() {
+        return mChromaMin;
+    }
+
+    /** The target chroma value for this target. */
+    @FloatRange(from = 0, to = 100)
+    public float getTargetChroma() {
+        return mChromaTarget;
+    }
+
+    /** The maximum chroma value for this target. */
+    @FloatRange(from = 0, to = 130)
+    public float getMaximumChroma() {
+        return mChromaMax;
+    }
+
+    /** The target hue value for this target. */
+    @FloatRange(from = 0, to = 100)
+    public float getTargetHue() {
+        return mTargetHue;
     }
 
     /**
-     * The minimum saturation value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getMinimumSaturation() {
-        return mSaturationTargets[INDEX_MIN];
-    }
-
-    /**
-     * The target saturation value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getTargetSaturation() {
-        return mSaturationTargets[INDEX_TARGET];
-    }
-
-    /**
-     * The maximum saturation value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getMaximumSaturation() {
-        return mSaturationTargets[INDEX_MAX];
-    }
-
-    /**
-     * The minimum lightness value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getMinimumLightness() {
-        return mLightnessTargets[INDEX_MIN];
-    }
-
-    /**
-     * The target lightness value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getTargetLightness() {
-        return mLightnessTargets[INDEX_TARGET];
-    }
-
-    /**
-     * The maximum lightness value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getMaximumLightness() {
-        return mLightnessTargets[INDEX_MAX];
-    }
-
-    /**
-     * Returns the weight of importance that this target places on a color's saturation within
-     * the image.
+     * Returns the weight of importance that this target places on a color's chroma within the
+     * image.
      *
      * <p>The larger the weight, relative to the other weights, the more important that a color
-     * being close to the target value has on selection.</p>
+     * being
+     * close to the target value has on selection.
      *
-     * @see #getTargetSaturation()
+     * @see #getTargetChroma()
      */
-    public float getSaturationWeight() {
-        return mWeights[INDEX_WEIGHT_SAT];
+    public float getChromaWeight() {
+        return mChromaWeight;
     }
 
     /**
-     * Returns the weight of importance that this target places on a color's lightness within
-     * the image.
+     * Returns the weight of importance that this target places on a color's lightness within the
+     * image.
      *
      * <p>The larger the weight, relative to the other weights, the more important that a color
-     * being close to the target value has on selection.</p>
+     * being
+     * close to the target value has on selection.
      *
-     * @see #getTargetLightness()
+     * @see #getTargetRelativeLuminance()
      */
     public float getLightnessWeight() {
-        return mWeights[INDEX_WEIGHT_LUMA];
+        return mRelativeLuminanceWeight;
     }
 
     /**
-     * Returns the weight of importance that this target places on a color's population within
-     * the image.
+     * Returns the weight of importance that this target places on a color's population within the
+     * image.
      *
-     * <p>The larger the weight, relative to the other weights, the more important that a
-     * color's population being close to the most populous has on selection.</p>
+     * <p>The larger the weight, relative to the other weights, the more important that a color's
+     * population being close to the most populous has on selection.
      */
     public float getPopulationWeight() {
-        return mWeights[INDEX_WEIGHT_POP];
+        return mPopulationWeight;
     }
 
     /**
-     * Returns whether any color selected for this target is exclusive for this target only.
+     * Returns the weight of importance that this target places on a color's hue.
      *
-     * <p>If false, then the color can be selected for other targets.</p>
+     * <p>The larger the weight, relative to the other weights, the more important that a color's
+     * hue being close to the desired hue has on selection.
      */
-    public boolean isExclusive() {
-        return mIsExclusive;
+    public float getHueWeight() {
+        return mHueWeight;
     }
 
-    private static void setTargetDefaultValues(final float[] values) {
-        values[INDEX_MIN] = 0f;
-        values[INDEX_TARGET] = 0.5f;
-        values[INDEX_MAX] = 1f;
-    }
 
-    private void setDefaultWeights() {
-        mWeights[INDEX_WEIGHT_SAT] = WEIGHT_SATURATION;
-        mWeights[INDEX_WEIGHT_LUMA] = WEIGHT_LUMA;
-        mWeights[INDEX_WEIGHT_POP] = WEIGHT_POPULATION;
-    }
-
-    void normalizeWeights() {
-        float sum = 0;
-        for (int i = 0, z = mWeights.length; i < z; i++) {
-            float weight = mWeights[i];
-            if (weight > 0) {
-                sum += weight;
-            }
-        }
-        if (sum != 0) {
-            for (int i = 0, z = mWeights.length; i < z; i++) {
-                if (mWeights[i] > 0) {
-                    mWeights[i] /= sum;
-                }
-            }
-        }
-    }
-
-    private static void setDefaultDarkLightnessValues(Target target) {
-        target.mLightnessTargets[INDEX_TARGET] = TARGET_DARK_LUMA;
-        target.mLightnessTargets[INDEX_MAX] = MAX_DARK_LUMA;
-    }
-
-    private static void setDefaultNormalLightnessValues(Target target) {
-        target.mLightnessTargets[INDEX_MIN] = MIN_NORMAL_LUMA;
-        target.mLightnessTargets[INDEX_TARGET] = TARGET_NORMAL_LUMA;
-        target.mLightnessTargets[INDEX_MAX] = MAX_NORMAL_LUMA;
-    }
-
-    private static void setDefaultLightLightnessValues(Target target) {
-        target.mLightnessTargets[INDEX_MIN] = MIN_LIGHT_LUMA;
-        target.mLightnessTargets[INDEX_TARGET] = TARGET_LIGHT_LUMA;
-    }
-
-    private static void setDefaultVibrantSaturationValues(Target target) {
-        target.mSaturationTargets[INDEX_MIN] = MIN_VIBRANT_SATURATION;
-        target.mSaturationTargets[INDEX_TARGET] = TARGET_VIBRANT_SATURATION;
-    }
-
-    private static void setDefaultMutedSaturationValues(Target target) {
-        target.mSaturationTargets[INDEX_TARGET] = TARGET_MUTED_SATURATION;
-        target.mSaturationTargets[INDEX_MAX] = MAX_MUTED_SATURATION;
-    }
-
-    /**
-     * Builder class for generating custom {@link Target} instances.
-     */
-    public final static class Builder {
+    /** Builder class for generating custom {@link Target} instances. */
+    public static class Builder {
         private final Target mTarget;
 
-        /**
-         * Create a new {@link Target} builder from scratch.
-         */
+        /** Create a new {@link Target} builder from scratch. */
         public Builder() {
             mTarget = new Target();
         }
 
-        /**
-         * Create a new builder based on an existing {@link Target}.
-         */
-        public Builder(Target target) {
+        /** Create a new builder based on an existing {@link Target}. */
+        public Builder(@NonNull Target target) {
             mTarget = new Target(target);
         }
 
-        /**
-         * Set the minimum saturation value for this target.
-         */
-        public Target.Builder setMinimumSaturation(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mSaturationTargets[INDEX_MIN] = value;
+        /** Set the minimum chroma value for this target. */
+        @NonNull
+        public Builder setMinimumChroma(@FloatRange(from = 0, to = 100) float value) {
+            mTarget.mChromaMin = value;
+            return this;
+        }
+
+        /** Set the target/ideal chroma value for this target. */
+        @NonNull
+        public Builder setTargetChroma(@FloatRange(from = 0, to = 100) float value) {
+            mTarget.mChromaTarget = value;
+            return this;
+        }
+
+        /** Set the maximum chroma value for this target. */
+        @NonNull
+        public Builder setMaximumChroma(@FloatRange(from = 0, to = 100) float value) {
+            mTarget.mChromaMax = value;
+            return this;
+        }
+
+        /** Set the minimum lightness value for this target, using Y in XYZ color space. */
+        @NonNull
+        public Builder setTargetRelativeLuminance(@FloatRange(from = 0, to = 100) float value) {
+            mTarget.mTargetRelativeLuminance = value;
+            return this;
+        }
+
+        /** Set the minimum lightness value for this target, using L* in LAB color space. */
+        @NonNull
+        public Builder setTargetPerceptualLuminance(@FloatRange(from = 0, to = 100) float value) {
+            mTarget.mTargetRelativeLuminance = Contrast.lstarToY(value);
             return this;
         }
 
         /**
-         * Set the target/ideal saturation value for this target.
+         * Set the hue desired from the target. This hue is not enforced, the only consequence
+         * is points will be awarded to seed colors the closer they are to this hue.
          */
-        public Target.Builder setTargetSaturation(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mSaturationTargets[INDEX_TARGET] = value;
+        @NonNull
+        public Builder setTargetHue(@IntRange(from = 0, to = 360) int hue) {
+            mTarget.mTargetHue = hue;
+            return this;
+        }
+
+        /** Sets lightness value for this target. */
+        @NonNull
+        public Builder setContrastRatio(
+                @FloatRange(from = 1, to = 21) float value,
+                @FloatRange(from = 0, to = 100) float relativeLuminance) {
+            float counterpartY = relativeLuminance;
+            float lstar = Contrast.yToLstar(counterpartY);
+
+            float targetY;
+            if (lstar < 50) {
+                targetY = Contrast.lighterY(counterpartY, value);
+            } else {
+                targetY = Contrast.darkerY(counterpartY, value);
+            }
+            mTarget.mTargetRelativeLuminance = targetY;
             return this;
         }
 
         /**
-         * Set the maximum saturation value for this target.
-         */
-        public Target.Builder setMaximumSaturation(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mSaturationTargets[INDEX_MAX] = value;
-            return this;
-        }
-
-        /**
-         * Set the minimum lightness value for this target.
-         */
-        public Target.Builder setMinimumLightness(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mLightnessTargets[INDEX_MIN] = value;
-            return this;
-        }
-
-        /**
-         * Set the target/ideal lightness value for this target.
-         */
-        public Target.Builder setTargetLightness(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mLightnessTargets[INDEX_TARGET] = value;
-            return this;
-        }
-
-        /**
-         * Set the maximum lightness value for this target.
-         */
-        public Target.Builder setMaximumLightness(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mLightnessTargets[INDEX_MAX] = value;
-            return this;
-        }
-
-        /**
-         * Set the weight of importance that this target will place on saturation values.
+         * Set the weight of importance that this target will place on chroma values.
          *
          * <p>The larger the weight, relative to the other weights, the more important that a color
-         * being close to the target value has on selection.</p>
+         * being close to the target value has on selection.
          *
-         * <p>A weight of 0 means that it has no weight, and thus has no
-         * bearing on the selection.</p>
+         * <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection.
          *
-         * @see #setTargetSaturation(float)
+         * @see #setTargetChroma(float)
          */
-        public Target.Builder setSaturationWeight(@FloatRange(from = 0) float weight) {
-            mTarget.mWeights[INDEX_WEIGHT_SAT] = weight;
+        @NonNull
+        public Builder setChromaWeight(@FloatRange(from = 0) float weight) {
+            mTarget.mChromaWeight = weight;
             return this;
         }
 
@@ -385,51 +251,40 @@
          * Set the weight of importance that this target will place on lightness values.
          *
          * <p>The larger the weight, relative to the other weights, the more important that a color
-         * being close to the target value has on selection.</p>
+         * being close to the target value has on selection.
          *
-         * <p>A weight of 0 means that it has no weight, and thus has no
-         * bearing on the selection.</p>
+         * <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection.
          *
-         * @see #setTargetLightness(float)
+         * @see #setTargetRelativeLuminance(float)
          */
-        public Target.Builder setLightnessWeight(@FloatRange(from = 0) float weight) {
-            mTarget.mWeights[INDEX_WEIGHT_LUMA] = weight;
+        @NonNull
+        public Builder setLightnessWeight(@FloatRange(from = 0) float weight) {
+            mTarget.mRelativeLuminanceWeight = weight;
             return this;
         }
 
         /**
          * Set the weight of importance that this target will place on a color's population within
-         * the image.
+         * the
+         * image.
          *
          * <p>The larger the weight, relative to the other weights, the more important that a
-         * color's population being close to the most populous has on selection.</p>
+         * color's
+         * population being close to the most populous has on selection.
          *
-         * <p>A weight of 0 means that it has no weight, and thus has no
-         * bearing on the selection.</p>
+         * <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection.
          */
-        public Target.Builder setPopulationWeight(@FloatRange(from = 0) float weight) {
-            mTarget.mWeights[INDEX_WEIGHT_POP] = weight;
+        @NonNull
+        public Builder setPopulationWeight(@FloatRange(from = 0) float weight) {
+            mTarget.mPopulationWeight = weight;
             return this;
         }
 
-        /**
-         * Set whether any color selected for this target is exclusive to this target only.
-         * Defaults to true.
-         *
-         * @param exclusive true if any the color is exclusive to this target, or false is the
-         *                  color can be selected for other targets.
-         */
-        public Target.Builder setExclusive(boolean exclusive) {
-            mTarget.mIsExclusive = exclusive;
-            return this;
-        }
 
-        /**
-         * Builds and returns the resulting {@link Target}.
-         */
+        /** Builds and returns the resulting {@link Target}. */
+        @NonNull
         public Target build() {
             return mTarget;
         }
     }
-
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java
index b035535..d791f7b 100644
--- a/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java
+++ b/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java
@@ -70,10 +70,9 @@
      *
      * @param pixels Pixels to quantize.
      * @param maxColors Maximum number of clusters to extract.
-     * @param filters Colors that should be ignored
      */
     @Override
-    public void quantize(int[] pixels, int maxColors, Palette.Filter[] filters) {
+    public void quantize(int[] pixels, int maxColors) {
         // Start by converting all colors to HSL.
         // HLS is way more meaningful for clustering than RGB.
         final float[] hsl = {0, 0, 0};
@@ -111,16 +110,18 @@
 
         // Convert data to final format, de-normalizing the hue.
         mQuantizedColors = new ArrayList<>();
+        float[] mHsl = new float[3];
         for (KMeans.Mean mean : optimalMeans) {
             if (mean.getItems().size() == 0) {
                 continue;
             }
             float[] centroid = mean.getCentroid();
-            mQuantizedColors.add(new Palette.Swatch(new float[]{
-                    centroid[0] * 360f,
-                    centroid[1],
-                    centroid[2]
-            }, mean.getItems().size()));
+
+            mHsl[0] = centroid[0] * 360f;
+            mHsl[1] = centroid[1];
+            mHsl[2] = centroid[2];
+            int color = ColorUtils.HSLToColor(mHsl);
+            mQuantizedColors.add(new Palette.Swatch(color, mean.getItems().size()));
         }
     }
 
diff --git a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
new file mode 100644
index 0000000..a87a34f
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.graphics.palette;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * A color quantizer based on the Kmeans algorithm.
+ *
+ * This is an implementation of Kmeans based on Celebi's 2011 paper,
+ * "Improving the Performance of K-Means for Color Quantization". In the paper, this algorithm is
+ * referred to as "WSMeans", or, "Weighted Square Means" The main advantages of this Kmeans
+ * implementation are taking advantage of triangle properties to avoid distance calculations, as
+ * well as indexing colors by their count, thus minimizing the number of points to move around.
+ *
+ * Celebi's paper also stabilizes results and guarantees high quality by using starting centroids
+ * from Wu's quantization algorithm. See CelebiQuantizer for more info.
+ */
+public class WSMeansQuantizer implements Quantizer {
+    Mean[] mMeans;
+    private final Map<Integer, Integer> mCountByColor = new HashMap<>();
+    private final Map<Integer, Integer> mMeanIndexByColor = new HashMap<>();
+    private final Set<Integer> mUniqueColors = new HashSet<>();
+    private final List<Palette.Swatch> mSwatches = new ArrayList<>();
+    private final CentroidProvider mCentroidProvider;
+
+    public WSMeansQuantizer(
+            float[][] means, CentroidProvider centroidProvider, int[] pixels, int maxColors) {
+        if (pixels == null) {
+            pixels = new int[]{};
+        }
+        mCentroidProvider = centroidProvider;
+        mMeans = new Mean[maxColors];
+        for (int i = 0; i < means.length; i++) {
+            mMeans[i] = new Mean(means[i]);
+        }
+
+        if (maxColors > means.length) {
+            int randomMeansToCreate = maxColors - means.length;
+            for (int i = 0; i < randomMeansToCreate; i++) {
+                mMeans[means.length + i] = new Mean(100);
+            }
+        }
+
+        for (int pixel : pixels) {
+            Integer currentCount = mCountByColor.get(pixel);
+            if (currentCount == null) {
+                currentCount = 0;
+                mUniqueColors.add(pixel);
+            }
+            mCountByColor.put(pixel, currentCount + 1);
+        }
+        for (int color : mUniqueColors) {
+            int closestMeanIndex = -1;
+            double closestMeanDistance = -1;
+            float[] centroid = mCentroidProvider.getCentroid(color);
+            for (int i = 0; i < mMeans.length; i++) {
+                double distance = mCentroidProvider.distance(centroid, mMeans[i].center);
+                if (closestMeanIndex == -1 || distance < closestMeanDistance) {
+                    closestMeanIndex = i;
+                    closestMeanDistance = distance;
+                }
+            }
+            mMeanIndexByColor.put(color, closestMeanIndex);
+        }
+
+        if (pixels.length == 0) {
+            return;
+        }
+
+        predict(maxColors, 0);
+    }
+
+    /** Create starting centroids for K-means from a set of colors. */
+    public static float[][] createStartingCentroids(CentroidProvider centroidProvider,
+            List<Palette.Swatch> swatches) {
+        float[][] startingCentroids = new float[swatches.size()][];
+        for (int i = 0; i < swatches.size(); i++) {
+            startingCentroids[i] = centroidProvider.getCentroid(swatches.get(i).getInt());
+        }
+        return startingCentroids;
+    }
+
+    /** Create random starting centroids for K-means. */
+    public static float[][] randomMeans(int maxColors, int upperBound) {
+        float[][] means = new float[maxColors][];
+        for (int i = 0; i < maxColors; i++) {
+            means[i] = new Mean(upperBound).center;
+        }
+        return means;
+    }
+
+
+    @Override
+    public void quantize(int[] pixels, int maxColors) {
+
+    }
+
+    @Override
+    public List<Palette.Swatch> getQuantizedColors() {
+        return mSwatches;
+    }
+
+    private void predict(int maxColors, int iterationsCompleted) {
+        double[][] centroidDistance = new double[maxColors][maxColors];
+        for (int i = 0; i <= maxColors; i++) {
+            for (int j = i + 1; j < maxColors; j++) {
+                float[] meanI = mMeans[i].center;
+                float[] meanJ = mMeans[j].center;
+                double distance = mCentroidProvider.distance(meanI, meanJ);
+                centroidDistance[i][j] = distance;
+                centroidDistance[j][i] = distance;
+            }
+        }
+
+        // Construct a K×K matrix M in which row i is a permutation of
+        // 1,2,…,K that represents the clusters in increasing order of
+        // distance of their centers from ci;
+        int[][] distanceMatrix = new int[maxColors][maxColors];
+        for (int i = 0; i < maxColors; i++) {
+            double[] distancesFromIToAnotherMean = centroidDistance[i];
+            double[] sortedByDistanceAscending = distancesFromIToAnotherMean.clone();
+            Arrays.sort(sortedByDistanceAscending);
+            int[] outputRow = new int[maxColors];
+            for (int j = 0; j < maxColors; j++) {
+                outputRow[j] = findIndex(distancesFromIToAnotherMean, sortedByDistanceAscending[j]);
+            }
+            distanceMatrix[i] = outputRow;
+        }
+
+        //   for (i=1;i≤N′;i=i+ 1) do
+        //   Let Sp be the cluster that xi was assigned to in the previous
+        //   iteration;
+        //   p=m[i];
+        //   min_dist=prev_dist=jjxi−cpjj2;
+        boolean anyColorMoved = false;
+        for (int intColor : mUniqueColors) {
+            float[] color = mCentroidProvider.getCentroid(intColor);
+            int indexOfCurrentMean = mMeanIndexByColor.get(intColor);
+            Mean currentMean = mMeans[indexOfCurrentMean];
+            double minDistance = mCentroidProvider.distance(color, currentMean.center);
+            for (int j = 1; j < maxColors; j++) {
+                int indexOfClusterFromCurrentToJ = distanceMatrix[indexOfCurrentMean][j];
+                double distanceBetweenJAndCurrent =
+                        centroidDistance[indexOfCurrentMean][indexOfClusterFromCurrentToJ];
+                if (distanceBetweenJAndCurrent >= (4 * minDistance)) {
+                    break;
+                }
+                double distanceBetweenJAndColor = mCentroidProvider.distance(mMeans[j].center,
+                        color);
+                if (distanceBetweenJAndColor < minDistance) {
+                    minDistance = distanceBetweenJAndColor;
+                    mMeanIndexByColor.remove(intColor);
+                    mMeanIndexByColor.put(intColor, j);
+                    anyColorMoved = true;
+                }
+            }
+        }
+
+        List<MeanBucket> buckets = new ArrayList<>();
+        for (int i = 0; i < maxColors; i++) {
+            buckets.add(new MeanBucket());
+        }
+
+        for (int intColor : mUniqueColors) {
+            int meanIndex = mMeanIndexByColor.get(intColor);
+            MeanBucket meanBucket = buckets.get(meanIndex);
+            meanBucket.add(mCentroidProvider.getCentroid(intColor), intColor,
+                    mCountByColor.get(intColor));
+        }
+
+        List<Palette.Swatch> swatches = new ArrayList<>();
+        boolean done = !anyColorMoved && iterationsCompleted > 0 || iterationsCompleted >= 100;
+        if (done) {
+            for (int i = 0; i < buckets.size(); i++) {
+                MeanBucket a = buckets.get(i);
+                if (a.mCount <= 0) {
+                    continue;
+                }
+                List<MeanBucket> bucketsToMerge = new ArrayList<>();
+                for (int j = i + 1; j < buckets.size(); j++) {
+                    MeanBucket b = buckets.get(j);
+                    if (b.mCount == 0) {
+                        continue;
+                    }
+                    float[] bCentroid = b.getCentroid();
+                    assert (a.mCount > 0);
+                    assert (a.getCentroid() != null);
+
+                    assert (bCentroid != null);
+                    if (mCentroidProvider.distance(a.getCentroid(), b.getCentroid()) < 5) {
+                        bucketsToMerge.add(b);
+                    }
+                }
+
+                for (MeanBucket bucketToMerge : bucketsToMerge) {
+                    float[] centroid = bucketToMerge.getCentroid();
+                    a.add(centroid, mCentroidProvider.getColor(centroid), bucketToMerge.mCount);
+                    buckets.remove(bucketToMerge);
+                }
+            }
+
+            for (MeanBucket bucket : buckets) {
+                float[] centroid = bucket.getCentroid();
+                if (centroid == null) {
+                    continue;
+                }
+
+                int rgb = mCentroidProvider.getColor(centroid);
+                swatches.add(new Palette.Swatch(rgb, bucket.mCount));
+                mSwatches.clear();
+                mSwatches.addAll(swatches);
+            }
+        } else {
+            List<MeanBucket> emptyBuckets = new ArrayList<>();
+            for (int i = 0; i < buckets.size(); i++) {
+                MeanBucket bucket = buckets.get(i);
+                if ((bucket.getCentroid() == null) || (bucket.mCount == 0)) {
+                    emptyBuckets.add(bucket);
+                    for (Integer color : mUniqueColors) {
+                        int meanIndex = mMeanIndexByColor.get(color);
+                        if (meanIndex > i) {
+                            mMeanIndexByColor.put(color, meanIndex--);
+                        }
+                    }
+                }
+            }
+
+            Mean[] newMeans = new Mean[buckets.size()];
+            for (int i = 0; i < buckets.size(); i++) {
+                float[] centroid = buckets.get(i).getCentroid();
+                newMeans[i] = new Mean(centroid);
+            }
+
+            predict(buckets.size(), iterationsCompleted + 1);
+        }
+
+    }
+
+    private static int findIndex(double[] list, double element) {
+        for (int i = 0; i < list.length; i++) {
+            if (list[i] == element) {
+                return i;
+            }
+        }
+        throw new IllegalArgumentException("Element not in list");
+    }
+}
diff --git a/core/java/com/android/internal/graphics/palette/WuQuantizer.java b/core/java/com/android/internal/graphics/palette/WuQuantizer.java
new file mode 100644
index 0000000..01e45f6
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/WuQuantizer.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.graphics.palette;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+// All reference Wu implementations are based on the original C code by Wu.
+// Comments on methods are the same as in the original implementation, and the comment below
+// is the original class header.
+
+/**
+ * Wu's Color Quantizer (v. 2) (see Graphics Gems vol. II, pp. 126-133) Author: Xiaolin Wu
+ *
+ * <p>Algorithm: Greedy orthogonal bipartition of RGB space for variance minimization aided by
+ * inclusion-exclusion tricks. For speed no nearest neighbor search is done. Slightly better
+ * performance can be expected by more sophisticated but more expensive versions.
+ */
+public class WuQuantizer implements Quantizer {
+    private static final int MAX_COLORS = 256;
+    private static final int RED = 2;
+    private static final int GREEN = 1;
+    private static final int BLUE = 0;
+
+    private static final int QUANT_SIZE = 33;
+    private final List<Palette.Swatch> mSwatches = new ArrayList<>();
+
+    @Override
+    public List<Palette.Swatch> getQuantizedColors() {
+        return mSwatches;
+    }
+
+    private static final class Box {
+        int mR0; /* min value, exclusive */
+        int mR1; /* max value, inclusive */
+        int mG0;
+        int mG1;
+        int mB0;
+        int mB1;
+        int mVol;
+    }
+
+    private final int mSize; /* image size, in bytes. */
+    private int mMaxColors;
+    private int[] mQadd;
+    private final int[] mPixels;
+
+    private final double[][][] mM2 = new double[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE];
+    private final long[][][] mWt = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE];
+    private final long[][][] mMr = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE];
+    private final long[][][] mMg = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE];
+    private final long[][][] mMb = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE];
+
+    public WuQuantizer(int[] pixels, int maxColorCount) {
+        if (pixels == null) {
+            pixels = new int[]{};
+        }
+        this.mPixels = pixels;
+        this.mSize = pixels.length;
+    }
+
+    @Override
+    public void quantize(int[] colors, int maxColorCount) {
+        // All of the sample Wu implementations are reimplementations of a snippet of C code from
+        // the early 90s. They all cap the maximum # of colors at 256, and it is impossible to tell
+        // if this is a requirement, a consequence of QUANT_SIZE, or arbitrary.
+        this.mMaxColors = Math.min(MAX_COLORS, maxColorCount);
+        Box[] cube = new Box[mMaxColors];
+        int red, green, blue;
+
+        int next, i, k;
+        long weight;
+        double[] vv = new double[mMaxColors];
+        double temp;
+
+        compute3DHistogram(mWt, mMr, mMg, mMb, mM2);
+        computeMoments(mWt, mMr, mMg, mMb, mM2);
+
+        for (i = 0; i < mMaxColors; i++) {
+            cube[i] = new Box();
+        }
+
+        cube[0].mR0 = cube[0].mG0 = cube[0].mB0 = 0;
+        cube[0].mR1 = cube[0].mG1 = cube[0].mB1 = QUANT_SIZE - 1;
+        next = 0;
+
+        for (i = 1; i < mMaxColors; ++i) {
+            if (cut(cube[next], cube[i])) {
+                vv[next] = (cube[next].mVol > 1) ? getVariance(cube[next]) : 0.0f;
+                vv[i] = (cube[i].mVol > 1) ? getVariance(cube[i]) : 0.0f;
+            } else {
+                vv[next] = 0.0f;
+                i--;
+            }
+            next = 0;
+            temp = vv[0];
+            for (k = 1; k <= i; ++k) {
+                if (vv[k] > temp) {
+                    temp = vv[k];
+                    next = k;
+                }
+            }
+            if (temp <= 0.0f) {
+                break;
+            }
+        }
+
+        for (k = 0; k < mMaxColors; ++k) {
+            weight = getVolume(cube[k], mWt);
+            if (weight > 0) {
+                red = (int) (getVolume(cube[k], mMr) / weight);
+                green = (int) (getVolume(cube[k], mMg) / weight);
+                blue = (int) (getVolume(cube[k], mMb) / weight);
+                colors[k] = ((red & 0x0ff) << 16) | ((green & 0x0ff) << 8) | (blue & 0x0ff);
+            } else {
+                colors[k] = 0;
+            }
+        }
+
+        int bitsPerPixel = 0;
+        while ((1 << bitsPerPixel) < mMaxColors) {
+            bitsPerPixel++;
+        }
+
+        List<Palette.Swatch> swatches = new ArrayList<>();
+        for (int l = 0; l < k; l++) {
+            int pixel = colors[l];
+            if (pixel == 0) {
+                continue;
+            }
+            swatches.add(new Palette.Swatch(pixel, 0));
+        }
+        mSwatches.clear();
+        mSwatches.addAll(swatches);
+    }
+
+    /* Histogram is in elements 1..HISTSIZE along each axis,
+     * element 0 is for base or marginal value
+     * NB: these must start out 0!
+     */
+    private void compute3DHistogram(
+            long[][][] vwt, long[][][] vmr, long[][][] vmg, long[][][] vmb, double[][][] m2) {
+        // build 3-D color histogram of counts, r/g/b, and c^2
+        int r, g, b;
+        int i;
+        int inr;
+        int ing;
+        int inb;
+        int[] table = new int[256];
+
+        for (i = 0; i < 256; i++) {
+            table[i] = i * i;
+        }
+
+        mQadd = new int[mSize];
+
+        for (i = 0; i < mSize; ++i) {
+            int rgb = mPixels[i];
+            // Skip less than opaque pixels. They're not meaningful in the context of palette
+            // generation for UI schemes.
+            if ((rgb >>> 24) < 0xff) {
+                continue;
+            }
+            r = ((rgb >> 16) & 0xff);
+            g = ((rgb >> 8) & 0xff);
+            b = (rgb & 0xff);
+            inr = (r >> 3) + 1;
+            ing = (g >> 3) + 1;
+            inb = (b >> 3) + 1;
+            mQadd[i] = (inr << 10) + (inr << 6) + inr + (ing << 5) + ing + inb;
+            /*[inr][ing][inb]*/
+            ++vwt[inr][ing][inb];
+            vmr[inr][ing][inb] += r;
+            vmg[inr][ing][inb] += g;
+            vmb[inr][ing][inb] += b;
+            m2[inr][ing][inb] += table[r] + table[g] + table[b];
+        }
+    }
+
+    /* At conclusion of the histogram step, we can interpret
+     *   wt[r][g][b] = sum over voxel of P(c)
+     *   mr[r][g][b] = sum over voxel of r*P(c)  ,  similarly for mg, mb
+     *   m2[r][g][b] = sum over voxel of c^2*P(c)
+     * Actually each of these should be divided by 'size' to give the usual
+     * interpretation of P() as ranging from 0 to 1, but we needn't do that here.
+     *
+     * We now convert histogram into moments so that we can rapidly calculate
+     * the sums of the above quantities over any desired box.
+     */
+    private void computeMoments(
+            long[][][] vwt, long[][][] vmr, long[][][] vmg, long[][][] vmb, double[][][] m2) {
+        /* compute cumulative moments. */
+        int i, r, g, b;
+        int line, line_r, line_g, line_b;
+        int[] area = new int[QUANT_SIZE];
+        int[] area_r = new int[QUANT_SIZE];
+        int[] area_g = new int[QUANT_SIZE];
+        int[] area_b = new int[QUANT_SIZE];
+        double line2;
+        double[] area2 = new double[QUANT_SIZE];
+
+        for (r = 1; r < QUANT_SIZE; ++r) {
+            for (i = 0; i < QUANT_SIZE; ++i) {
+                area2[i] = area[i] = area_r[i] = area_g[i] = area_b[i] = 0;
+            }
+            for (g = 1; g < QUANT_SIZE; ++g) {
+                line2 = line = line_r = line_g = line_b = 0;
+                for (b = 1; b < QUANT_SIZE; ++b) {
+                    line += vwt[r][g][b];
+                    line_r += vmr[r][g][b];
+                    line_g += vmg[r][g][b];
+                    line_b += vmb[r][g][b];
+                    line2 += m2[r][g][b];
+
+                    area[b] += line;
+                    area_r[b] += line_r;
+                    area_g[b] += line_g;
+                    area_b[b] += line_b;
+                    area2[b] += line2;
+
+                    vwt[r][g][b] = vwt[r - 1][g][b] + area[b];
+                    vmr[r][g][b] = vmr[r - 1][g][b] + area_r[b];
+                    vmg[r][g][b] = vmg[r - 1][g][b] + area_g[b];
+                    vmb[r][g][b] = vmb[r - 1][g][b] + area_b[b];
+                    m2[r][g][b] = m2[r - 1][g][b] + area2[b];
+                }
+            }
+        }
+    }
+
+    private long getVolume(Box cube, long[][][] mmt) {
+        /* Compute sum over a box of any given statistic */
+        return (mmt[cube.mR1][cube.mG1][cube.mB1]
+                - mmt[cube.mR1][cube.mG1][cube.mB0]
+                - mmt[cube.mR1][cube.mG0][cube.mB1]
+                + mmt[cube.mR1][cube.mG0][cube.mB0]
+                - mmt[cube.mR0][cube.mG1][cube.mB1]
+                + mmt[cube.mR0][cube.mG1][cube.mB0]
+                + mmt[cube.mR0][cube.mG0][cube.mB1]
+                - mmt[cube.mR0][cube.mG0][cube.mB0]);
+    }
+
+    /* The next two routines allow a slightly more efficient calculation
+     * of Vol() for a proposed subbox of a given box.  The sum of Top()
+     * and Bottom() is the Vol() of a subbox split in the given direction
+     * and with the specified new upper bound.
+     */
+    private long getBottom(Box cube, int dir, long[][][] mmt) {
+        /* Compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1 */
+        /* (depending on dir) */
+        switch (dir) {
+            case RED:
+                return (-mmt[cube.mR0][cube.mG1][cube.mB1]
+                        + mmt[cube.mR0][cube.mG1][cube.mB0]
+                        + mmt[cube.mR0][cube.mG0][cube.mB1]
+                        - mmt[cube.mR0][cube.mG0][cube.mB0]);
+            case GREEN:
+                return (-mmt[cube.mR1][cube.mG0][cube.mB1]
+                        + mmt[cube.mR1][cube.mG0][cube.mB0]
+                        + mmt[cube.mR0][cube.mG0][cube.mB1]
+                        - mmt[cube.mR0][cube.mG0][cube.mB0]);
+            case BLUE:
+                return (-mmt[cube.mR1][cube.mG1][cube.mB0]
+                        + mmt[cube.mR1][cube.mG0][cube.mB0]
+                        + mmt[cube.mR0][cube.mG1][cube.mB0]
+                        - mmt[cube.mR0][cube.mG0][cube.mB0]);
+            default:
+                return 0;
+        }
+    }
+
+    private long getTop(Box cube, int dir, int pos, long[][][] mmt) {
+        /* Compute remainder of Vol(cube, mmt), substituting pos for */
+        /* r1, g1, or b1 (depending on dir) */
+        switch (dir) {
+            case RED:
+                return (mmt[pos][cube.mG1][cube.mB1]
+                        - mmt[pos][cube.mG1][cube.mB0]
+                        - mmt[pos][cube.mG0][cube.mB1]
+                        + mmt[pos][cube.mG0][cube.mB0]);
+            case GREEN:
+                return (mmt[cube.mR1][pos][cube.mB1]
+                        - mmt[cube.mR1][pos][cube.mB0]
+                        - mmt[cube.mR0][pos][cube.mB1]
+                        + mmt[cube.mR0][pos][cube.mB0]);
+            case BLUE:
+                return (mmt[cube.mR1][cube.mG1][pos]
+                        - mmt[cube.mR1][cube.mG0][pos]
+                        - mmt[cube.mR0][cube.mG1][pos]
+                        + mmt[cube.mR0][cube.mG0][pos]);
+            default:
+                return 0;
+        }
+    }
+
+    private double getVariance(Box cube) {
+        /* Compute the weighted variance of a box */
+        /* NB: as with the raw statistics, this is really the variance * size */
+        double dr, dg, db, xx;
+        dr = getVolume(cube, mMr);
+        dg = getVolume(cube, mMg);
+        db = getVolume(cube, mMb);
+        xx =
+                mM2[cube.mR1][cube.mG1][cube.mB1]
+                        - mM2[cube.mR1][cube.mG1][cube.mB0]
+                        - mM2[cube.mR1][cube.mG0][cube.mB1]
+                        + mM2[cube.mR1][cube.mG0][cube.mB0]
+                        - mM2[cube.mR0][cube.mG1][cube.mB1]
+                        + mM2[cube.mR0][cube.mG1][cube.mB0]
+                        + mM2[cube.mR0][cube.mG0][cube.mB1]
+                        - mM2[cube.mR0][cube.mG0][cube.mB0];
+        return xx - (dr * dr + dg * dg + db * db) / getVolume(cube, mWt);
+    }
+
+    /* We want to minimize the sum of the variances of two subboxes.
+     * The sum(c^2) terms can be ignored since their sum over both subboxes
+     * is the same (the sum for the whole box) no matter where we split.
+     * The remaining terms have a minus sign in the variance formula,
+     * so we drop the minus sign and MAXIMIZE the sum of the two terms.
+     */
+    private double maximize(
+            Box cube,
+            int dir,
+            int first,
+            int last,
+            int[] cut,
+            long wholeR,
+            long wholeG,
+            long wholeB,
+            long wholeW) {
+        long half_r, half_g, half_b, half_w;
+        long base_r, base_g, base_b, base_w;
+        int i;
+        double temp, max;
+
+        base_r = getBottom(cube, dir, mMr);
+        base_g = getBottom(cube, dir, mMg);
+        base_b = getBottom(cube, dir, mMb);
+        base_w = getBottom(cube, dir, mWt);
+
+        max = 0.0f;
+        cut[0] = -1;
+
+        for (i = first; i < last; ++i) {
+            half_r = base_r + getTop(cube, dir, i, mMr);
+            half_g = base_g + getTop(cube, dir, i, mMg);
+            half_b = base_b + getTop(cube, dir, i, mMb);
+            half_w = base_w + getTop(cube, dir, i, mWt);
+            /* now half_x is sum over lower half of box, if split at i */
+            if (half_w == 0) /* subbox could be empty of pixels! */ {
+                continue; /* never split into an empty box */
+            }
+            temp = (half_r * half_r + half_g * half_g + half_b * half_b) / (double) half_w;
+            half_r = wholeR - half_r;
+            half_g = wholeG - half_g;
+            half_b = wholeB - half_b;
+            half_w = wholeW - half_w;
+            if (half_w == 0) /* subbox could be empty of pixels! */ {
+                continue; /* never split into an empty box */
+            }
+            temp += (half_r * half_r + half_g * half_g + half_b * half_b) / (double) half_w;
+
+            if (temp > max) {
+                max = temp;
+                cut[0] = i;
+            }
+        }
+
+        return max;
+    }
+
+    private boolean cut(Box set1, Box set2) {
+        int dir;
+        int[] cutr = new int[1];
+        int[] cutg = new int[1];
+        int[] cutb = new int[1];
+        double maxr, maxg, maxb;
+        long whole_r, whole_g, whole_b, whole_w;
+
+        whole_r = getVolume(set1, mMr);
+        whole_g = getVolume(set1, mMg);
+        whole_b = getVolume(set1, mMb);
+        whole_w = getVolume(set1, mWt);
+
+        maxr = maximize(set1, RED, set1.mR0 + 1, set1.mR1, cutr, whole_r, whole_g, whole_b,
+                whole_w);
+        maxg = maximize(set1, GREEN, set1.mG0 + 1, set1.mG1, cutg, whole_r, whole_g, whole_b,
+                whole_w);
+        maxb = maximize(set1, BLUE, set1.mB0 + 1, set1.mB1, cutb, whole_r, whole_g, whole_b,
+                whole_w);
+
+        if (maxr >= maxg && maxr >= maxb) {
+            dir = RED;
+            if (cutr[0] < 0) return false; /* can't split the box */
+        } else if (maxg >= maxr && maxg >= maxb) {
+            dir = GREEN;
+        } else {
+            dir = BLUE;
+        }
+
+        set2.mR1 = set1.mR1;
+        set2.mG1 = set1.mG1;
+        set2.mB1 = set1.mB1;
+
+        switch (dir) {
+            case RED:
+                set2.mR0 = set1.mR1 = cutr[0];
+                set2.mG0 = set1.mG0;
+                set2.mB0 = set1.mB0;
+                break;
+            case GREEN:
+                set2.mG0 = set1.mG1 = cutg[0];
+                set2.mR0 = set1.mR0;
+                set2.mB0 = set1.mB0;
+                break;
+            case BLUE:
+                set2.mB0 = set1.mB1 = cutb[0];
+                set2.mR0 = set1.mR0;
+                set2.mG0 = set1.mG0;
+                break;
+        }
+        set1.mVol = (set1.mR1 - set1.mR0) * (set1.mG1 - set1.mG0) * (set1.mB1 - set1.mB0);
+        set2.mVol = (set2.mR1 - set2.mR0) * (set2.mG1 - set2.mG0) * (set2.mB1 - set2.mB0);
+
+        return true;
+    }
+}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index e82cc73..342456a 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -25,6 +25,9 @@
 import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
 import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
 
+import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED;
+import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.HardwareRendererObserver;
@@ -72,6 +75,7 @@
     private long mEndVsyncId = INVALID_ID;
     private boolean mMetricsFinalized;
     private boolean mCancelled = false;
+    private FrameTrackerListener mListener;
 
     private static class JankInfo {
         long frameVsyncId;
@@ -109,7 +113,7 @@
             @NonNull SurfaceControlWrapper surfaceControlWrapper,
             @NonNull ChoreographerWrapper choreographer,
             @NonNull FrameMetricsWrapper metrics, int traceThresholdMissedFrames,
-            int traceThresholdFrameTimeMillis) {
+            int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener) {
         mSession = session;
         mRendererWrapper = renderer;
         mMetricsWrapper = metrics;
@@ -120,6 +124,7 @@
         mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
         mTraceThresholdMissedFrames = traceThresholdMissedFrames;
         mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
+        mListener = listener;
 
         // If the surface isn't valid yet, wait until it's created.
         if (viewRootWrapper.getSurfaceControl().isValid()) {
@@ -165,11 +170,15 @@
      */
     public synchronized void begin() {
         mBeginVsyncId = mChoreographer.getVsyncId() + 1;
+        mSession.setTimeStamp(System.nanoTime());
         Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
         mRendererWrapper.addObserver(mObserver);
         if (mSurfaceControl != null) {
             mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
         }
+        if (mListener != null) {
+            mListener.onNotifyCujEvents(mSession, ACTION_SESSION_BEGIN);
+        }
     }
 
     /**
@@ -224,7 +233,6 @@
     }
 
     private boolean isInRange(long vsyncId) {
-
         // It's possible that we may miss a callback for the frame with vsyncId == mEndVsyncId.
         // Because of that, we collect all frames even if they happen after the end so we eventually
         // have a frame after the end with both callbacks present.
@@ -371,6 +379,9 @@
                     missedAppFramesCount + missedSfFramesCounts,
                     maxFrameTimeNanos,
                     missedSfFramesCounts);
+            if (mListener != null) {
+                mListener.onNotifyCujEvents(mSession, ACTION_METRICS_LOGGED);
+            }
         }
         if (DEBUG) {
             Log.i(TAG, "FrameTracker: CUJ=" + mSession.getName()
@@ -495,4 +506,17 @@
             return mChoreographer.getVsyncId();
         }
     }
+
+    /**
+     * A listener that notifies cuj events.
+     */
+    public interface FrameTrackerListener {
+        /**
+         * Notify that the CUJ session was created.
+         *
+         * @param session the CUJ session
+         * @param action the specific action
+         */
+        void onNotifyCujEvents(Session session, String action);
+    }
 }
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index cba6af9..0294ec3 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.jank;
 
+import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
+
 import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
 import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
@@ -48,9 +50,12 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
 import android.os.Build;
 import android.os.HandlerExecutor;
 import android.os.HandlerThread;
+import android.os.SystemProperties;
 import android.provider.DeviceConfig;
 import android.util.Log;
 import android.util.SparseArray;
@@ -59,6 +64,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
+import com.android.internal.jank.FrameTracker.FrameTrackerListener;
 import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
 import com.android.internal.jank.FrameTracker.ViewRootWrapper;
 import com.android.internal.util.PerfettoTrigger;
@@ -74,6 +80,8 @@
  */
 public class InteractionJankMonitor {
     private static final String TAG = InteractionJankMonitor.class.getSimpleName();
+    private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName();
+
     private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
     private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
     private static final String SETTINGS_ENABLED_KEY = "enabled";
@@ -90,6 +98,14 @@
     private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
     private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
 
+    public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN";
+    public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
+    public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED";
+    public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME";
+    public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP";
+    @VisibleForTesting
+    public static final String PROP_NOTIFY_CUJ_EVENT = "debug.notify_cuj_events";
+
     // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
     public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
     public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1;
@@ -256,15 +272,28 @@
      */
     @VisibleForTesting
     public FrameTracker createFrameTracker(View v, Session session) {
+        final Context c = v.getContext().getApplicationContext();
         synchronized (this) {
+            boolean needListener = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false);
+            FrameTrackerListener eventsListener =
+                    !needListener ? null : (s, act) -> notifyEvents(c, act, s);
+
             return new FrameTracker(session, mWorker.getThreadHandler(),
                     new ThreadedRendererWrapper(v.getThreadedRenderer()),
                     new ViewRootWrapper(v.getViewRootImpl()), new SurfaceControlWrapper(),
                     new ChoreographerWrapper(Choreographer.getInstance()), mMetrics,
-                    mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis);
+                    mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, eventsListener);
         }
     }
 
+    private void notifyEvents(Context context, String action, Session session) {
+        Intent intent = new Intent(action);
+        intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj()));
+        intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp());
+        intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY);
+        context.sendBroadcast(intent);
+    }
+
     /**
      * Begin a trace session.
      *
@@ -479,6 +508,7 @@
     public static class Session {
         @CujType
         private int mCujType;
+        private long mTimeStamp;
 
         public Session(@CujType int cujType) {
             mCujType = cujType;
@@ -505,5 +535,13 @@
         public String getName() {
             return "J<" + getNameOfCuj(mCujType) + ">";
         }
+
+        public void setTimeStamp(long timeStamp) {
+            mTimeStamp = timeStamp;
+        }
+
+        public long getTimeStamp() {
+            return mTimeStamp;
+        }
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1f8ffe0..62e1ee1 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -7965,16 +7965,14 @@
 
         /** Adds the given energy to the given standard energy bucket for this uid. */
         private void addEnergyToStandardBucketLocked(long energyDeltaUJ,
-                @StandardEnergyBucket int energyBucket, boolean accumulate) {
+                @StandardEnergyBucket int energyBucket) {
             getOrCreateMeasuredEnergyStatsLocked()
-                    .updateStandardBucket(energyBucket, energyDeltaUJ, accumulate);
+                    .updateStandardBucket(energyBucket, energyDeltaUJ);
         }
 
         /** Adds the given energy to the given custom energy bucket for this uid. */
-        private void addEnergyToCustomBucketLocked(long energyDeltaUJ, int energyBucket,
-                boolean accumulate) {
-            getOrCreateMeasuredEnergyStatsLocked()
-                    .updateCustomBucket(energyBucket, energyDeltaUJ, accumulate);
+        private void addEnergyToCustomBucketLocked(long energyDeltaUJ, int energyBucket) {
+            getOrCreateMeasuredEnergyStatsLocked().updateCustomBucket(energyBucket, energyDeltaUJ);
         }
 
         /**
@@ -12468,7 +12466,7 @@
             return;
         }
 
-        mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ, true);
+        mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ);
 
         // Now we blame individual apps, but only if the display was ON.
         if (energyBucket != MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON) {
@@ -12506,7 +12504,7 @@
             final long appDisplayEnergyMJ =
                     (totalDisplayEnergyMJ * fgTimeMs + (totalFgTimeMs / 2))
                     / totalFgTimeMs;
-            uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true);
+            uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket);
 
             // To mitigate round-off errors, remove this app from numerator & denominator totals
             totalDisplayEnergyMJ -= appDisplayEnergyMJ;
@@ -12520,6 +12518,7 @@
      * @param totalEnergyUJ energy (microjoules) used for this bucket since this was last called.
      * @param uidEnergies map of uid->energy (microjoules) for this bucket since last called.
      *                    Data inside uidEnergies will not be modified (treated immutable).
+     *                    Uids not already known to BatteryStats will be ignored.
      */
     public void updateCustomMeasuredEnergyDataLocked(int customEnergyBucket,
             long totalEnergyUJ, @Nullable SparseLongArray uidEnergies) {
@@ -12532,7 +12531,7 @@
         if (mGlobalMeasuredEnergyStats == null) return;
         if (!mOnBatteryInternal || mIgnoreNextExternalStats || totalEnergyUJ <= 0) return;
 
-        mGlobalMeasuredEnergyStats.updateCustomBucket(customEnergyBucket, totalEnergyUJ, true);
+        mGlobalMeasuredEnergyStats.updateCustomBucket(customEnergyBucket, totalEnergyUJ);
 
         if (uidEnergies == null) return;
         final int numUids = uidEnergies.size();
@@ -12540,10 +12539,20 @@
             final int uidInt = mapUid(uidEnergies.keyAt(i));
             final long uidEnergyUJ = uidEnergies.valueAt(i);
             if (uidEnergyUJ == 0) continue;
-            // TODO(b/180030409): Worry about dead Uids (no longer in BSI) being revived by this,
-            //  or converse problem of not creating a new Uid if its first blame is recorded here.
-            final Uid uidObj = getUidStatsLocked(uidInt);
-            uidObj.addEnergyToCustomBucketLocked(uidEnergyUJ, customEnergyBucket, true);
+            final Uid uidObj = getAvailableUidStatsLocked(uidInt);
+            if (uidObj != null) {
+                uidObj.addEnergyToCustomBucketLocked(uidEnergyUJ, customEnergyBucket);
+            } else {
+                // Ignore any uid not already known to BatteryStats, rather than creating a new Uid.
+                // Otherwise we could end up reviving dead Uids. Note that the CPU data is updated
+                // first, so any uid that has used any CPU should already be known to BatteryStats.
+                // Recently removed uids (especially common for isolated uids) can reach this path
+                // and are ignored.
+                if (!Process.isIsolated(uidInt)) {
+                    Slog.w(TAG, "Received measured energy " + totalEnergyUJ + " for custom bucket "
+                        + customEnergyBucket + " for non-existent uid " + uidInt);
+                }
+            }
         }
     }
 
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index eef9fa7..0163acc 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -22,10 +22,12 @@
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
 import android.os.Bundle;
-import android.os.SystemClock;
+import android.os.UidBatteryConsumer;
 import android.os.UserHandle;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -85,7 +87,7 @@
     }
 
     /**
-     * Returns a snapshot of battery attribution data.
+     * Returns snapshots of battery attribution data, one per supplied query.
      */
     public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
 
@@ -112,12 +114,19 @@
         return results;
     }
 
-    private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
+    /**
+     * Returns a snapshot of battery attribution data.
+     */
+    @VisibleForTesting
+    public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
+        final long realtimeUs = mStats.mClocks.elapsedRealtime() * 1000;
+        final long uptimeUs = mStats.mClocks.uptimeMillis() * 1000;
+
         final long[] customMeasuredEnergiesMicroJoules =
                 mStats.getCustomMeasuredEnergiesMicroJoules();
         final int customPowerComponentCount = customMeasuredEnergiesMicroJoules != null
-                        ? customMeasuredEnergiesMicroJoules.length
-                        : 0;
+                ? customMeasuredEnergiesMicroJoules.length
+                : 0;
 
         // TODO(b/174186358): read extra time component number from configuration
         final int customTimeComponentCount = 0;
@@ -128,12 +137,14 @@
 
         SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats();
         for (int i = uidStats.size() - 1; i >= 0; i--) {
-            batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
+            final BatteryStats.Uid uid = uidStats.valueAt(i);
+            batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid)
+                    .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND,
+                            getProcessBackgroundTimeMs(uid, realtimeUs))
+                    .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND,
+                            getProcessForegroundTimeMs(uid, realtimeUs));
         }
 
-        final long realtimeUs = SystemClock.elapsedRealtime() * 1000;
-        final long uptimeUs = SystemClock.uptimeMillis() * 1000;
-
         final List<PowerCalculator> powerCalculators = getPowerCalculators();
         for (int i = 0, count = powerCalculators.size(); i < count; i++) {
             PowerCalculator powerCalculator = powerCalculators.get(i);
@@ -143,4 +154,35 @@
 
         return batteryUsageStatsBuilder.build();
     }
+
+    private long getProcessForegroundTimeMs(BatteryStats.Uid uid, long realtimeUs) {
+        final long topStateDurationMs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP,
+                realtimeUs, BatteryStats.STATS_SINCE_CHARGED) / 1000;
+
+        long foregroundActivityDurationMs = 0;
+        final BatteryStats.Timer foregroundActivityTimer = uid.getForegroundActivityTimer();
+        if (foregroundActivityTimer != null) {
+            foregroundActivityDurationMs = foregroundActivityTimer.getTotalTimeLocked(realtimeUs,
+                    BatteryStats.STATS_SINCE_CHARGED) / 1000;
+        }
+
+        // Use the min value of STATE_TOP time and foreground activity time, since both of these
+        // times are imprecise
+        final long foregroundDurationMs = Math.min(topStateDurationMs,
+                foregroundActivityDurationMs);
+
+        long foregroundServiceDurationMs = 0;
+        final BatteryStats.Timer foregroundServiceTimer = uid.getForegroundServiceTimer();
+        if (foregroundServiceTimer != null) {
+            foregroundServiceDurationMs = foregroundServiceTimer.getTotalTimeLocked(realtimeUs,
+                    BatteryStats.STATS_SINCE_CHARGED) / 1000;
+        }
+
+        return foregroundDurationMs + foregroundServiceDurationMs;
+    }
+
+    private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs) {
+        return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, realtimeUs,
+                BatteryStats.STATS_SINCE_CHARGED) / 1000;
+    }
 }
diff --git a/core/java/com/android/internal/os/UsageBasedPowerEstimator.java b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java
index 5910b61..fd52014 100644
--- a/core/java/com/android/internal/os/UsageBasedPowerEstimator.java
+++ b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java
@@ -32,6 +32,10 @@
         mAveragePowerMahPerMs = averagePowerMilliAmp / MILLIS_IN_HOUR;
     }
 
+    public boolean isSupported() {
+        return mAveragePowerMahPerMs != 0;
+    }
+
     /**
      * Given a {@link BatteryStats.Timer}, returns the accumulated duration.
      */
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 63763f7..98f613f 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -15,8 +15,13 @@
  */
 package com.android.internal.os;
 
+import android.os.BatteryConsumer;
 import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.SparseArray;
@@ -30,25 +35,93 @@
 public class WifiPowerCalculator extends PowerCalculator {
     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
     private static final String TAG = "WifiPowerCalculator";
-    private final double mIdleCurrentMa;
-    private final double mTxCurrentMa;
-    private final double mRxCurrentMa;
-    private final PowerProfile mPowerProfile;
+    private final UsageBasedPowerEstimator mIdlePowerEstimator;
+    private final UsageBasedPowerEstimator mTxPowerEstimator;
+    private final UsageBasedPowerEstimator mRxPowerEstimator;
+    private final UsageBasedPowerEstimator mPowerOnPowerEstimator;
+    private final UsageBasedPowerEstimator mScanPowerEstimator;
+    private final UsageBasedPowerEstimator mBatchScanPowerEstimator;
     private final boolean mHasWifiPowerController;
-    private double mTotalAppPowerDrain = 0;
-    private long mTotalAppRunningTime = 0;
-    private WifiPowerEstimator mWifiPowerEstimator;
-    private boolean mHasWifiPowerReporting;
+    private final double mWifiPowerPerPacket;
+
+    private static class PowerDurationAndTraffic {
+        public double powerMah;
+        public long durationMs;
+
+        public long wifiRxPackets;
+        public long wifiTxPackets;
+        public long wifiRxBytes;
+        public long wifiTxBytes;
+    }
 
     public WifiPowerCalculator(PowerProfile profile) {
-        mPowerProfile = profile;
+        mPowerOnPowerEstimator = new UsageBasedPowerEstimator(
+                profile.getAveragePower(PowerProfile.POWER_WIFI_ON));
+        mScanPowerEstimator = new UsageBasedPowerEstimator(
+                profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN));
+        mBatchScanPowerEstimator = new UsageBasedPowerEstimator(
+                profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN));
+        mIdlePowerEstimator = new UsageBasedPowerEstimator(
+                profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE));
+        mTxPowerEstimator = new UsageBasedPowerEstimator(
+                profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX));
+        mRxPowerEstimator = new UsageBasedPowerEstimator(
+                profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX));
 
-        mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
-        mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);
-        mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);
+        mWifiPowerPerPacket = getWifiPowerPerPacket(profile);
 
-        mHasWifiPowerController = mIdleCurrentMa != 0 && mTxCurrentMa != 0 && mRxCurrentMa != 0;
-        mWifiPowerEstimator = new WifiPowerEstimator(mPowerProfile);
+        mHasWifiPowerController =
+                mIdlePowerEstimator.isSupported() && mTxPowerEstimator.isSupported()
+                        && mRxPowerEstimator.isSupported();
+    }
+
+    @Override
+    public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+
+        // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point,
+        // so always check this field.
+        final boolean hasWifiPowerReporting =
+                mHasWifiPowerController && batteryStats.hasWifiActivityReporting();
+
+        final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
+                builder.getOrCreateSystemBatteryConsumerBuilder(
+                        SystemBatteryConsumer.DRAIN_TYPE_WIFI);
+
+        long totalAppDurationMs = 0;
+        double totalAppPowerMah = 0;
+        final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic();
+        final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+                builder.getUidBatteryConsumerBuilders();
+        for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+            final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+            calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), rawRealtimeUs,
+                    BatteryStats.STATS_SINCE_CHARGED,
+                    hasWifiPowerReporting);
+
+            totalAppDurationMs += powerDurationAndTraffic.durationMs;
+            totalAppPowerMah += powerDurationAndTraffic.powerMah;
+
+            app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI,
+                    powerDurationAndTraffic.durationMs);
+            app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
+                    powerDurationAndTraffic.powerMah);
+
+            if (app.getUid() == Process.WIFI_UID) {
+                systemBatteryConsumerBuilder.addUidBatteryConsumer(app);
+                app.excludeFromBatteryUsageStats();
+            }
+        }
+
+        calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs,
+                BatteryStats.STATS_SINCE_CHARGED,
+                hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah);
+
+        systemBatteryConsumerBuilder
+                .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI,
+                        powerDurationAndTraffic.durationMs)
+                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
+                        powerDurationAndTraffic.powerMah);
     }
 
     /**
@@ -64,100 +137,151 @@
 
         // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point,
         // so always check this field.
-        mHasWifiPowerReporting = mHasWifiPowerController && batteryStats.hasWifiActivityReporting();
+        final boolean hasWifiPowerReporting =
+                mHasWifiPowerController && batteryStats.hasWifiActivityReporting();
 
-        super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
+        final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0);
 
-        BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0);
-        calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType);
-
+        long totalAppDurationMs = 0;
+        double totalAppPowerMah = 0;
+        final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic();
         for (int i = sippers.size() - 1; i >= 0; i--) {
-            BatterySipper app = sippers.get(i);
-            if (app.getUid() == Process.WIFI_UID) {
-                if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs);
-                app.isAggregated = true;
-                bs.add(app);
+            final BatterySipper app = sippers.get(i);
+            if (app.drainType == BatterySipper.DrainType.APP) {
+                calculateApp(powerDurationAndTraffic, app.uidObj, rawRealtimeUs, statsType,
+                        hasWifiPowerReporting);
+
+                totalAppDurationMs += powerDurationAndTraffic.durationMs;
+                totalAppPowerMah += powerDurationAndTraffic.powerMah;
+
+                app.wifiPowerMah = powerDurationAndTraffic.powerMah;
+                app.wifiRunningTimeMs = powerDurationAndTraffic.durationMs;
+                app.wifiRxBytes = powerDurationAndTraffic.wifiRxBytes;
+                app.wifiRxPackets = powerDurationAndTraffic.wifiRxPackets;
+                app.wifiTxBytes = powerDurationAndTraffic.wifiTxBytes;
+                app.wifiTxPackets = powerDurationAndTraffic.wifiTxPackets;
+                if (app.getUid() == Process.WIFI_UID) {
+                    if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs);
+                    app.isAggregated = true;
+                    bs.add(app);
+                }
             }
         }
+
+        calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, statsType,
+                hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah);
+
+        bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs;
+        bs.wifiPowerMah += powerDurationAndTraffic.powerMah;
+
         if (bs.sumPower() > 0) {
             sippers.add(bs);
         }
     }
 
-    @Override
-    protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-            long rawUptimeUs, int statsType) {
-        if (!mHasWifiPowerReporting) {
-            mWifiPowerEstimator.calculateApp(app, u, rawRealtimeUs, rawUptimeUs, statsType);
-            return;
-        }
-
-        final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
-        if (counter == null) {
-            return;
-        }
-
-        final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
-        final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType);
-        final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);
-        app.wifiRunningTimeMs = idleTime + rxTime + txTime;
-        mTotalAppRunningTime += app.wifiRunningTimeMs;
-
-        app.wifiPowerMah =
-                ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa))
-                        / (1000 * 60 * 60);
-        mTotalAppPowerDrain += app.wifiPowerMah;
-
-        app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
+    private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u,
+            long rawRealtimeUs,
+            int statsType, boolean hasWifiPowerReporting) {
+        powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets(
+                BatteryStats.NETWORK_WIFI_RX_DATA,
                 statsType);
-        app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
+        powerDurationAndTraffic.wifiTxPackets = u.getNetworkActivityPackets(
+                BatteryStats.NETWORK_WIFI_TX_DATA,
                 statsType);
-        app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
+        powerDurationAndTraffic.wifiRxBytes = u.getNetworkActivityBytes(
+                BatteryStats.NETWORK_WIFI_RX_DATA,
                 statsType);
-        app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
+        powerDurationAndTraffic.wifiTxBytes = u.getNetworkActivityBytes(
+                BatteryStats.NETWORK_WIFI_TX_DATA,
                 statsType);
 
-        if (DEBUG && app.wifiPowerMah != 0) {
-            Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime + "ms tx=" +
-                    txTime + "ms power=" + formatCharge(app.wifiPowerMah));
+        if (hasWifiPowerReporting) {
+            final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
+            if (counter != null) {
+                final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
+                final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType);
+                final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);
+
+                powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime;
+                powerDurationAndTraffic.powerMah = mIdlePowerEstimator.calculatePower(idleTime)
+                        + mTxPowerEstimator.calculatePower(txTime)
+                        + mRxPowerEstimator.calculatePower(rxTime);
+
+                if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
+                    Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime
+                            + "ms tx=" + txTime + "ms power=" + formatCharge(
+                            powerDurationAndTraffic.powerMah));
+                }
+            }
+        } else {
+            final double wifiPacketPower = (
+                    powerDurationAndTraffic.wifiRxPackets + powerDurationAndTraffic.wifiTxPackets)
+                    * mWifiPowerPerPacket;
+            final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
+            final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
+            long batchScanTimeMs = 0;
+            for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
+                batchScanTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
+            }
+
+            powerDurationAndTraffic.durationMs = wifiRunningTime;
+            powerDurationAndTraffic.powerMah = wifiPacketPower
+                    + mPowerOnPowerEstimator.calculatePower(wifiRunningTime)
+                    + mScanPowerEstimator.calculatePower(wifiScanTimeMs)
+                    + mBatchScanPowerEstimator.calculatePower(batchScanTimeMs);
+
+            if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
+                Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(
+                        powerDurationAndTraffic.powerMah));
+            }
         }
     }
 
-    private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
-            long rawUptimeUs, int statsType) {
-        if (!mHasWifiPowerReporting) {
-            mWifiPowerEstimator.calculateRemaining(app, stats, rawRealtimeUs, rawUptimeUs,
-                    statsType);
-            return;
+    private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic,
+            BatteryStats stats, long rawRealtimeUs,
+            int statsType, boolean hasWifiPowerReporting, long totalAppDurationMs,
+            double totalAppPowerMah) {
+        long totalDurationMs;
+        double totalPowerMah;
+        if (hasWifiPowerReporting) {
+            final BatteryStats.ControllerActivityCounter counter =
+                    stats.getWifiControllerActivity();
+
+            final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
+            final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
+            final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
+
+            totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs;
+
+            totalPowerMah =
+                    counter.getPowerCounter().getCountLocked(statsType) / (double) (1000 * 60 * 60);
+            if (totalPowerMah == 0) {
+                // Some controllers do not report power drain, so we can calculate it here.
+                totalPowerMah = mIdlePowerEstimator.calculatePower(idleTimeMs)
+                        + mTxPowerEstimator.calculatePower(txTimeMs)
+                        + mRxPowerEstimator.calculatePower(rxTimeMs);
+            }
+        } else {
+            totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000;
+            totalPowerMah = mPowerOnPowerEstimator.calculatePower(totalDurationMs);
         }
 
-        final BatteryStats.ControllerActivityCounter counter = stats.getWifiControllerActivity();
-
-        final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
-        final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
-        final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
-
-        app.wifiRunningTimeMs = Math.max(0,
-                (idleTimeMs + rxTimeMs + txTimeMs) - mTotalAppRunningTime);
-
-        double powerDrainMah = counter.getPowerCounter().getCountLocked(statsType)
-                / (double) (1000 * 60 * 60);
-        if (powerDrainMah == 0) {
-            // Some controllers do not report power drain, so we can calculate it here.
-            powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
-                    + (rxTimeMs * mRxCurrentMa)) / (1000 * 60 * 60);
-        }
-        app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain);
+        powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs);
+        powerDurationAndTraffic.powerMah = Math.max(0, totalPowerMah - totalAppPowerMah);
 
         if (DEBUG) {
-            Log.d(TAG, "left over WiFi power: " + formatCharge(app.wifiPowerMah));
+            Log.d(TAG, "left over WiFi power: " + formatCharge(powerDurationAndTraffic.powerMah));
         }
     }
 
-    @Override
-    public void reset() {
-        mTotalAppPowerDrain = 0;
-        mTotalAppRunningTime = 0;
-        mWifiPowerEstimator.reset();
+    /**
+     * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB.
+     */
+    private static double getWifiPowerPerPacket(PowerProfile profile) {
+        // TODO(b/179392913): Extract average bit rates from system
+        final long wifiBps = 1000000;
+        final double averageWifiActivePower =
+                profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) / 3600;
+        return averageWifiActivePower / (((double) wifiBps) / 8 / 2048);
     }
 }
diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java
deleted file mode 100644
index d0a105c..0000000
--- a/core/java/com/android/internal/os/WifiPowerEstimator.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import android.os.BatteryStats;
-import android.os.Process;
-import android.os.UserHandle;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.util.List;
-
-/**
- * Estimates WiFi power usage based on timers in BatteryStats.
- */
-public class WifiPowerEstimator extends PowerCalculator {
-    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
-    private static final String TAG = "WifiPowerEstimator";
-    private final double mWifiPowerPerPacket;
-    private final double mWifiPowerOn;
-    private final double mWifiPowerScan;
-    private final double mWifiPowerBatchScan;
-    private long mTotalAppWifiRunningTimeMs = 0;
-
-    public WifiPowerEstimator(PowerProfile profile) {
-        mWifiPowerPerPacket = getWifiPowerPerPacket(profile);
-        mWifiPowerOn = profile.getAveragePower(PowerProfile.POWER_WIFI_ON);
-        mWifiPowerScan = profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN);
-        mWifiPowerBatchScan = profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN);
-    }
-
-    @Override
-    public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
-            long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
-        super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
-
-        BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0);
-        calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType);
-
-        for (int i = sippers.size() - 1; i >= 0; i--) {
-            BatterySipper app = sippers.get(i);
-            if (app.getUid() == Process.WIFI_UID) {
-                if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs);
-                app.isAggregated = true;
-                bs.add(app);
-            }
-        }
-        if (bs.sumPower() > 0) {
-            sippers.add(bs);
-        }
-    }
-
-    /**
-     * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB.
-     */
-    private static double getWifiPowerPerPacket(PowerProfile profile) {
-        final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
-        final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
-                / 3600;
-        return WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048);
-    }
-
-    @Override
-    protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-                             long rawUptimeUs, int statsType) {
-        app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
-                statsType);
-        app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
-                statsType);
-        app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
-                statsType);
-        app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
-                statsType);
-
-        final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets)
-                * mWifiPowerPerPacket;
-
-        app.wifiRunningTimeMs = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
-        mTotalAppWifiRunningTimeMs += app.wifiRunningTimeMs;
-        final double wifiLockPower = (app.wifiRunningTimeMs * mWifiPowerOn) / (1000*60*60);
-
-        final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
-        final double wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000*60*60);
-
-        double wifiBatchScanPower = 0;
-        for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
-            final long batchScanTimeMs =
-                    u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
-            final double batchScanPower = (batchScanTimeMs * mWifiPowerBatchScan) / (1000*60*60);
-            wifiBatchScanPower += batchScanPower;
-        }
-
-        app.wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
-        if (DEBUG && app.wifiPowerMah != 0) {
-            Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(app.wifiPowerMah));
-        }
-    }
-
-    void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
-                                   long rawUptimeUs, int statsType) {
-        final long totalRunningTimeMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType)
-                / 1000;
-        final double powerDrain = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn)
-                / (1000*60*60);
-        app.wifiRunningTimeMs = totalRunningTimeMs;
-        app.wifiPowerMah = Math.max(0, powerDrain);
-    }
-
-    @Override
-    public void reset() {
-        mTotalAppWifiRunningTimeMs = 0;
-    }
-}
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index d7b4d78..d49203c 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -193,34 +193,30 @@
         return mAccumulatedEnergiesMicroJoules.length;
     }
 
-    // TODO: Get rid of the 'accumulate' boolean. It's always true.
     /** Updates the given standard energy bucket with the given energy if accumulate is true. */
-    public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ,
-            boolean accumulate) {
+    public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ) {
         checkValidStandardBucket(bucket);
-        updateEntry(bucket, energyDeltaUJ, accumulate);
+        updateEntry(bucket, energyDeltaUJ);
     }
 
     /** Updates the given custom energy bucket with the given energy if accumulate is true. */
-    public void updateCustomBucket(int customBucket, long energyDeltaUJ, boolean accumulate) {
+    public void updateCustomBucket(int customBucket, long energyDeltaUJ) {
         if (!isValidCustomBucket(customBucket)) {
             Slog.e(TAG, "Attempted to update invalid custom bucket " + customBucket);
             return;
         }
         final int index = customBucketToIndex(customBucket);
-        updateEntry(index, energyDeltaUJ, accumulate);
+        updateEntry(index, energyDeltaUJ);
     }
 
     /** Updates the given index with the given energy if accumulate is true. */
-    private void updateEntry(int index, long energyDeltaUJ, boolean accumulate) {
-        if (accumulate) {
-            if (mAccumulatedEnergiesMicroJoules[index] >= 0L) {
-                mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ;
-            } else {
-                Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket "
-                        + getBucketName(index) + " whose value was "
-                        + mAccumulatedEnergiesMicroJoules[index]);
-            }
+    private void updateEntry(int index, long energyDeltaUJ) {
+        if (mAccumulatedEnergiesMicroJoules[index] >= 0L) {
+            mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ;
+        } else {
+            Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket "
+                    + getBucketName(index) + " whose value was "
+                    + mAccumulatedEnergiesMicroJoules[index]);
         }
     }
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 7edc6c8..fde48e8 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -67,7 +67,7 @@
     void onNotificationError(String pkg, String tag, int id,
             int uid, int initialPid, String message, int userId);
     void onClearAllNotifications(int userId);
-    void onNotificationClear(String pkg, String tag, int id, int userId, String key,
+    void onNotificationClear(String pkg, int userId, String key,
             int dismissalSurface, int dismissalSentiment, in NotificationVisibility nv);
     void onNotificationVisibilityChanged( in NotificationVisibility[] newlyVisibleKeys,
             in NotificationVisibility[] noLongerVisibleKeys);
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index f7d440d..95e0a3b 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -94,6 +94,6 @@
     void notifyBarringInfoChanged(int slotIndex, int subId, in BarringInfo barringInfo);
     void notifyPhysicalChannelConfigForSubscriber(in int subId,
             in List<PhysicalChannelConfig> configs);
-    void notifyDataEnabled(boolean enabled, int reason);
+    void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason);
     void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in Map allowedNetworkTypeList);
 }
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index ef2275d..ab0149f 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -30,6 +30,7 @@
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.PointerIcon;
+import android.view.ScrollCaptureResponse;
 import android.view.WindowInsets.Type.InsetsType;
 import android.window.ClientWindowFrames;
 
@@ -160,7 +161,9 @@
     @Override
     public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
         try {
-            callbacks.onUnavailable();
+            callbacks.onScrollCaptureResponse(
+                    new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build());
+
         } catch (RemoteException ex) {
             // ignore
         }
diff --git a/core/java/com/android/internal/view/OWNERS b/core/java/com/android/internal/view/OWNERS
index 851d1f3..eb2478f 100644
--- a/core/java/com/android/internal/view/OWNERS
+++ b/core/java/com/android/internal/view/OWNERS
@@ -18,3 +18,7 @@
 per-file BaseIWIndow.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file RotationPolicy.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file WindowManagerPolicyThread.java = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Scroll Capture
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index 85fa791..a41511b 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -16,15 +16,19 @@
 
 package com.android.internal.view;
 
+import android.annotation.UiThread;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.graphics.HardwareRenderer;
 import android.graphics.Matrix;
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.RenderNode;
-import android.os.Handler;
+import android.os.CancellationSignal;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.Display.ColorMode;
 import android.view.ScrollCaptureCallback;
 import android.view.ScrollCaptureSession;
 import android.view.Surface;
@@ -44,32 +48,42 @@
  * @param <V> the specific View subclass handled
  * @see ScrollCaptureViewHelper
  */
+@UiThread
 public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCallback {
 
-    public static final long NO_FRAME_PRODUCED = -1;
-
     private static final String TAG = "ScrollCaptureViewSupport";
 
     private static final boolean WAIT_FOR_ANIMATION = true;
 
     private final WeakReference<V> mWeakView;
     private final ScrollCaptureViewHelper<V> mViewHelper;
-    private ViewRenderer mRenderer;
-    private Handler mUiHandler;
+    private final ViewRenderer mRenderer;
     private boolean mStarted;
     private boolean mEnded;
 
     ScrollCaptureViewSupport(V containingView, ScrollCaptureViewHelper<V> viewHelper) {
         mWeakView = new WeakReference<>(containingView);
         mRenderer = new ViewRenderer();
-        mUiHandler = containingView.getHandler();
+        // TODO(b/177649144): provide access to color space from android.media.Image
         mViewHelper = viewHelper;
     }
 
-    // Base implementation of ScrollCaptureCallback
+    /** Based on ViewRootImpl#updateColorModeIfNeeded */
+    @ColorMode
+    private static int getColorMode(View containingView) {
+        Context context = containingView.getContext();
+        int colorMode = containingView.getViewRootImpl().mWindowAttributes.getColorMode();
+        if (!context.getResources().getConfiguration().isScreenWideColorGamut()) {
+            colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+        }
+        return colorMode;
+    }
 
     @Override
-    public final void onScrollCaptureSearch(Consumer<Rect> onReady) {
+    public final void onScrollCaptureSearch(CancellationSignal signal, Consumer<Rect> onReady) {
+        if (signal.isCanceled()) {
+            return;
+        }
         V view = mWeakView.get();
         mStarted = false;
         mEnded = false;
@@ -82,7 +96,11 @@
     }
 
     @Override
-    public final void onScrollCaptureStart(ScrollCaptureSession session, Runnable onReady) {
+    public final void onScrollCaptureStart(ScrollCaptureSession session, CancellationSignal signal,
+            Runnable onReady) {
+        if (signal.isCanceled()) {
+            return;
+        }
         V view = mWeakView.get();
 
         mEnded = false;
@@ -99,18 +117,22 @@
     }
 
     @Override
-    public final void onScrollCaptureImageRequest(ScrollCaptureSession session, Rect requestRect) {
+    public final void onScrollCaptureImageRequest(ScrollCaptureSession session,
+            CancellationSignal signal, Rect requestRect, Consumer<Rect> onComplete) {
+        if (signal.isCanceled()) {
+            return;
+        }
         V view = mWeakView.get();
         if (view == null || !view.isVisibleToUser()) {
             // Signal to the controller that we have a problem and can't continue.
-            session.notifyBufferSent(NO_FRAME_PRODUCED, new Rect());
+            onComplete.accept(new Rect());
             return;
         }
         // Ask the view to scroll as needed to bring this area into view.
         ScrollResult scrollResult = mViewHelper.onScrollRequested(view, session.getScrollBounds(),
                 requestRect);
         if (scrollResult.availableArea.isEmpty()) {
-            session.notifyBufferSent(NO_FRAME_PRODUCED, scrollResult.availableArea);
+            onComplete.accept(scrollResult.availableArea);
             return;
         }
         view.invalidate(); // don't wait for vsync
@@ -121,17 +143,13 @@
         viewCaptureArea.offset(0, -scrollResult.scrollDelta);
 
         if (WAIT_FOR_ANIMATION) {
-            Log.d(TAG, "render: delaying until animation");
             view.postOnAnimation(() ->  {
-                Log.d(TAG, "postOnAnimation(): rendering now");
-                long resultFrame = mRenderer.renderView(view, viewCaptureArea);
-                Log.d(TAG, "notifyBufferSent: " + scrollResult.availableArea);
-
-                session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea));
+                mRenderer.renderView(view, viewCaptureArea);
+                onComplete.accept(new Rect(scrollResult.availableArea));
             });
         } else {
-            long resultFrame = mRenderer.renderView(view, viewCaptureArea);
-            session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea));
+            mRenderer.renderView(view, viewCaptureArea);
+            onComplete.accept(new Rect(scrollResult.availableArea));
         }
     }
 
@@ -239,7 +257,7 @@
             mCaptureRenderNode.endRecording();
         }
 
-        public long renderView(View view, Rect sourceRect) {
+        public void renderView(View view, Rect sourceRect) {
             if (updateForView(view)) {
                 setupLighting(view);
             }
@@ -258,7 +276,7 @@
             switch (request.syncAndDraw()) {
                 case HardwareRenderer.SYNC_OK:
                 case HardwareRenderer.SYNC_REDRAW_REQUESTED:
-                    return frameNumber;
+                    return;
 
                 case HardwareRenderer.SYNC_FRAME_DROPPED:
                     Log.e(TAG, "syncAndDraw(): SYNC_FRAME_DROPPED !");
@@ -270,7 +288,6 @@
                     Log.e(TAG, "syncAndDraw(): SYNC_CONTEXT_IS_STOPPED !");
                     break;
             }
-            return NO_FRAME_PRODUCED;
         }
 
         public void trimMemory() {
@@ -289,5 +306,17 @@
             mTempMatrix.mapRect(mTempRectF);
             mTempRectF.round(outRect);
         }
+
+        public void setColorMode(@ColorMode int colorMode) {
+            mRenderer.setColorMode(colorMode);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "ScrollCaptureViewSupport{"
+                + "view=" + mWeakView.get()
+                + ", helper=" + mViewHelper
+                + '}';
     }
 }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 8982519..b40ffb0 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -24,6 +24,7 @@
 import android.content.ComponentName;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
+import android.hardware.SensorPrivacyManager;
 import android.os.Build;
 import android.os.CarrierAssociatedAppEntry;
 import android.os.Environment;
@@ -1234,10 +1235,10 @@
             addFeature(PackageManager.FEATURE_RAM_NORMAL, 0);
         }
 
-        if (IncrementalManager.isFeatureEnabled()) {
+        final int incrementalVersion = IncrementalManager.getVersion();
+        if (incrementalVersion > 0) {
             addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY, 0);
-            addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY_VERSION,
-                    IncrementalManager.isV2Available() ? 2 : 1);
+            addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY_VERSION, incrementalVersion);
         }
 
         if (PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT) {
@@ -1252,6 +1253,14 @@
             addFeature(PackageManager.FEATURE_CROSS_LAYER_BLUR, 0);
         }
 
+        if (SensorPrivacyManager.USE_MICROPHONE_TOGGLE) {
+            addFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE, 0);
+        }
+
+        if (SensorPrivacyManager.USE_CAMERA_TOGGLE) {
+            addFeature(PackageManager.FEATURE_CAMERA_TOGGLE, 0);
+        }
+
         for (String featureName : mUnavailableFeatures) {
             removeFeature(featureName);
         }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index dd1a594..b485f0f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -121,6 +121,7 @@
                 "android_view_PointerIcon.cpp",
                 "android_view_Surface.cpp",
                 "android_view_SurfaceControl.cpp",
+                "android_view_SurfaceControlFpsListener.cpp",
                 "android_graphics_BLASTBufferQueue.cpp",
                 "android_view_SurfaceSession.cpp",
                 "android_view_TextureView.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 1751be0..dc77bba 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -120,6 +120,7 @@
 extern int register_android_view_InputWindowHandle(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
 extern int register_android_view_SurfaceControl(JNIEnv* env);
+extern int register_android_view_SurfaceControlFpsListener(JNIEnv* env);
 extern int register_android_view_SurfaceSession(JNIEnv* env);
 extern int register_android_view_CompositionSamplingListener(JNIEnv* env);
 extern int register_android_view_TextureView(JNIEnv* env);
@@ -1488,6 +1489,7 @@
         REG_JNI(register_android_view_InputWindowHandle),
         REG_JNI(register_android_view_Surface),
         REG_JNI(register_android_view_SurfaceControl),
+        REG_JNI(register_android_view_SurfaceControlFpsListener),
         REG_JNI(register_android_view_SurfaceSession),
         REG_JNI(register_android_view_CompositionSamplingListener),
         REG_JNI(register_android_view_TextureView),
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 35d1d7b..19c6a62 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -42,6 +42,9 @@
 per-file android_os_HwRemoteBinder* = file:platform/system/libhwbinder:/OWNERS
 per-file EphemeralStorage* = file:platform/system/libhwbinder:/OWNERS
 
+# Sensor
+per-file android_hardware_SensorManager* = arthuri@google.com, bduddie@google.com, stange@google.com
+
 per-file *Zygote* = file:/ZYGOTE_OWNERS
 per-file Android.bp = file:platform/build/soong:/OWNERS
 per-file android_animation_* = file:/core/java/android/animation/OWNERS
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index cbf4481..451ea93 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -27,7 +27,6 @@
 #include <android-base/chrono_utils.h>
 #include <android/graphics/region.h>
 #include <android/gui/BnScreenCaptureListener.h>
-#include <android/hardware/display/IDeviceProductInfoConstants.h>
 #include <android/os/IInputConstants.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_hardware_HardwareBuffer.h>
@@ -1023,24 +1022,16 @@
     } else {
         LOG_FATAL("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
     }
-    jint connectionToSinkType;
-    // Relative address maps to HDMI physical address. All addresses are 4 digits long allowing
-    // for a 5–device-deep hierarchy. For more information, refer:
-    // Section 8.7 - Physical Address of HDMI Specification Version 1.3a
-    using android::hardware::display::IDeviceProductInfoConstants;
-    if (info->relativeAddress.size() != 4) {
-        connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_UNKNOWN;
-    } else if (info->relativeAddress[0] == 0) {
-        connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_BUILT_IN;
-    } else if (info->relativeAddress[1] == 0) {
-        connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_DIRECT;
-    } else {
-        connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_TRANSITIVE;
+    auto relativeAddress = env->NewIntArray(info->relativeAddress.size());
+    auto relativeAddressData = env->GetIntArrayElements(relativeAddress, nullptr);
+    for (int i = 0; i < info->relativeAddress.size(); i++) {
+        relativeAddressData[i] = info->relativeAddress[i];
     }
+    env->ReleaseIntArrayElements(relativeAddress, relativeAddressData, 0);
 
     return env->NewObject(gDeviceProductInfoClassInfo.clazz, gDeviceProductInfoClassInfo.ctor, name,
                           manufacturerPnpId, productId, modelYear, manufactureDate,
-                          connectionToSinkType);
+                          relativeAddress);
 }
 
 static jobject nativeGetStaticDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) {
@@ -1979,7 +1970,7 @@
                              "Ljava/lang/String;"
                              "Ljava/lang/Integer;"
                              "Landroid/hardware/display/DeviceProductInfo$ManufactureDate;"
-                             "I)V");
+                             "[I)V");
 
     jclass deviceProductInfoManufactureDateClazz =
             FindClassOrDie(env, "android/hardware/display/DeviceProductInfo$ManufactureDate");
diff --git a/core/jni/android_view_SurfaceControlFpsListener.cpp b/core/jni/android_view_SurfaceControlFpsListener.cpp
new file mode 100644
index 0000000..6fa12e5
--- /dev/null
+++ b/core/jni/android_view_SurfaceControlFpsListener.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SurfaceControlFpsListener"
+
+#include <android/gui/BnFpsListener.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+
+#include "android_util_Binder.h"
+#include "core_jni_helpers.h"
+
+namespace android {
+
+namespace {
+
+struct {
+    jclass mClass;
+    jmethodID mDispatchOnFpsReported;
+} gListenerClassInfo;
+
+struct SurfaceControlFpsListener : public gui::BnFpsListener {
+    SurfaceControlFpsListener(JNIEnv* env, jobject listener)
+          : mListener(env->NewWeakGlobalRef(listener)) {}
+
+    binder::Status onFpsReported(float fps) override {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onFpsReported.");
+
+        jobject listener = env->NewGlobalRef(mListener);
+        if (listener == NULL) {
+            // Weak reference went out of scope
+            return binder::Status::ok();
+        }
+        env->CallStaticVoidMethod(gListenerClassInfo.mClass,
+                                  gListenerClassInfo.mDispatchOnFpsReported, listener,
+                                  static_cast<jfloat>(fps));
+        env->DeleteGlobalRef(listener);
+
+        if (env->ExceptionCheck()) {
+            ALOGE("SurfaceControlFpsListener.onFpsReported() failed.");
+            LOGE_EX(env);
+            env->ExceptionClear();
+        }
+        return binder::Status::ok();
+    }
+
+protected:
+    virtual ~SurfaceControlFpsListener() {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        env->DeleteWeakGlobalRef(mListener);
+    }
+
+private:
+    jweak mListener;
+};
+
+jlong nativeCreate(JNIEnv* env, jclass clazz, jobject obj) {
+    SurfaceControlFpsListener* listener = new SurfaceControlFpsListener(env, obj);
+    listener->incStrong((void*)nativeCreate);
+    return reinterpret_cast<jlong>(listener);
+}
+
+void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
+    SurfaceControlFpsListener* listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);
+    listener->decStrong((void*)nativeCreate);
+}
+
+void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr, jlong layerObj) {
+    sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);
+    auto layer = reinterpret_cast<SurfaceControl*>(layerObj);
+    sp<IBinder> layerHandle = layer != nullptr ? layer->getHandle() : nullptr;
+    if (SurfaceComposerClient::addFpsListener(layerHandle, listener) != OK) {
+        constexpr auto error_msg = "Couldn't addFpsListener";
+        ALOGE(error_msg);
+        jniThrowRuntimeException(env, error_msg);
+    }
+}
+
+void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) {
+    sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);
+
+    if (SurfaceComposerClient::removeFpsListener(listener) != OK) {
+        constexpr auto error_msg = "Couldn't removeFpsListener";
+        ALOGE(error_msg);
+        jniThrowRuntimeException(env, error_msg);
+    }
+}
+
+const JNINativeMethod gMethods[] = {
+        /* name, signature, funcPtr */
+        {"nativeCreate", "(Landroid/view/SurfaceControlFpsListener;)J", (void*)nativeCreate},
+        {"nativeDestroy", "(J)V", (void*)nativeDestroy},
+        {"nativeRegister", "(JJ)V", (void*)nativeRegister},
+        {"nativeUnregister", "(J)V", (void*)nativeUnregister}};
+
+} // namespace
+
+int register_android_view_SurfaceControlFpsListener(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, "android/view/SurfaceControlFpsListener", gMethods,
+                                       NELEM(gMethods));
+    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    jclass clazz = env->FindClass("android/view/SurfaceControlFpsListener");
+    gListenerClassInfo.mClass = MakeGlobalRefOrDie(env, clazz);
+    gListenerClassInfo.mDispatchOnFpsReported =
+            env->GetStaticMethodID(clazz, "dispatchOnFpsReported",
+                                   "(Landroid/view/SurfaceControlFpsListener;F)V");
+    return 0;
+}
+
+} // namespace android
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index d3c2d31..ec41a47 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -627,6 +627,7 @@
         optional int64 duration_ms = 2;
         optional string tag = 3;
         optional int32 type = 4;
+        optional int32 reason_code = 5;
     }
     repeated PendingTempWhitelist pending_temp_whitelist = 26;
 
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
index 4b1ee02..f64e146 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -144,7 +144,7 @@
      */
     optional int64 total_state_entry_count = 3;
     /**
-     * Last time this state was entered. Time in milliseconds since boot
+     * Last time this state was entered. Walltime in milliseconds since Unix epoch.
      */
     optional int64 last_entry_timestamp_ms = 4;
 }
@@ -208,7 +208,7 @@
     /** Unique index identifying the energy consumer. */
     optional int32 id = 1;
 
-    /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
+    /** Walltime in milliseconds since Unix epoch */
     optional int64 timestamp_ms = 2;
 
     /** Accumulated energy since device boot in microwatt-seconds (uWs) */
@@ -247,7 +247,7 @@
      */
     optional int32 id = 1;
 
-    /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
+    /** Walltime in milliseconds since Unix epoch */
     optional int64 timestamp_ms = 2;
 
     /** Accumulated energy since device boot in microwatt-seconds (uWs) */
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index a1be865c..ec502c3 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -174,10 +174,10 @@
     // Use root_display_area instead
     optional WindowContainerProto window_container = 1 [deprecated=true];
     optional int32 id = 2;
-    reserved 3; // stacks
-    optional DockedStackDividerControllerProto docked_stack_divider_controller = 4 [deprecated=true];
+    reserved 3; // RootTasks
+    optional DockedTaskDividerControllerProto docked_task_divider_controller = 4 [deprecated=true];
     // Will be removed soon.
-    optional PinnedStackControllerProto pinned_stack_controller = 5 [deprecated=true];
+    optional PinnedTaskControllerProto pinned_task_controller = 5 [deprecated=true];
     /* non app windows */
     repeated WindowTokenProto above_app_windows = 6 [deprecated=true];
     repeated WindowTokenProto below_app_windows = 7 [deprecated=true];
@@ -256,15 +256,15 @@
     optional int32 last_orientation = 5 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
 }
 
-/* represents DockedStackDividerController */
-message DockedStackDividerControllerProto {
+/* represents DockedTaskDividerController */
+message DockedTaskDividerControllerProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional bool minimized_dock = 1 [deprecated=true];
 }
 
-/* represents PinnedStackController */
-message PinnedStackControllerProto {
+/* represents PinnedTaskController */
+message PinnedTaskControllerProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional .android.graphics.RectProto default_bounds = 1 [deprecated=true];
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 4cf9cfb..b988b5a 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -44,11 +44,73 @@
     ],
 }
 
+genrule {
+    name: "remote-color-resources-compile-public",
+    tools: ["aapt2"],
+    srcs: [
+        "remote_color_resources_res/values/public.xml",
+    ],
+    out: ["values_public.arsc.flat"],
+    cmd: "$(location aapt2) compile $(in) -o $(genDir)",
+}
+
+genrule {
+    name: "remote-color-resources-compile-colors",
+    tools: ["aapt2"],
+    srcs: [
+        "remote_color_resources_res/values/colors.xml",
+    ],
+    out: ["values_colors.arsc.flat"],
+    cmd: "$(location aapt2) compile $(in) -o $(genDir)",
+}
+
+genrule {
+    name: "remote-color-resources-apk",
+    tools: ["aapt2"],
+    // The first input file in the list must be the manifest
+    srcs: [
+        "RemoteThemeColorsAndroidManifest.xml",
+        ":remote-color-resources-compile-public",
+        ":remote-color-resources-compile-colors",
+    ],
+    out: ["remote-color-resources.apk"],
+    cmd: "$(location aapt2) link -o $(out) --manifest $(in)"
+}
+
+genrule {
+    name: "remote-color-resources-arsc",
+    srcs: [":remote-color-resources-apk"],
+    out: ["res/raw/remote_views_color_resources.arsc"],
+    cmd: "mkdir -p $(genDir)/remote-color-resources-arsc && "
+        + "unzip -x $(in) resources.arsc -d $(genDir)/remote-color-resources-arsc && "
+        + "mkdir -p $$(dirname $(out)) && "
+        + "mv $(genDir)/remote-color-resources-arsc/resources.arsc $(out) && "
+        + "echo 'Created $(out)'"
+}
+
+genrule {
+    name: "remote-color-resources-arsc-zip",
+    tools: ["soong_zip"],
+    srcs: [
+        ":remote-color-resources-arsc",
+        "remote_color_resources_res/symbols.xml",
+    ],
+    out: ["remote_views_color_resources.zip"],
+    cmd: "INPUTS=($(in)) && "
+        + "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && "
+        + "mkdir -p $$RES_DIR/values && "
+        + "cp $${INPUTS[1]} $$RES_DIR/values && "
+        + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR && "
+        + "cp $(out) ."
+}
+
 android_app {
     name: "framework-res",
     sdk_version: "core_platform",
     certificate: "platform",
 
+    srcs: [":remote-color-resources-arsc"],
+
     // Disable dexpreopt and verify_uses_libraries check as the app
     // contains no Java code to be dexpreopted.
     enforce_uses_libs: false,
@@ -72,6 +134,10 @@
         "--auto-add-overlay",
     ],
 
+    resource_zips: [
+        ":remote-color-resources-arsc-zip",
+    ],
+
     // Create package-export.apk, which other packages can use to get
     // PRODUCT-agnostic resource data like IDs and type definitions.
     export_package_resources: true,
@@ -96,6 +162,7 @@
     srcs: [
         "assets/**/*",
         "res/**/*",
+        ":remote-color-resources-arsc",
     ],
 }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5dd8580..2150efe 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2641,6 +2641,16 @@
         android:label="@string/permlab_manageProfileAndDeviceOwners"
         android:description="@string/permdesc_manageProfileAndDeviceOwners" />
 
+    <!-- @TestApi @hide Allows an application to reset the record of previous system update freeze
+         periods. -->
+    <permission android:name="android.permission.CLEAR_FREEZE_PERIOD"
+                android:protectionLevel="signature" />
+
+    <!-- @TestApi @hide Allows an application to force available DevicePolicyManager logs to
+         DPC. -->
+    <permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS"
+                android:protectionLevel="signature" />
+
     <!-- Allows an application to get full detailed information about
          recently running tasks, with full fidelity to the real state.
          @hide -->
@@ -3972,17 +3982,6 @@
     <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
         android:protectionLevel="signature|privileged" />
 
-    <!-- Allows an application to use the package installer v2 APIs.
-         <p>The package installer v2 APIs are still a work in progress and we're
-         currently validating they work in all scenarios.
-         <p>Not for use by third-party applications.
-         TODO(b/152310230): use this permission to protect only Incremental installations
-         once the APIs are confirmed to be sufficient.
-         @hide
-    -->
-    <permission android:name="com.android.permission.USE_INSTALLER_V2"
-        android:protectionLevel="signature|verifier" />
-
     <!-- Allows an application to use System Data Loaders.
          <p>Not for use by third-party applications.
          @hide
@@ -5421,6 +5420,12 @@
          @hide -->
     <permission android:name="android.permission.MANAGE_SENSOR_PRIVACY"
                 android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows sensor privacy changes to be observed.
+         @hide -->
+    <permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY"
+                android:protectionLevel="signature|installer" />
+
     <!-- @SystemApi Permission that protects the {@link Intent#ACTION_REVIEW_ACCESSIBILITY_SERVICES}
          intent.
          @hide -->
diff --git a/core/res/RemoteThemeColorsAndroidManifest.xml b/core/res/RemoteThemeColorsAndroidManifest.xml
new file mode 100644
index 0000000..11970fd
--- /dev/null
+++ b/core/res/RemoteThemeColorsAndroidManifest.xml
@@ -0,0 +1,5 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  package="android">
+<application/>
+</manifest>
+
diff --git a/core/res/remote_color_resources_res/symbols.xml b/core/res/remote_color_resources_res/symbols.xml
new file mode 100644
index 0000000..82d5e3e
--- /dev/null
+++ b/core/res/remote_color_resources_res/symbols.xml
@@ -0,0 +1,4 @@
+<resources>
+  <!-- ARSC file used to overlay local colors when rendering a RemoteViews -->
+  <java-symbol type="raw" name="remote_views_color_resources" />
+</resources>
diff --git a/core/res/remote_color_resources_res/values/colors.xml b/core/res/remote_color_resources_res/values/colors.xml
new file mode 100644
index 0000000..295f16e
--- /dev/null
+++ b/core/res/remote_color_resources_res/values/colors.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <!-- Note: the values of the colors doesn't really matter (they will always be overwritten before used), but they help a lot debugging, to find out which color is where in the ARSC file. -->
+  <color name="system_primary_0">#01010101</color>
+  <color name="system_primary_50">#02020202</color>
+  <color name="system_primary_100">#03030303</color>
+  <color name="system_primary_200">#04040404</color>
+  <color name="system_primary_300">#05050505</color>
+  <color name="system_primary_400">#06060606</color>
+  <color name="system_primary_500">#07070707</color>
+  <color name="system_primary_600">#08080808</color>
+  <color name="system_primary_700">#09090909</color>
+  <color name="system_primary_800">#0a0a0a0a</color>
+  <color name="system_primary_900">#0b0b0b0b</color>
+  <color name="system_primary_1000">#0c0c0c0c</color>
+  <color name="system_secondary_0">#10101010</color>
+  <color name="system_secondary_50">#20202020</color>
+  <color name="system_secondary_100">#30303030</color>
+  <color name="system_secondary_200">#40404040</color>
+  <color name="system_secondary_300">#50505050</color>
+  <color name="system_secondary_400">#60606060</color>
+  <color name="system_secondary_500">#70707070</color>
+  <color name="system_secondary_600">#80808080</color>
+  <color name="system_secondary_700">#90909090</color>
+  <color name="system_secondary_800">#a0a0a0a0</color>
+  <color name="system_secondary_900">#b0b0b0b0</color>
+  <color name="system_secondary_1000">#c0c0c0c0</color>
+  <color name="system_neutral_0">#1f1f1f1f</color>
+  <color name="system_neutral_50">#2f2f2f2f</color>
+  <color name="system_neutral_100">#3f3f3f3f</color>
+  <color name="system_neutral_200">#4f4f4f4f</color>
+  <color name="system_neutral_300">#5f5f5f5f</color>
+  <color name="system_neutral_400">#6f6f6f6f</color>
+  <color name="system_neutral_500">#7f7f7f7f</color>
+  <color name="system_neutral_600">#8f8f8f8f</color>
+  <color name="system_neutral_700">#9f9f9f9f</color>
+  <color name="system_neutral_800">#afafafaf</color>
+  <color name="system_neutral_900">#bfbfbfbf</color>
+  <color name="system_neutral_1000">#cfcfcfcf</color>
+</resources>
diff --git a/core/res/remote_color_resources_res/values/public.xml b/core/res/remote_color_resources_res/values/public.xml
new file mode 100644
index 0000000..e628f09
--- /dev/null
+++ b/core/res/remote_color_resources_res/values/public.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <public-group type="color" first-id="0x0106001d">
+    <public name="system_primary_0" />
+    <public name="system_primary_50" />
+    <public name="system_primary_100" />
+    <public name="system_primary_200" />
+    <public name="system_primary_300" />
+    <public name="system_primary_400" />
+    <public name="system_primary_500" />
+    <public name="system_primary_600" />
+    <public name="system_primary_700" />
+    <public name="system_primary_800" />
+    <public name="system_primary_900" />
+    <public name="system_primary_1000" />
+    <public name="system_secondary_0" />
+    <public name="system_secondary_50" />
+    <public name="system_secondary_100" />
+    <public name="system_secondary_200" />
+    <public name="system_secondary_300" />
+    <public name="system_secondary_400" />
+    <public name="system_secondary_500" />
+    <public name="system_secondary_600" />
+    <public name="system_secondary_700" />
+    <public name="system_secondary_800" />
+    <public name="system_secondary_900" />
+    <public name="system_secondary_1000" />
+    <public name="system_neutral_0" />
+    <public name="system_neutral_50" />
+    <public name="system_neutral_100" />
+    <public name="system_neutral_200" />
+    <public name="system_neutral_300" />
+    <public name="system_neutral_400" />
+    <public name="system_neutral_500" />
+    <public name="system_neutral_600" />
+    <public name="system_neutral_700" />
+    <public name="system_neutral_800" />
+    <public name="system_neutral_900" />
+    <public name="system_neutral_1000" />
+  </public-group>
+</resources>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 6211463..1de1d04 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -17,8 +17,8 @@
 <NotificationHeaderView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/notification_header"
-    android:layout_width="wrap_content"
-    android:layout_height="@dimen/notification_header_solo_height"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/notification_header_height"
     android:layout_marginBottom="@dimen/notification_header_margin_bottom"
     android:clipChildren="false"
     android:gravity="center_vertical"
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index de537b2..2d1c342 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -38,11 +38,7 @@
             android:layout_gravity="top"
             >
 
-            <include
-                layout="@layout/notification_template_header"
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/notification_header_big_height"
-                />
+            <include layout="@layout/notification_template_header" />
 
             <LinearLayout
                 android:id="@+id/notification_main_column"
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index 696cb65..bdd4430 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -36,7 +36,6 @@
         layout="@layout/notification_template_header"
         android:layout_width="match_parent"
         android:layout_height="@dimen/media_notification_header_height"
-        android:layout_gravity="start"
         />
 
     <LinearLayout
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index e1b7bc4..6f3c77f 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -23,11 +23,7 @@
     android:clipChildren="false"
     >
 
-    <include
-        layout="@layout/notification_template_header"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_header_big_height"
-        />
+    <include layout="@layout/notification_template_header" />
 
     <include layout="@layout/notification_template_right_icon" />
 
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 2452a32..2954ba2 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -23,11 +23,7 @@
     android:tag="bigText"
     >
 
-    <include
-        layout="@layout/notification_template_header"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_header_big_height"
-        />
+    <include layout="@layout/notification_template_header" />
 
     <com.android.internal.widget.RemeasuringLinearLayout
         android:id="@+id/notification_action_list_margin_target"
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 7daccd2..baffdd5 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -32,7 +32,8 @@
     />
     <include layout="@layout/notification_template_header"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/media_notification_header_height" />
+        android:layout_height="@dimen/media_notification_header_height"
+        />
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml
index 513da5e..e6d724f 100644
--- a/core/res/res/layout/splash_screen_view.xml
+++ b/core/res/res/layout/splash_screen_view.xml
@@ -21,14 +21,16 @@
     android:orientation="vertical">
 
     <View android:id="@+id/splashscreen_icon_view"
-    android:layout_height="wrap_content"
-    android:layout_width="wrap_content"
-    android:layout_gravity="center"/>
+          android:layout_height="wrap_content"
+          android:layout_width="wrap_content"
+          android:layout_gravity="center"
+          android:contentDescription="@string/splash_screen_view_icon_description"/>
 
     <View android:id="@+id/splashscreen_branding_view"
           android:layout_height="wrap_content"
           android:layout_width="wrap_content"
           android:layout_gravity="center_horizontal|bottom"
-          android:layout_marginBottom="60dp"/>
+          android:layout_marginBottom="60dp"
+          android:contentDescription="@string/splash_screen_view_branding_description"/>
 
 </android.window.SplashScreenView>
\ No newline at end of file
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 29f2b6f..4410e94 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -26,6 +26,10 @@
 
     <color name="notification_default_color_dark">#ddffffff</color>
 
+    <color name="notification_primary_text_color_current">@color/notification_primary_text_color_dark</color>
+    <color name="notification_secondary_text_color_current">@color/notification_secondary_text_color_dark</color>
+    <color name="notification_default_color_current">@color/notification_default_color_dark</color>
+
     <color name="chooser_row_divider">@color/list_divider_color_dark</color>
     <color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color>
     <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color>
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index 952cdd0..3fd82b8 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -28,9 +28,4 @@
     </style>
 
     <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Dialog" />
-
-    <style name="TextAppearance.Material.Notification">
-        <item name="textColor">?attr/textColorPrimary</item>
-        <item name="textSize">@dimen/notification_text_size</item>
-    </style>
 </resources>
\ No newline at end of file
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0ae6a76..45e11ba 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1893,6 +1893,11 @@
             <!-- User data will remain unchanged during rollback. -->
             <enum name="retain" value="2" />
         </attr>
+
+        <!-- Applications can set this attribute to an xml resource within their app where they
+         specified the rules determining which files and directories can be copied from the device
+         as part of backup or transfer operations. -->
+        <attr name="dataExtractionRules" format="reference"/>
     </declare-styleable>
 
     <!-- An attribution is a logical part of an app and is identified by a tag.
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 59c260c..d79c01f 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -143,6 +143,10 @@
     <color name="notification_default_color_dark">@color/primary_text_default_material_light</color>
     <color name="notification_default_color_light">#a3202124</color>
 
+    <color name="notification_primary_text_color_current">@color/notification_primary_text_color_light</color>
+    <color name="notification_secondary_text_color_current">@color/notification_secondary_text_color_light</color>
+    <color name="notification_default_color_current">@color/notification_default_color_light</color>
+
     <color name="notification_default_color">#757575</color> <!-- Gray 600 -->
 
     <color name="notification_action_button_text_color">@color/notification_default_color</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9a917b7..12cb398 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1173,6 +1173,9 @@
     <!-- Default value for LED off time when the battery is low on charge in miliseconds -->
     <integer name="config_notificationsBatteryLedOff">2875</integer>
 
+    <!-- If true, only colorized CallStyle notifications will apply custom colors -->
+    <bool name="config_callNotificationActionColorsRequireColorized">true</bool>
+
     <!-- Number of notifications to keep in the notification service historical archive -->
     <integer name="config_notificationServiceArchiveSize">100</integer>
 
@@ -1950,6 +1953,8 @@
     <string name="config_systemShell" translatable="false">com.android.shell</string>
     <!-- The name of the package that will hold the system contacts role. -->
     <string name="config_systemContacts" translatable="false">com.android.contacts</string>
+    <!-- The name of the package that will hold the speech recognizer role by default. -->
+    <string name="config_systemSpeechRecognizer" translatable="false"></string>
 
     <!-- The name of the package that will be allowed to change its components' label/icon. -->
     <string name="config_overrideComponentUiPackage" translatable="false"></string>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 1ca5498..695a831 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -271,11 +271,8 @@
     <!-- The top margin before the notification progress bar. -->
     <dimen name="notification_progress_margin_top">8dp</dimen>
 
-    <!-- height of the notification header when the notification is alone (minimized / groups) -->
-    <dimen name="notification_header_solo_height">48dp</dimen>
-
-    <!-- height of the notification header when in a "big" layout -->
-    <dimen name="notification_header_big_height">56dp</dimen>
+    <!-- height of the notification header -->
+    <dimen name="notification_header_height">56dp</dimen>
 
     <!-- The height of the background for a notification header on a group -->
     <dimen name="notification_header_background_height">49.5dp</dimen>
@@ -365,7 +362,7 @@
     <dimen name="media_notification_expanded_image_margin_bottom">20dp</dimen>
 
     <!-- The absolute height for the header in a media notification. -->
-    <dimen name="media_notification_header_height">@dimen/notification_header_big_height</dimen>
+    <dimen name="media_notification_header_height">@dimen/notification_header_height</dimen>
 
     <!-- The margin of the content to an image-->
     <dimen name="notification_content_image_margin_end">8dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4732e5f..2004d0a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3084,6 +3084,7 @@
     <public name="hand_minuteTintMode"/>
     <public name="hand_secondTint"/>
     <public name="hand_secondTintMode"/>
+    <public name="dataExtractionRules"/>
   </public-group>
 
   <public-group type="drawable" first-id="0x010800b5">
@@ -3165,6 +3166,8 @@
     <public name="config_customMediaKeyDispatcher" />
     <!-- @hide @SystemApi -->
     <public name="config_customMediaSessionPolicyProvider" />
+    <!-- @hide @SystemApi -->
+    <public name="config_systemSpeechRecognizer" />
   </public-group>
 
   <public-group type="id" first-id="0x01020055">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3505dee..576c44d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1644,7 +1644,7 @@
     <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] -->
     <string name="face_acquired_pan_too_extreme">Turn your head a little less.</string>
     <!-- Message shown during acqusition when the user's face is tilted too high or too low [CHAR LIMIT=50] -->
-    <string name="face_acquired_tilt_too_extreme">Turn your head a little less.</string>
+    <string name="face_acquired_tilt_too_extreme">Tilt your head a little less.</string>
     <!-- Message shown during acquisiton when the user's face is tilted too far left or right [CHAR LIMIT=50] -->
     <string name="face_acquired_roll_too_extreme">Turn your head a little less.</string>
     <!-- Message shown during acquisition when the user's face is obscured [CHAR LIMIT=50] -->
@@ -5849,4 +5849,8 @@
     <!--- Label for notification channel for all sensor privacy related notifications. [CHAR LIMIT=NONE] -->
     <string name="sensor_privacy_notification_channel_label">Sensor Privacy</string>
 
+    <!-- Content description for the icon on the splash screen. [CHAR LIMIT=50] -->
+    <string name="splash_screen_view_icon_description">Application icon</string>
+    <!-- Content description for the branding image on the splash screen. [CHAR LIMIT=50] -->
+    <string name="splash_screen_view_branding_description">Application branding image</string>
 </resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 158289a..3c4a5d4 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -466,18 +466,20 @@
     </style>
 
     <style name="TextAppearance.Material.Notification">
-        <item name="textColor">@color/notification_secondary_text_color_light</item>
+        <item name="textColor">@color/notification_secondary_text_color_current</item>
         <item name="textSize">@dimen/notification_text_size</item>
     </style>
 
     <style name="TextAppearance.Material.Notification.Reply" />
 
     <style name="TextAppearance.Material.Notification.Title">
+        <item name="textColor">@color/notification_primary_text_color_current</item>
         <item name="fontFamily">sans-serif-medium</item>
         <item name="textSize">@dimen/notification_title_text_size</item>
     </style>
 
     <style name="TextAppearance.Material.Notification.BigTitle">
+        <item name="textColor">@color/notification_primary_text_color_current</item>
         <item name="fontFamily">sans-serif-medium</item>
         <item name="textSize">@dimen/notification_big_title_text_size</item>
     </style>
@@ -492,9 +494,8 @@
 
     <style name="TextAppearance.Material.Notification.Time" parent="TextAppearance.Material.Notification.Info" />
 
-    <style name="TextAppearance.Material.Notification.Emphasis">
-        <item name="textColor">#66000000</item>
-    </style>
+    <!-- unused; keep identical to parent -->
+    <style name="TextAppearance.Material.Notification.Emphasis"/>
 
     <style name="TextAppearance.Material.Notification.Conversation.AppName" parent="TextAppearance.Material.Notification.Title">
         <item name="android:textSize">16sp</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e73d6e3..7ad05de 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2286,6 +2286,7 @@
   <java-symbol type="string" name="config_wifi_tether_enable" />
   <java-symbol type="bool" name="config_intrusiveNotificationLed" />
   <java-symbol type="bool" name="config_notificationBadging" />
+  <java-symbol type="bool" name="config_callNotificationActionColorsRequireColorized" />
   <java-symbol type="dimen" name="preference_fragment_padding_bottom" />
   <java-symbol type="dimen" name="preference_fragment_padding_side" />
   <java-symbol type="drawable" name="expander_ic_maximized" />
diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp
index e178776..2789e9f 100644
--- a/core/tests/GameManagerTests/Android.bp
+++ b/core/tests/GameManagerTests/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "FrameworksCoreGameManagerTests",
     // Include all test java files
diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp b/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp
index ade97b8..1c0ea83 100644
--- a/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp
+++ b/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "BatteryStatsLoadTests",
     srcs: ["src/**/*.java"],
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/Android.bp b/core/tests/batterystatstests/BatteryStatsViewer/Android.bp
index 1e0498b..c2e7d81 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/Android.bp
+++ b/core/tests/batterystatstests/BatteryStatsViewer/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "BatteryStatsViewer",
     srcs: ["src/**/*.java"],
diff --git a/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java b/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java
new file mode 100644
index 0000000..1cf4302
--- /dev/null
+++ b/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.SuppressLint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.MergedConfiguration;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+
+import com.google.caliper.AfterExperiment;
+import com.google.caliper.BeforeExperiment;
+
+/**
+ * Benchmark of read/write large Parcelable class. This also shows the performance of different
+ * implementations for nested Parcelable class:
+ * <ul>
+ *   <li>Well-written read/writeFromParcel (direct access)</li>
+ *   <li>read/writeTypedObject (object creation + addition int to indicate nullity)</li>
+ *   <li>read/writeParcelable (object creation + addition type String)</li>
+ * </ul>
+ */
+public class ParcelableBenchmark {
+    private Parcel mParcel;
+
+    @BeforeExperiment
+    protected void setUp() {
+        mParcel = Parcel.obtain();
+    }
+
+    @AfterExperiment
+    protected void tearDown() {
+        mParcel.recycle();
+        mParcel = null;
+    }
+
+    public void timeReadWriteMergedConfiguration(int reps) {
+        final MergedConfiguration mergedConfiguration = new MergedConfiguration();
+        for (int i = 0; i < reps; i++) {
+            mergedConfiguration.writeToParcel(mParcel, 0);
+            mParcel.setDataPosition(0);
+            mergedConfiguration.readFromParcel(mParcel);
+        }
+    }
+
+    public void timeReadWriteInsetsState(int reps) {
+        final InsetsState insetsState = new InsetsState();
+        for (int i = 0; i < InsetsState.SIZE; i++) {
+            insetsState.addSource(new InsetsSource(i));
+        }
+        for (int i = 0; i < reps; i++) {
+            insetsState.writeToParcel(mParcel, 0);
+            mParcel.setDataPosition(0);
+            insetsState.readFromParcel(mParcel);
+        }
+    }
+
+    public void timeReadWritePointArray(int reps) {
+        final PointArray pointArray = new PointArray();
+        for (int i = 0; i < reps; i++) {
+            pointArray.writeToParcel(mParcel, 0);
+            mParcel.setDataPosition(0);
+            pointArray.readFromParcel(mParcel);
+        }
+    }
+
+    public void timeReadWritePointArrayFast(int reps) {
+        final PointArrayFast pointArray = new PointArrayFast();
+        for (int i = 0; i < reps; i++) {
+            pointArray.writeToParcel(mParcel, 0);
+            mParcel.setDataPosition(0);
+            pointArray.readFromParcel(mParcel);
+        }
+    }
+
+    @SuppressLint("ParcelCreator")
+    private static class PointArray implements Parcelable {
+        Rect mBounds = new Rect();
+        Point[] mPoints = new Point[10];
+        {
+            for (int i = 0; i < mPoints.length; i++) {
+                mPoints[i] = new Point();
+            }
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeParcelable(mBounds, flags);
+            dest.writeParcelableArray(mPoints, flags);
+        }
+
+        void readFromParcel(Parcel in) {
+            mBounds = in.readParcelable(Rect.class.getClassLoader());
+            mPoints = in.readParcelableArray(Point.class.getClassLoader(), Point.class);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+    }
+
+    @SuppressLint("ParcelCreator")
+    private static class PointArrayFast extends PointArray {
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            mBounds.writeToParcel(dest, flags);
+            dest.writeTypedArray(mPoints, flags);
+        }
+
+        @Override
+        void readFromParcel(Parcel in) {
+            mBounds.readFromParcel(in);
+            in.readTypedArray(mPoints, Point.CREATOR);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
index 1573c19..7cb6804 100644
--- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
+++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
@@ -37,6 +37,7 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Parcel;
+import android.os.UserHandle;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -124,13 +125,13 @@
     }
 
     @Test
-    public void testUid() {
+    public void testUserHandle() {
         PeopleSpaceTile tile = new PeopleSpaceTile
                 .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps)
-                .setUid(42)
+                .setUserHandle(new UserHandle(0))
                 .build();
 
-        assertThat(tile.getUid()).isEqualTo(42);
+        assertThat(tile.getUserHandle()).isEqualTo(new UserHandle(0));
     }
 
     @Test
@@ -227,7 +228,7 @@
                 .setUserName("name")
                 .setUserIcon(mIcon)
                 .setContactUri(Uri.parse("contact"))
-                .setUid(42)
+                .setUserHandle(new UserHandle(1))
                 .setPackageName("package.name")
                 .setLastInteractionTimestamp(7L)
                 .setIsImportantConversation(true)
@@ -246,7 +247,7 @@
         assertThat(readTile.getUserName()).isEqualTo(tile.getUserName());
         assertThat(readTile.getUserIcon().toString()).isEqualTo(tile.getUserIcon().toString());
         assertThat(readTile.getContactUri()).isEqualTo(tile.getContactUri());
-        assertThat(readTile.getUid()).isEqualTo(tile.getUid());
+        assertThat(readTile.getUserHandle()).isEqualTo(tile.getUserHandle());
         assertThat(readTile.getPackageName()).isEqualTo(tile.getPackageName());
         assertThat(readTile.getLastInteractionTimestamp()).isEqualTo(
                 tile.getLastInteractionTimestamp());
diff --git a/core/tests/coretests/src/android/util/RotationUtilsTest.java b/core/tests/coretests/src/android/util/RotationUtilsTest.java
new file mode 100644
index 0000000..5dbe03e
--- /dev/null
+++ b/core/tests/coretests/src/android/util/RotationUtilsTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import static android.util.RotationUtils.rotateBounds;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Rect;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link RotationUtils}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:RotationUtilsTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RotationUtilsTest {
+
+    @Test
+    public void testRotateBounds() {
+        Rect testParent = new Rect(0, 0, 1000, 600);
+        Rect testInner = new Rect(40, 20, 120, 80);
+
+        Rect testResult = new Rect(testInner);
+        rotateBounds(testResult, testParent, ROTATION_90);
+        assertEquals(new Rect(20, 880, 80, 960), testResult);
+
+        testResult.set(testInner);
+        rotateBounds(testResult, testParent, ROTATION_180);
+        assertEquals(new Rect(880, 520, 960, 580), testResult);
+
+        testResult.set(testInner);
+        rotateBounds(testResult, testParent, ROTATION_270);
+        assertEquals(new Rect(520, 40, 580, 120), testResult);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/BlurAggregatorTest.java b/core/tests/coretests/src/android/view/BlurAggregatorTest.java
new file mode 100644
index 0000000..b01f275
--- /dev/null
+++ b/core/tests/coretests/src/android/view/BlurAggregatorTest.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable.Aggregator;
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable.BlurRegion;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BlurAggregatorTest {
+    private static final int TEST_BLUR_RADIUS = 30;
+    private static final int TEST_FRAME_NUMBER = 1;
+
+    private Context mContext;
+
+    private Aggregator mAggregator;
+    private BackgroundBlurDrawable mDrawable;
+
+    private ViewRootImpl mViewRoot;
+
+    @Before
+    public void setUp() {
+        mContext = getInstrumentation().getTargetContext();
+        getInstrumentation().runOnMainSync(() -> {
+            mViewRoot = new ViewRootImpl(mContext, mContext.getDisplayNoVerify());
+        });
+        mAggregator = new Aggregator(mViewRoot);
+        mDrawable = createTestBackgroundBlurDrawable();
+    }
+
+    private BackgroundBlurDrawable createTestBackgroundBlurDrawable() {
+        final BackgroundBlurDrawable drawable = mAggregator.createBackgroundBlurDrawable(mContext);
+        drawable.setBlurRadius(TEST_BLUR_RADIUS);
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        mAggregator.getBlurRegionsToDispatchToSf(TEST_FRAME_NUMBER, blurRegions, hasUpdates);
+        return drawable;
+    }
+
+    @Test
+    public void testBlurRadiusUpdatePropagatesToRenderThreadIfNeeded() {
+        mDrawable.setBlurRadius(TEST_BLUR_RADIUS);
+        assertFalse(mAggregator.hasUpdates());
+
+        mDrawable.setBlurRadius(0);
+        assertTrue(mAggregator.hasUpdates());
+        BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(0, blurRegions.length);
+        assertFalse(mAggregator.hasUpdates());
+
+        mDrawable.setBlurRadius(TEST_BLUR_RADIUS);
+        assertTrue(mAggregator.hasUpdates());
+        blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+        assertEquals(TEST_BLUR_RADIUS, blurRegions[0].blurRadius);
+        assertFalse(mAggregator.hasUpdates());
+
+    }
+
+    @Test
+    public void testAlphaUpdatePropagatesToRenderThreadIfNeeded() {
+        mDrawable.setAlpha(20);
+        assertTrue(mAggregator.hasUpdates());
+        BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+        assertEquals(20 / 255f, blurRegions[0].alpha);
+        assertFalse(mAggregator.hasUpdates());
+
+        mDrawable.setAlpha(20);
+        assertFalse(mAggregator.hasUpdates());
+
+        mDrawable.setAlpha(0);
+        assertTrue(mAggregator.hasUpdates());
+        blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(0, blurRegions.length);
+        assertFalse(mAggregator.hasUpdates());
+    }
+
+    @Test
+    public void testCornerRadiusUpdatePropagatesToRenderThreadIfNeeded() {
+        mDrawable.setCornerRadius(1f, 2f, 3f, 4f);
+        assertTrue(mAggregator.hasUpdates());
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+        assertEquals(1f, blurRegions[0].cornerRadiusTL);
+        assertEquals(2f, blurRegions[0].cornerRadiusTR);
+        assertEquals(3f, blurRegions[0].cornerRadiusBL);
+        assertEquals(4f, blurRegions[0].cornerRadiusBR);
+        assertFalse(mAggregator.hasUpdates());
+    }
+
+    @Test
+    public void testVisibleUpdatePropagatesToRenderThreadIfNeeded() {
+        mDrawable.setVisible(false, /* restart= */false);
+        assertTrue(mAggregator.hasUpdates());
+        BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(0, blurRegions.length);
+        assertFalse(mAggregator.hasUpdates());
+
+        mDrawable.setVisible(true, /* restart= */ false);
+        assertTrue(mAggregator.hasUpdates());
+        blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+        assertEquals(TEST_BLUR_RADIUS, blurRegions[0].blurRadius);
+        assertFalse(mAggregator.hasUpdates());
+    }
+
+    @Test
+    public void testBlurRegionCopyForRtIsSameIfNoUiUpdates() {
+        mDrawable.setBlurRadius(30);
+        BlurRegion[] blurRegions1 = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions1.length);
+        assertEquals(30, blurRegions1[0].blurRadius);
+
+        BlurRegion[] blurRegions2 = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(blurRegions1, blurRegions2);
+    }
+
+    @Test
+    public void testPositionUpdateAppearsInBlurRegion() {
+        BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+
+        mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
+        mAggregator.getBlurRegionsToDispatchToSf(TEST_FRAME_NUMBER, blurRegions,
+                mAggregator.hasUpdates());
+        assertEquals(1, blurRegions[0].rect.left);
+        assertEquals(2, blurRegions[0].rect.top);
+        assertEquals(3, blurRegions[0].rect.right);
+        assertEquals(4, blurRegions[0].rect.bottom);
+    }
+
+    @Test
+    public void testNoBlurRegionsDispatchedWhenNoUpdates() {
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        assertFalse(hasUpdates);
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+
+        float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, hasUpdates);
+        assertNull(blurRegionsForSf);
+    }
+
+    @Test
+    public void testBlurRegionDispatchedIfOnlyDrawableUpdated() {
+        mDrawable.setBlurRadius(50);
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        assertTrue(hasUpdates);
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+
+        float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, hasUpdates);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(1, blurRegionsForSf.length);
+        assertEquals(50f, blurRegionsForSf[0][0]);
+    }
+
+    @Test
+    public void testBlurRegionDispatchedIfOnlyPositionUpdated() {
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        assertFalse(hasUpdates);
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+
+        mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
+        float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, hasUpdates);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(1, blurRegionsForSf.length);
+        assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]);
+        assertEquals(1f, blurRegionsForSf[0][2]);
+        assertEquals(2f, blurRegionsForSf[0][3]);
+        assertEquals(3f, blurRegionsForSf[0][4]);
+        assertEquals(4f, blurRegionsForSf[0][5]);
+    }
+
+    @Test
+    public void testPositionUpdateIsAppliedInNextFrameIfMissed() {
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        assertFalse(hasUpdates);
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+
+        mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
+        float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER + 1, blurRegions, hasUpdates);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(1, blurRegionsForSf.length);
+        assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]);
+        assertEquals(1f, blurRegionsForSf[0][2]);
+        assertEquals(2f, blurRegionsForSf[0][3]);
+        assertEquals(3f, blurRegionsForSf[0][4]);
+        assertEquals(4f, blurRegionsForSf[0][5]);
+    }
+
+    @Test
+    public void testMultipleDrawablesDispatchedToSfIfOneIsUpdated() {
+        final BackgroundBlurDrawable drawable2 = createTestBackgroundBlurDrawable();
+        drawable2.setBlurRadius(50);
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        assertTrue(hasUpdates);
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(2, blurRegions.length);
+
+        // Check that an update in one of the drawables triggers a dispatch of all blur regions
+        float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, hasUpdates);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(2, blurRegionsForSf.length);
+
+        // Check that the Aggregator deleted all position updates for frame TEST_FRAME_NUMBER
+        blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, /* hasUiUpdates= */ false);
+        assertNull(blurRegionsForSf);
+
+        // Check that a position update triggers a dispatch of all blur regions
+        drawable2.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
+        blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER + 1, blurRegions, hasUpdates);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(2, blurRegionsForSf.length);
+    }
+
+    @Test
+    public void testUiThreadUpdatesDoNotChangeStateOnRenderThread() {
+        // Updates for frame N
+        mDrawable.setBlurRadius(50);
+        mDrawable.setCornerRadius(1, 2, 3, 4);
+        mDrawable.setAlpha(20);
+
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+        assertEquals(50, blurRegions[0].blurRadius);
+        assertEquals(20 / 255f, blurRegions[0].alpha);
+        assertEquals(1f, blurRegions[0].cornerRadiusTL);
+        assertEquals(2f, blurRegions[0].cornerRadiusTR);
+        assertEquals(3f, blurRegions[0].cornerRadiusBL);
+        assertEquals(4f, blurRegions[0].cornerRadiusBR);
+
+        // Updates for frame N+1
+        mDrawable.setBlurRadius(60);
+        mDrawable.setCornerRadius(10, 20, 30, 40);
+        mDrawable.setAlpha(40);
+
+        // Assert state for frame N is untouched
+        assertEquals(50, blurRegions[0].blurRadius);
+        assertEquals(20 / 255f, blurRegions[0].alpha);
+        assertEquals(1f, blurRegions[0].cornerRadiusTL);
+        assertEquals(2f, blurRegions[0].cornerRadiusTR);
+        assertEquals(3f, blurRegions[0].cornerRadiusBL);
+        assertEquals(4f, blurRegions[0].cornerRadiusBR);
+    }
+
+    @Test
+    public void testPositionUpdatesForFutureFramesAreNotAppliedForCurrentFrame() {
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+
+        mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
+        mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER + 1, 5, 6, 7, 8);
+
+        final float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, /* hasUiUpdates= */ false);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(1, blurRegionsForSf.length);
+        // Assert state for first frame is not affected by update for second frame
+        assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]);
+        assertEquals(1f, blurRegionsForSf[0][2]);
+        assertEquals(2f, blurRegionsForSf[0][3]);
+        assertEquals(3f, blurRegionsForSf[0][4]);
+        assertEquals(4f, blurRegionsForSf[0][5]);
+
+        final float[][] blurRegionsForSfForNextFrame = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER + 1, blurRegions, /* hasUiUpdates= */ false);
+        assertNotNull(blurRegionsForSfForNextFrame);
+        assertEquals(1, blurRegionsForSfForNextFrame.length);
+        // Assert second frame updates are applied normally
+        assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSfForNextFrame[0][0]);
+        assertEquals(5f, blurRegionsForSfForNextFrame[0][2]);
+        assertEquals(6f, blurRegionsForSfForNextFrame[0][3]);
+        assertEquals(7f, blurRegionsForSfForNextFrame[0][4]);
+        assertEquals(8f, blurRegionsForSfForNextFrame[0][5]);
+    }
+
+}
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index 80165f0..fa1aa5e 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -9,3 +9,6 @@
 per-file *Insets* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file *View* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file *Visibility* = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Scroll Capture
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
index b9cf1e4..516fb76 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
@@ -16,16 +16,12 @@
 
 package android.view;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
 import static androidx.test.InstrumentationRegistry.getTargetContext;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -35,6 +31,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.os.ICancellationSignal;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -42,9 +39,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
 
 /**
  * Tests of {@link ScrollCaptureConnection}.
@@ -56,261 +51,138 @@
     private final Point mPositionInWindow = new Point(1, 2);
     private final Rect mLocalVisibleRect = new Rect(2, 3, 4, 5);
     private final Rect mScrollBounds = new Rect(3, 4, 5, 6);
+    private final TestScrollCaptureCallback mCallback = new TestScrollCaptureCallback();
+
+    private ScrollCaptureTarget mTarget;
+    private ScrollCaptureConnection mConnection;
 
     private Handler mHandler;
-    private ScrollCaptureTarget mTarget1;
 
     @Mock
     private Surface mSurface;
     @Mock
-    private IScrollCaptureCallbacks mConnectionCallbacks;
+    private IScrollCaptureCallbacks mRemote;
     @Mock
-    private View mMockView1;
-    @Mock
-    private ScrollCaptureCallback mCallback1;
+    private View mView;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mHandler = new Handler(getTargetContext().getMainLooper());
+        when(mSurface.isValid()).thenReturn(true);
+        when(mView.getScrollCaptureHint()).thenReturn(View.SCROLL_CAPTURE_HINT_INCLUDE);
 
-        when(mMockView1.getHandler()).thenReturn(mHandler);
-        when(mMockView1.getScrollCaptureHint()).thenReturn(View.SCROLL_CAPTURE_HINT_INCLUDE);
-
-        mTarget1 = new ScrollCaptureTarget(
-                mMockView1, mLocalVisibleRect, mPositionInWindow, mCallback1);
-        mTarget1.setScrollBounds(mScrollBounds);
-    }
-
-    /** Test the DelayedAction timeout helper class works as expected. */
-    @Test
-    public void testDelayedAction() {
-        Runnable action = Mockito.mock(Runnable.class);
-        ScrollCaptureConnection.DelayedAction delayed =
-                new ScrollCaptureConnection.DelayedAction(mHandler, 100, action);
-        try {
-            Thread.sleep(200);
-        } catch (InterruptedException ex) {
-            /* ignore */
-        }
-        getInstrumentation().waitForIdleSync();
-        assertFalse(delayed.cancel());
-        assertFalse(delayed.timeoutNow());
-        verify(action, times(1)).run();
-    }
-
-    /** Test the DelayedAction cancel() */
-    @Test
-    public void testDelayedAction_cancel() {
-        Runnable action = Mockito.mock(Runnable.class);
-        ScrollCaptureConnection.DelayedAction delayed =
-                new ScrollCaptureConnection.DelayedAction(mHandler, 100, action);
-        try {
-            Thread.sleep(50);
-        } catch (InterruptedException ex) {
-            /* ignore */
-        }
-        assertTrue(delayed.cancel());
-        assertFalse(delayed.timeoutNow());
-        try {
-            Thread.sleep(200);
-        } catch (InterruptedException ex) {
-            /* ignore */
-        }
-        getInstrumentation().waitForIdleSync();
-        verify(action, never()).run();
-    }
-
-    /** Test the DelayedAction timeoutNow() - for testing only */
-    @Test
-    public void testDelayedAction_timeoutNow() {
-        Runnable action = Mockito.mock(Runnable.class);
-        ScrollCaptureConnection.DelayedAction delayed =
-                new ScrollCaptureConnection.DelayedAction(mHandler, 100, action);
-        try {
-            Thread.sleep(50);
-        } catch (InterruptedException ex) {
-            /* ignore */
-        }
-        assertTrue(delayed.timeoutNow());
-        assertFalse(delayed.cancel());
-        getInstrumentation().waitForIdleSync();
-        verify(action, times(1)).run();
+        mTarget = new ScrollCaptureTarget(mView, mLocalVisibleRect, mPositionInWindow, mCallback);
+        mTarget.setScrollBounds(mScrollBounds);
+        mConnection = new ScrollCaptureConnection(Runnable::run, mTarget, mRemote);
     }
 
     /** Test creating a client with valid info */
     @Test
     public void testConstruction() {
-        new ScrollCaptureConnection(mTarget1, mConnectionCallbacks);
+        ScrollCaptureTarget target = new ScrollCaptureTarget(
+                mView, mLocalVisibleRect, mPositionInWindow, mCallback);
+        target.setScrollBounds(new Rect(1, 2, 3, 4));
+        new ScrollCaptureConnection(Runnable::run, target, mRemote);
     }
 
     /** Test creating a client fails if arguments are not valid. */
     @Test
     public void testConstruction_requiresScrollBounds() {
         try {
-            mTarget1.setScrollBounds(null);
-            new ScrollCaptureConnection(mTarget1, mConnectionCallbacks);
+            mTarget.setScrollBounds(null);
+            new ScrollCaptureConnection(Runnable::run, mTarget, mRemote);
             fail("An exception was expected.");
         } catch (RuntimeException ex) {
             // Ignore, expected.
         }
     }
 
-    @SuppressWarnings("SameParameterValue")
-    private static Answer<Void> runRunnable(int arg) {
-        return invocation -> {
-            Runnable r = invocation.getArgument(arg);
-            r.run();
-            return null;
-        };
-    }
-
-    @SuppressWarnings("SameParameterValue")
-    private static Answer<Void> reportBufferSent(int sessionArg, long frameNum, Rect capturedArea) {
-        return invocation -> {
-            ScrollCaptureSession session = invocation.getArgument(sessionArg);
-            session.notifyBufferSent(frameNum, capturedArea);
-            return null;
-        };
-    }
-
     /** @see ScrollCaptureConnection#startCapture(Surface) */
     @Test
     public void testStartCapture() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
+        mConnection.startCapture(mSurface);
 
-        // Have the session start accepted immediately
-        doAnswer(runRunnable(1)).when(mCallback1)
-                .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class));
-        connection.startCapture(mSurface);
-        getInstrumentation().waitForIdleSync();
+        mCallback.completeStartRequest();
+        assertTrue(mConnection.isStarted());
 
-        verify(mCallback1, times(1))
-                .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class));
-        verify(mConnectionCallbacks, times(1)).onCaptureStarted();
-        verifyNoMoreInteractions(mConnectionCallbacks);
+        verify(mRemote, times(1)).onCaptureStarted();
+        verifyNoMoreInteractions(mRemote);
     }
 
     @Test
-    public void testStartCaptureTimeout() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
-        connection.startCapture(mSurface);
+    public void testStartCapture_cancellation() throws Exception {
+        ICancellationSignal signal = mConnection.startCapture(mSurface);
+        signal.cancel();
 
-        // Force timeout to fire
-        connection.getTimeoutAction().timeoutNow();
+        mCallback.completeStartRequest();
+        assertFalse(mConnection.isStarted());
 
-        getInstrumentation().waitForIdleSync();
-        verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class));
-    }
-
-    private void startCapture(ScrollCaptureConnection connection) throws Exception {
-        doAnswer(runRunnable(1)).when(mCallback1)
-                .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class));
-        connection.startCapture(mSurface);
-        getInstrumentation().waitForIdleSync();
-        reset(mCallback1, mConnectionCallbacks);
+        verifyNoMoreInteractions(mRemote);
     }
 
     /** @see ScrollCaptureConnection#requestImage(Rect) */
     @Test
     public void testRequestImage() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
-        startCapture(connection);
+        mConnection.startCapture(mSurface);
+        mCallback.completeStartRequest();
+        reset(mRemote);
 
-        // Stub the callback to complete the request immediately
-        doAnswer(reportBufferSent(/* sessionArg */ 0, /* frameNum */ 1L, new Rect(1, 2, 3, 4)))
-                .when(mCallback1)
-                .onScrollCaptureImageRequest(any(ScrollCaptureSession.class), any(Rect.class));
+        mConnection.requestImage(new Rect(1, 2, 3, 4));
+        mCallback.completeImageRequest(new Rect(1, 2, 3, 4));
 
-        // Make the inbound binder call
-        connection.requestImage(new Rect(1, 2, 3, 4));
-
-        // Wait for handler thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mCallback1, times(1)).onScrollCaptureImageRequest(
-                any(ScrollCaptureSession.class), eq(new Rect(1, 2, 3, 4)));
-
-        // Wait for binder thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mConnectionCallbacks, times(1))
-                .onCaptureBufferSent(eq(1L), eq(new Rect(1, 2, 3, 4)));
-
-        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
+        verify(mRemote, times(1))
+                .onImageRequestCompleted(eq(0), eq(new Rect(1, 2, 3, 4)));
+        verifyNoMoreInteractions(mRemote);
     }
 
     @Test
-    public void testRequestImageTimeout() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
-        startCapture(connection);
+    public void testRequestImage_cancellation() throws Exception {
+        mConnection.startCapture(mSurface);
+        mCallback.completeStartRequest();
+        reset(mRemote);
 
-        // Make the inbound binder call
-        connection.requestImage(new Rect(1, 2, 3, 4));
+        ICancellationSignal signal = mConnection.requestImage(new Rect(1, 2, 3, 4));
+        signal.cancel();
+        mCallback.completeImageRequest(new Rect(1, 2, 3, 4));
 
-        // Wait for handler thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mCallback1, times(1)).onScrollCaptureImageRequest(
-                any(ScrollCaptureSession.class), eq(new Rect(1, 2, 3, 4)));
-
-        // Force timeout to fire
-        connection.getTimeoutAction().timeoutNow();
-        getInstrumentation().waitForIdleSync();
-
-        // (callback not stubbed, does nothing)
-        // Timeout triggers request to end capture
-        verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class));
-        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
+        verifyNoMoreInteractions(mRemote);
     }
 
     /** @see ScrollCaptureConnection#endCapture() */
     @Test
     public void testEndCapture() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
-        startCapture(connection);
+        mConnection.startCapture(mSurface);
+        mCallback.completeStartRequest();
+        reset(mRemote);
 
-        // Stub the callback to complete the request immediately
-        doAnswer(runRunnable(0))
-                .when(mCallback1)
-                .onScrollCaptureEnd(any(Runnable.class));
+        mConnection.endCapture();
+        mCallback.completeEndRequest();
 
-        // Make the inbound binder call
-        connection.endCapture();
+        // And the reply is sent
+        verify(mRemote, times(1)).onCaptureEnded();
+        verifyNoMoreInteractions(mRemote);
+    }
 
-        // Wait for handler thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class));
+    /** @see ScrollCaptureConnection#endCapture() */
+    @Test
+    public void testEndCapture_cancellation() throws Exception {
+        mConnection.startCapture(mSurface);
+        mCallback.completeStartRequest();
+        reset(mRemote);
 
-        // Wait for binder thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mConnectionCallbacks, times(1)).onConnectionClosed();
+        ICancellationSignal signal = mConnection.endCapture();
+        signal.cancel();
+        mCallback.completeEndRequest();
 
-        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
+        verifyNoMoreInteractions(mRemote);
     }
 
     @Test
-    public void testEndCaptureTimeout() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
-        startCapture(connection);
-
-        // Make the inbound binder call
-        connection.endCapture();
-
-        // Wait for handler thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class));
-
-        // Force timeout to fire
-        connection.getTimeoutAction().timeoutNow();
-
-        // Wait for binder thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mConnectionCallbacks, times(1)).onConnectionClosed();
-
-        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
+    public void testClose() throws Exception {
+        mConnection.close();
+        assertFalse(mConnection.isConnected());
+        verifyNoMoreInteractions(mRemote);
     }
+
 }
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
new file mode 100644
index 0000000..cc229e1
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.CancellationSignal;
+import android.os.SystemClock;
+
+import androidx.annotation.NonNull;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Tests of {@link ScrollCaptureTargetSelector}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ScrollCaptureSearchResultsTest {
+
+
+    private static final Rect EMPTY_RECT = new Rect();
+    private static final String TAG = "Test";
+
+    private final Executor mDirectExec = Runnable::run;
+    private Executor mBgExec;
+
+    @Before
+    public void setUp() {
+        mBgExec = Executors.newSingleThreadExecutor();
+    }
+
+    @Test
+    public void testNoTargets() {
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+        assertTrue(results.isComplete());
+
+        assertNull("Expected null due to empty queue", results.getTopResult());
+    }
+
+    @Test
+    public void testNoValidTargets() {
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+
+        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mDirectExec);
+        callback1.setScrollBounds(EMPTY_RECT);
+        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
+                new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+
+        // Supplies scrollBounds = empty rect
+        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec);
+        callback2.setScrollBounds(EMPTY_RECT);
+        ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50),
+                new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE);
+
+        results.addTarget(target1);
+        results.addTarget(target2);
+
+        assertTrue(results.isComplete());
+        assertNull("Expected null due to no valid targets", results.getTopResult());
+    }
+
+    @Test
+    public void testSingleTarget() {
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+        FakeScrollCaptureCallback callback = new FakeScrollCaptureCallback(mDirectExec);
+        ScrollCaptureTarget target = createTarget(callback,
+                new Rect(20, 30, 40, 50), new Point(10, 10),
+                View.SCROLL_CAPTURE_HINT_AUTO);
+        callback.setScrollBounds(new Rect(2, 2, 18, 18));
+
+        results.addTarget(target);
+        assertTrue(results.isComplete());
+
+        ScrollCaptureTarget result = results.getTopResult();
+        assertSame("Excepted the same target as a result", target, result);
+        assertEquals("result has wrong scroll bounds",
+                new Rect(2, 2, 18, 18), result.getScrollBounds());
+    }
+
+    @Test
+    public void testSingleTarget_backgroundThread() throws InterruptedException {
+        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mBgExec);
+        ScrollCaptureTarget target1 = createTarget(callback1,
+                new Rect(20, 30, 40, 50), new Point(10, 10),
+                View.SCROLL_CAPTURE_HINT_AUTO);
+        callback1.setDelay(100);
+        callback1.setScrollBounds(new Rect(2, 2, 18, 18));
+
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+        results.addTarget(target1);
+
+        CountDownLatch latch = new CountDownLatch(1);
+        results.setOnCompleteListener(latch::countDown);
+        if (!latch.await(200, TimeUnit.MILLISECONDS)) {
+            fail("onComplete listener was expected");
+        }
+
+        ScrollCaptureTarget result = results.getTopResult();
+        assertSame("Excepted the single target1 as a result", target1, result);
+        assertEquals("Result has wrong scroll bounds",
+                new Rect(2, 2, 18, 18), result.getScrollBounds());
+    }
+
+    @Test
+    public void testRanking() {
+
+        // 1 - Empty
+        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mDirectExec);
+        callback1.setScrollBounds(EMPTY_RECT);
+        ViewGroup targetView1 = new FakeView(getTargetContext(), 0, 0, 60, 60, 1);
+        ScrollCaptureTarget target1 = createTargetWithView(targetView1, callback1,
+                new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+
+        // 2 - 10x10 + HINT_INCLUDE
+        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec);
+        callback2.setScrollBounds(new Rect(0, 0, 10, 10));
+        ViewGroup targetView2 = new FakeView(getTargetContext(), 0, 0, 60, 60, 2);
+        ScrollCaptureTarget target2 = createTargetWithView(targetView2, callback2,
+                 new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_INCLUDE);
+
+        // 3 - 20x20 + AUTO
+        FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mDirectExec);
+        callback3.setScrollBounds(new Rect(0, 0, 20, 20));
+        ViewGroup targetView3 = new FakeView(getTargetContext(), 0, 0, 60, 60, 3);
+        ScrollCaptureTarget target3 = createTargetWithView(targetView3, callback3,
+                new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+
+        // 4 - 30x30 + AUTO
+        FakeScrollCaptureCallback callback4 = new FakeScrollCaptureCallback(mDirectExec);
+        callback4.setScrollBounds(new Rect(0, 0, 10, 10));
+        ViewGroup targetView4 = new FakeView(getTargetContext(), 0, 0, 60, 60, 4);
+        ScrollCaptureTarget target4 = createTargetWithView(targetView4, callback4,
+                new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+
+        // 5 - 10x10 + child of #4
+        FakeScrollCaptureCallback callback5 = new FakeScrollCaptureCallback(mDirectExec);
+        callback5.setScrollBounds(new Rect(0, 0, 10, 10));
+        ViewGroup targetView5 = new FakeView(getTargetContext(), 0, 0, 60, 60, 5);
+        ScrollCaptureTarget target5 = createTargetWithView(targetView5, callback5,
+                new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+        targetView4.addView(targetView5);
+
+        // 6 - 20x20 + child of #4
+        FakeScrollCaptureCallback callback6 = new FakeScrollCaptureCallback(mDirectExec);
+        callback6.setScrollBounds(new Rect(0, 0, 20, 20));
+        ViewGroup targetView6 = new FakeView(getTargetContext(), 0, 0, 60, 60, 6);
+        ScrollCaptureTarget target6 = createTargetWithView(targetView6, callback6,
+                new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+        targetView4.addView(targetView6);
+
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+        results.addTarget(target1);
+        results.addTarget(target2);
+        results.addTarget(target3);
+        results.addTarget(target4);
+        results.addTarget(target5);
+        results.addTarget(target6);
+        assertTrue(results.isComplete());
+
+        // Verify "top" result
+        assertEquals(target2, results.getTopResult());
+
+        // Verify priority ("best" first)
+        assertThat(results.getTargets())
+                .containsExactly(
+                        target2,
+                        target6,
+                        target5,
+                        target4,
+                        target3,
+                        target1);
+    }
+
+    /**
+     * If a timeout expires, late results are ignored.
+     */
+    @Test
+    public void testTimeout() {
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+
+        // callback 1, 10x10, hint=AUTO, responds after 100ms from bg thread
+        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mBgExec);
+        callback1.setScrollBounds(new Rect(5, 5, 15, 15));
+        callback1.setDelay(100);
+        ScrollCaptureTarget target1 = createTarget(
+                callback1, new Rect(20, 30, 40, 50), new Point(10, 10),
+                View.SCROLL_CAPTURE_HINT_AUTO);
+        results.addTarget(target1);
+
+        // callback 2, 20x20, hint=AUTO, responds after 5s from bg thread
+        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mBgExec);
+        callback2.setScrollBounds(new Rect(0, 0, 20, 20));
+        callback2.setDelay(1000);
+        ScrollCaptureTarget target2 = createTarget(
+                callback2, new Rect(20, 30, 40, 50), new Point(10, 10),
+                View.SCROLL_CAPTURE_HINT_AUTO);
+        results.addTarget(target2);
+
+        // callback 3, 20x20, hint=INCLUDE, responds after 10s from bg thread
+        FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mBgExec);
+        callback3.setScrollBounds(new Rect(0, 0, 20, 20));
+        callback3.setDelay(1500);
+        ScrollCaptureTarget target3 = createTarget(
+                callback3, new Rect(20, 30, 40, 50), new Point(10, 10),
+                View.SCROLL_CAPTURE_HINT_INCLUDE);
+        results.addTarget(target3);
+
+        // callback 1 will be received
+        // callback 2 & 3 will be ignored due to timeout
+        SystemClock.sleep(500);
+        results.finish();
+
+        ScrollCaptureTarget result = results.getTopResult();
+        assertSame("Expected target1 as the result, due to timeouts of others", target1, result);
+        assertEquals("callback1 should have been called",
+                1, callback1.getOnScrollCaptureSearchCount());
+        assertEquals("callback2 should have been called",
+                1, callback2.getOnScrollCaptureSearchCount());
+        assertEquals("callback3 should have been called",
+                1, callback3.getOnScrollCaptureSearchCount());
+
+        assertEquals("result has wrong scroll bounds",
+                new Rect(5, 5, 15, 15), result.getScrollBounds());
+        assertNull("target2 should not have been updated",
+                target2.getScrollBounds());
+        assertNull("target3 should not have been updated",
+                target3.getScrollBounds());
+    }
+
+    @Test
+    public void testWithCallbackMultipleReplies() {
+        // Calls response methods 3 times each
+        ScrollCaptureCallback callback1 = new CallbackStub() {
+            @Override
+            public void onScrollCaptureSearch(@NonNull CancellationSignal signal,
+                    @NonNull Consumer<Rect> onReady) {
+                onReady.accept(new Rect(1, 2, 3, 4));
+                onReady.accept(new Rect(9, 10, 11, 12));
+            }
+        };
+
+        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
+                new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO);
+
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+        results.addTarget(target1);
+        assertTrue(results.isComplete());
+
+        ScrollCaptureTarget result = results.getTopResult();
+        assertSame("Expected target1", target1, result);
+        assertEquals("result has wrong scroll bounds",
+                new Rect(1, 2, 3, 4), result.getScrollBounds());
+    }
+
+    private void setupTargetView(View view, Rect localVisibleRect, int scrollCaptureHint) {
+        view.setScrollCaptureHint(scrollCaptureHint);
+        view.onVisibilityAggregated(true);
+        // Treat any offset as padding, outset localVisibleRect on all sides and use this as
+        // child bounds
+        Rect bounds = new Rect(localVisibleRect);
+        bounds.inset(-bounds.left, -bounds.top, bounds.left, bounds.top);
+        view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        view.onVisibilityAggregated(true);
+    }
+
+    private ScrollCaptureTarget createTarget(ScrollCaptureCallback callback, Rect localVisibleRect,
+            Point positionInWindow, int scrollCaptureHint) {
+        View mockView = new View(getTargetContext());
+        return createTargetWithView(mockView, callback, localVisibleRect, positionInWindow,
+                scrollCaptureHint);
+    }
+
+    private ScrollCaptureTarget createTargetWithView(View view, ScrollCaptureCallback callback,
+            Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) {
+        setupTargetView(view, localVisibleRect, scrollCaptureHint);
+        return new ScrollCaptureTarget(view, localVisibleRect, positionInWindow, callback);
+    }
+
+
+    static class FakeView extends ViewGroup implements ViewParent {
+        FakeView(Context context, int l, int t, int r, int b, int id) {
+            super(context);
+            layout(l, t, r, b);
+            setId(id);
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        }
+    }
+
+    static class FakeScrollCaptureCallback implements ScrollCaptureCallback {
+        private final Executor mExecutor;
+        private Rect mScrollBounds;
+        private long mDelayMillis;
+        private int mOnScrollCaptureSearchCount;
+        FakeScrollCaptureCallback(Executor executor) {
+            mExecutor = executor;
+        }
+        public int getOnScrollCaptureSearchCount() {
+            return mOnScrollCaptureSearchCount;
+        }
+
+        @Override
+        public void onScrollCaptureSearch(CancellationSignal signal, Consumer<Rect> onReady) {
+            mOnScrollCaptureSearchCount++;
+            run(() -> {
+                Rect b = getScrollBounds();
+                onReady.accept(b);
+            });
+        }
+
+        @Override
+        public void onScrollCaptureStart(ScrollCaptureSession session, CancellationSignal signal,
+                Runnable onReady) {
+            run(onReady);
+        }
+
+        @Override
+        public void onScrollCaptureImageRequest(ScrollCaptureSession session,
+                CancellationSignal signal, Rect captureArea, Consumer<Rect> onReady) {
+            run(() -> onReady.accept(captureArea));
+        }
+
+        @Override
+        public void onScrollCaptureEnd(Runnable onReady) {
+            run(onReady);
+        }
+
+        public void setScrollBounds(@Nullable Rect scrollBounds) {
+            mScrollBounds = scrollBounds;
+        }
+
+        public void setDelay(long delayMillis) {
+            mDelayMillis = delayMillis;
+        }
+
+        protected Rect getScrollBounds() {
+            return mScrollBounds;
+        }
+
+        protected void run(Runnable r) {
+            mExecutor.execute(() -> {
+                delay();
+                r.run();
+            });
+        }
+
+        protected void delay() {
+            if (mDelayMillis > 0) {
+                try {
+                    Thread.sleep(mDelayMillis);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+            }
+        }
+    }
+    static class CallbackStub implements ScrollCaptureCallback {
+        @Override
+        public void onScrollCaptureSearch(@NonNull CancellationSignal signal,
+                @NonNull Consumer<Rect> onReady) {
+        }
+
+        @Override
+        public void onScrollCaptureStart(@NonNull ScrollCaptureSession session,
+                @NonNull CancellationSignal signal, @NonNull Runnable onReady) {
+        }
+
+        @Override
+        public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session,
+                @NonNull CancellationSignal signal, @NonNull Rect captureArea,
+                Consumer<Rect> onReady) {
+        }
+
+        @Override
+        public void onScrollCaptureEnd(@NonNull Runnable onReady) {
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java b/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java
deleted file mode 100644
index 8b21b8e..0000000
--- a/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import static androidx.test.InstrumentationRegistry.getTargetContext;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Handler;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.LinkedList;
-import java.util.function.Consumer;
-
-/**
- * Tests of {@link ScrollCaptureTargetResolver}.
- */
-@RunWith(AndroidJUnit4.class)
-public class ScrollCaptureTargetResolverTest {
-
-    private static final long TEST_TIMEOUT_MS = 2000;
-    private static final long RESOLVER_TIMEOUT_MS = 1000;
-
-    private Handler mHandler;
-    private TargetConsumer mTargetConsumer;
-
-    @Before
-    public void setUp() {
-        mTargetConsumer = new TargetConsumer();
-        mHandler = new Handler(getTargetContext().getMainLooper());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testEmptyQueue() throws InterruptedException {
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(new LinkedList<>());
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        // Test only
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertNull("Expected null due to empty queue", result);
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testNoValidTargets() throws InterruptedException {
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-
-        // Supplies scrollBounds = null
-        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback();
-        callback1.setScrollBounds(null);
-        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
-                new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        // Supplies scrollBounds = empty rect
-        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback();
-        callback2.setScrollBounds(new Rect());
-        ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50),
-                new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE);
-
-        targetQueue.add(target1);
-        targetQueue.add(target2);
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        // Test only
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertNull("Expected null due to no valid targets", result);
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testSingleTarget() throws InterruptedException {
-        FakeScrollCaptureCallback callback = new FakeScrollCaptureCallback();
-        ScrollCaptureTarget target = createTarget(callback,
-                new Rect(20, 30, 40, 50), new Point(10, 10),
-                View.SCROLL_CAPTURE_HINT_AUTO);
-        callback.setScrollBounds(new Rect(2, 2, 18, 18));
-
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-        targetQueue.add(target);
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        // Test only
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertSame("Excepted the same target as a result", target, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(2, 2, 18, 18), result.getScrollBounds());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testSingleTarget_backgroundThread() throws InterruptedException {
-        BackgroundTestCallback callback1 = new BackgroundTestCallback();
-        ScrollCaptureTarget target1 = createTarget(callback1,
-                new Rect(20, 30, 40, 50), new Point(10, 10),
-                View.SCROLL_CAPTURE_HINT_AUTO);
-        callback1.setDelay(100);
-        callback1.setScrollBounds(new Rect(2, 2, 18, 18));
-
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-        targetQueue.add(target1);
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        // Test only
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertSame("Excepted the single target1 as a result", target1, result);
-        assertEquals("Result has wrong scroll bounds",
-                new Rect(2, 2, 18, 18), result.getScrollBounds());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testPreferNonEmptyBounds() throws InterruptedException {
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-
-        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback();
-        callback1.setScrollBounds(new Rect());
-        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
-                new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback();
-        callback2.setScrollBounds(new Rect(0, 0, 20, 20));
-        ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50),
-                new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE);
-
-        FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback();
-        callback3.setScrollBounds(null);
-        ScrollCaptureTarget target3 = createTarget(callback3, new Rect(20, 30, 40, 50),
-                new Point(0, 40), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        targetQueue.add(target1);
-        targetQueue.add(target2); // scrollBounds not null or empty()
-        targetQueue.add(target3);
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertEquals("Expected " + target2 + " as a result", target2, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(0, 0, 20, 20), result.getScrollBounds());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testPreferHintInclude() throws InterruptedException {
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-
-        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback();
-        callback1.setScrollBounds(new Rect(0, 0, 20, 20));
-        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
-                new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback();
-        callback2.setScrollBounds(new Rect(1, 1, 19, 19));
-        ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50),
-                new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE);
-
-        FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback();
-        callback3.setScrollBounds(new Rect(2, 2, 18, 18));
-        ScrollCaptureTarget target3 = createTarget(callback3, new Rect(20, 30, 40, 50),
-                new Point(0, 40), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        targetQueue.add(target1);
-        targetQueue.add(target2); // * INCLUDE > AUTO
-        targetQueue.add(target3);
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertEquals("input = " + targetQueue + " Expected " + target2
-                + " as the result, due to hint=INCLUDE", target2, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(1, 1, 19, 19), result.getScrollBounds());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testDescendantPreferred() throws InterruptedException {
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-
-        ViewGroup targetView1 = new FakeRootView(getTargetContext(), 0, 0, 60, 60); // 60x60
-        ViewGroup targetView2 = new FakeRootView(getTargetContext(), 20, 30, 40, 50); // 20x20
-        ViewGroup targetView3 = new FakeRootView(getTargetContext(), 5, 5, 15, 15); // 10x10
-
-        targetView1.addView(targetView2);
-        targetView2.addView(targetView3);
-
-        // Create first target with an unrelated parent
-        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback();
-        callback1.setScrollBounds(new Rect(0, 0, 60, 60));
-        ScrollCaptureTarget target1 = createTargetWithView(targetView1, callback1,
-                new Rect(0, 0, 60, 60),
-                new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        // Create second target associated with a view within parent2
-        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback();
-        callback2.setScrollBounds(new Rect(0, 0, 20, 20));
-        ScrollCaptureTarget target2 = createTargetWithView(targetView2, callback2,
-                new Rect(0, 0, 20, 20),
-                new Point(20, 30), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        // Create third target associated with a view within parent3
-        FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback();
-        callback3.setScrollBounds(new Rect(0, 0, 15, 15));
-        ScrollCaptureTarget target3 = createTargetWithView(targetView3, callback3,
-                new Rect(0, 0, 15, 15),
-                new Point(25, 35), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        targetQueue.add(target1); // auto, 60x60
-        targetQueue.add(target2); // auto, 20x20
-        targetQueue.add(target3); // auto, 15x15 <- innermost scrollable
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        // Test only
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertSame("Expected target3 as the result, due to relation", target3, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(0, 0, 15, 15), result.getScrollBounds());
-    }
-
-    /**
-     * If a timeout expires, late results are ignored.
-     */
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testTimeout() throws InterruptedException {
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-
-        // callback 1, 10x10, hint=AUTO, responds immediately from bg thread
-        BackgroundTestCallback callback1 = new BackgroundTestCallback();
-        callback1.setScrollBounds(new Rect(5, 5, 15, 15));
-        ScrollCaptureTarget target1 = createTarget(
-                callback1, new Rect(20, 30, 40, 50), new Point(10, 10),
-                View.SCROLL_CAPTURE_HINT_AUTO);
-        targetQueue.add(target1);
-
-        // callback 2, 20x20, hint=AUTO, responds after 5s from bg thread
-        BackgroundTestCallback callback2 = new BackgroundTestCallback();
-        callback2.setScrollBounds(new Rect(0, 0, 20, 20));
-        callback2.setDelay(5000);
-        ScrollCaptureTarget target2 = createTarget(
-                callback2, new Rect(20, 30, 40, 50), new Point(10, 10),
-                View.SCROLL_CAPTURE_HINT_AUTO);
-        targetQueue.add(target2);
-
-        // callback 3, 20x20, hint=INCLUDE, responds after 10s from bg thread
-        BackgroundTestCallback callback3 = new BackgroundTestCallback();
-        callback3.setScrollBounds(new Rect(0, 0, 20, 20));
-        callback3.setDelay(10000);
-        ScrollCaptureTarget target3 = createTarget(
-                callback3, new Rect(20, 30, 40, 50), new Point(10, 10),
-                View.SCROLL_CAPTURE_HINT_INCLUDE);
-        targetQueue.add(target3);
-
-        // callback 1 will be received
-        // callback 2 & 3 will be ignored due to timeout
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertSame("Expected target1 as the result, due to timeouts of others", target1, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(5, 5, 15, 15), result.getScrollBounds());
-        assertEquals("callback1 should have been called",
-                1, callback1.getOnScrollCaptureSearchCount());
-        assertEquals("callback2 should have been called",
-                1, callback2.getOnScrollCaptureSearchCount());
-        assertEquals("callback3 should have been called",
-                1, callback3.getOnScrollCaptureSearchCount());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testWithCallbackMultipleReplies() throws InterruptedException {
-        // Calls response methods 3 times each
-        RepeatingCaptureCallback callback1 = new RepeatingCaptureCallback(3);
-        callback1.setScrollBounds(new Rect(2, 2, 18, 18));
-        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
-                new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback();
-        callback2.setScrollBounds(new Rect(0, 0, 20, 20));
-        ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50),
-                new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-        targetQueue.add(target1);
-        targetQueue.add(target2);
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertSame("Expected target2 as the result, due to hint=INCLUDE", target2, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(0, 0, 20, 20), result.getScrollBounds());
-        assertEquals("callback1 should have been called once",
-                1, callback1.getOnScrollCaptureSearchCount());
-        assertEquals("callback2 should have been called once",
-                1, callback2.getOnScrollCaptureSearchCount());
-    }
-
-    private static class TargetConsumer implements Consumer<ScrollCaptureTarget> {
-        volatile ScrollCaptureTarget mResult;
-        int mAcceptCount;
-
-        ScrollCaptureTarget getLastValue() {
-            return mResult;
-        }
-
-        int acceptCount() {
-            return mAcceptCount;
-        }
-
-        @Override
-        public void accept(@Nullable ScrollCaptureTarget t) {
-            mAcceptCount++;
-            mResult = t;
-        }
-    }
-
-    private void setupTargetView(View view, Rect localVisibleRect, int scrollCaptureHint) {
-        view.setScrollCaptureHint(scrollCaptureHint);
-        view.onVisibilityAggregated(true);
-        // Treat any offset as padding, outset localVisibleRect on all sides and use this as
-        // child bounds
-        Rect bounds = new Rect(localVisibleRect);
-        bounds.inset(-bounds.left, -bounds.top, bounds.left, bounds.top);
-        view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
-        view.onVisibilityAggregated(true);
-    }
-
-    private ScrollCaptureTarget createTarget(ScrollCaptureCallback callback, Rect localVisibleRect,
-            Point positionInWindow, int scrollCaptureHint) {
-        View mockView = new View(getTargetContext());
-        return createTargetWithView(mockView, callback, localVisibleRect, positionInWindow,
-                scrollCaptureHint);
-    }
-
-    private ScrollCaptureTarget createTargetWithView(View view, ScrollCaptureCallback callback,
-            Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) {
-        setupTargetView(view, localVisibleRect, scrollCaptureHint);
-        return new ScrollCaptureTarget(view, localVisibleRect, positionInWindow, callback);
-    }
-
-
-    static class FakeRootView extends ViewGroup implements ViewParent {
-        FakeRootView(Context context, int l, int t, int r, int b) {
-            super(context);
-            layout(l, t, r, b);
-        }
-
-        @Override
-        protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        }
-    }
-
-    static class FakeScrollCaptureCallback implements ScrollCaptureCallback {
-        private Rect mScrollBounds;
-        private long mDelayMillis;
-        private int mOnScrollCaptureSearchCount;
-
-        public int getOnScrollCaptureSearchCount() {
-            return mOnScrollCaptureSearchCount;
-        }
-
-        @Override
-        public void onScrollCaptureSearch(Consumer<Rect> onReady) {
-            mOnScrollCaptureSearchCount++;
-            run(() -> {
-                Rect b = getScrollBounds();
-                onReady.accept(b);
-            });
-        }
-
-        @Override
-        public void onScrollCaptureStart(ScrollCaptureSession session, Runnable onReady) {
-            run(onReady);
-        }
-
-        @Override
-        public void onScrollCaptureImageRequest(ScrollCaptureSession session, Rect captureArea) {
-            run(() -> session.notifyBufferSent(0, captureArea));
-        }
-
-        @Override
-        public void onScrollCaptureEnd(Runnable onReady) {
-            run(onReady);
-        }
-
-        public void setScrollBounds(@Nullable Rect scrollBounds) {
-            mScrollBounds = scrollBounds;
-        }
-
-        public void setDelay(long delayMillis) {
-            mDelayMillis = delayMillis;
-        }
-
-        protected Rect getScrollBounds() {
-            return mScrollBounds;
-        }
-
-        protected void run(Runnable r) {
-            delay();
-            r.run();
-        }
-
-        protected void delay() {
-            if (mDelayMillis > 0) {
-                try {
-                    Thread.sleep(mDelayMillis);
-                } catch (InterruptedException e) {
-                    // Ignore
-                }
-            }
-        }
-    }
-
-    static class RepeatingCaptureCallback extends FakeScrollCaptureCallback {
-        private int mRepeatCount;
-
-        RepeatingCaptureCallback(int repeatCount) {
-            mRepeatCount = repeatCount;
-        }
-
-        protected void run(Runnable r) {
-            delay();
-            for (int i = 0; i < mRepeatCount; i++) {
-                r.run();
-            }
-        }
-    }
-
-    /** Response to async calls on an arbitrary background thread */
-    static class BackgroundTestCallback extends FakeScrollCaptureCallback {
-        static int sCount = 0;
-        private void runOnBackgroundThread(Runnable r) {
-            final Runnable target = () -> {
-                delay();
-                r.run();
-            };
-            Thread t = new Thread(target);
-            synchronized (BackgroundTestCallback.this) {
-                sCount++;
-            }
-            t.setName("Background-Thread-" + sCount);
-            t.start();
-        }
-
-        @Override
-        protected void run(Runnable r) {
-            runOnBackgroundThread(r);
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/view/SoundEffectConstantsTest.java b/core/tests/coretests/src/android/view/SoundEffectConstantsTest.java
new file mode 100644
index 0000000..45ffb122
--- /dev/null
+++ b/core/tests/coretests/src/android/view/SoundEffectConstantsTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link SoundEffectConstants}
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:SoundEffectConstantsTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SoundEffectConstantsTest {
+
+    @Test
+    public void testIsNavigationRepeat() {
+        assertTrue(SoundEffectConstants.isNavigationRepeat(
+                SoundEffectConstants.NAVIGATION_REPEAT_RIGHT));
+        assertTrue(SoundEffectConstants.isNavigationRepeat(
+                SoundEffectConstants.NAVIGATION_REPEAT_LEFT));
+        assertTrue(
+                SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_REPEAT_UP));
+        assertTrue(SoundEffectConstants.isNavigationRepeat(
+                SoundEffectConstants.NAVIGATION_REPEAT_DOWN));
+        assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_RIGHT));
+        assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_LEFT));
+        assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_UP));
+        assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_DOWN));
+        assertFalse(SoundEffectConstants.isNavigationRepeat(-1));
+    }
+}
diff --git a/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java b/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java
new file mode 100644
index 0000000..36104cf
--- /dev/null
+++ b/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class SurfaceControlFpsListenerTest {
+
+    @Test
+    public void registersAndUnregisters() {
+
+        SurfaceControlFpsListener listener = new SurfaceControlFpsListener() {
+            @Override
+            public void onFpsReported(float fps) {
+                // Ignore
+            }
+        };
+
+        listener.register(new SurfaceControl());
+
+        listener.unregister();
+    }
+}
diff --git a/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java b/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java
new file mode 100644
index 0000000..1520c6e
--- /dev/null
+++ b/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.junit.Assert.*;
+
+import android.graphics.Rect;
+import android.os.CancellationSignal;
+
+import androidx.annotation.NonNull;
+
+import java.util.function.Consumer;
+
+class TestScrollCaptureCallback implements ScrollCaptureCallback {
+    private Consumer<Rect> mSearchConsumer;
+    private Runnable mStartOnReady;
+    private Consumer<Rect> mImageOnComplete;
+    private Runnable mOnEndReady;
+    private volatile int mModCount;
+
+    @Override
+    public void onScrollCaptureSearch(@NonNull CancellationSignal signal,
+            @NonNull Consumer<Rect> onReady) {
+        mSearchConsumer = onReady;
+        mModCount++;
+    }
+
+    @Override
+    public void onScrollCaptureStart(@NonNull ScrollCaptureSession session,
+            @NonNull CancellationSignal signal, @NonNull Runnable onReady) {
+        mStartOnReady = onReady;
+        mModCount++;
+    }
+
+    @Override
+    public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session,
+            @NonNull CancellationSignal signal, @NonNull Rect captureArea,
+            @NonNull Consumer<Rect> onComplete) {
+        mImageOnComplete = onComplete;
+        mModCount++;
+    }
+
+    @Override
+    public void onScrollCaptureEnd(@NonNull Runnable onReady) {
+        mOnEndReady = onReady;
+    }
+
+    void completeSearchRequest(Rect scrollBounds) {
+        assertNotNull("Did not receive search request", mSearchConsumer);
+        mSearchConsumer.accept(scrollBounds);
+        mModCount++;
+    }
+
+    void verifyZeroInteractions() {
+        assertEquals("Expected zero interactions", 0, mModCount);
+    }
+
+    void completeStartRequest() {
+        assertNotNull("Did not receive start request", mStartOnReady);
+        mStartOnReady.run();
+    }
+
+    void completeImageRequest(Rect captured) {
+        assertNotNull("Did not receive image request", mImageOnComplete);
+        mImageOnComplete.accept(captured);
+    }
+
+    void completeEndRequest() {
+        assertNotNull("Did not receive end request", mOnEndReady);
+        mOnEndReady.run();
+    }
+}
diff --git a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java
index 3af0533..41cd4c5 100644
--- a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java
@@ -19,7 +19,9 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.testng.AssertJUnit.assertSame;
 
@@ -27,19 +29,20 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
-import java.util.LinkedList;
-import java.util.Queue;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Exercises Scroll Capture search in {@link ViewGroup}.
@@ -50,10 +53,7 @@
 @RunWith(MockitoJUnitRunner.class)
 public class ViewGroupScrollCaptureTest {
 
-    @Mock
-    ScrollCaptureCallback mMockCallback;
-    @Mock
-    ScrollCaptureCallback mMockCallback2;
+    private static final Executor DIRECT_EXECUTOR = Runnable::run;
 
     /** Make sure the hint flags are saved and loaded correctly. */
     @Test
@@ -103,25 +103,24 @@
     public void testDispatchScrollCaptureSearch_noCallback_hintAuto() throws Exception {
         final Context context = getInstrumentation().getContext();
         final MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200);
+        TestScrollCaptureCallback callback = new TestScrollCaptureCallback();
 
         // When system internal scroll capture is requested, this callback is returned.
-        viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback);
+        viewGroup.setScrollCaptureCallbackInternalForTest(callback);
 
         Rect localVisibleRect = new Rect(0, 0, 200, 200);
         Point windowOffset = new Point();
-        LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR);
 
         // Dispatch
-        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList);
-
-        // Verify the system checked for fallback support
-        viewGroup.assertDispatchScrollCaptureCount(1);
-        viewGroup.assertLastDispatchScrollCaptureArgs(localVisibleRect, windowOffset);
+        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget);
+        callback.completeSearchRequest(new Rect(1, 2, 3, 4));
+        assertTrue(results.isComplete());
 
         // Verify the target is as expected.
-        assertEquals(1, targetList.size());
-        ScrollCaptureTarget target = targetList.get(0);
-        assertSame("Target has the wrong callback", mMockCallback, target.getCallback());
+        ScrollCaptureTarget target = results.getTopResult();
+        assertNotNull("Target not found", target);
+        assertSame("Target has the wrong callback", callback, target.getCallback());
         assertSame("Target has the wrong View", viewGroup, target.getContainingView());
         assertEquals("Target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO,
                 target.getContainingView().getScrollCaptureHint());
@@ -139,18 +138,22 @@
         final MockViewGroup viewGroup =
                 new MockViewGroup(context, 0, 0, 200, 200, View.SCROLL_CAPTURE_HINT_EXCLUDE);
 
+        TestScrollCaptureCallback callback = new TestScrollCaptureCallback();
+
         // When system internal scroll capture is requested, this callback is returned.
-        viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback);
+        viewGroup.setScrollCaptureCallbackInternalForTest(callback);
 
         Rect localVisibleRect = new Rect(0, 0, 200, 200);
         Point windowOffset = new Point();
-        LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR);
+        assertTrue(results.isComplete());
 
         // Dispatch
-        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList);
+        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget);
+        callback.verifyZeroInteractions();
 
         // Verify the results.
-        assertEquals("Target list size should be zero.", 0, targetList.size());
+        assertTrue("Results should be empty.", results.isEmpty());
     }
 
     /**
@@ -164,27 +167,34 @@
         final Context context = getInstrumentation().getContext();
         MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200);
 
+        TestScrollCaptureCallback callback = new TestScrollCaptureCallback();
+        TestScrollCaptureCallback callback2 = new TestScrollCaptureCallback();
+
         // With an already provided scroll capture callback
-        viewGroup.setScrollCaptureCallback(mMockCallback);
+        viewGroup.setScrollCaptureCallback(callback);
 
         // When system internal scroll capture is requested, this callback is returned.
-        viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback);
+        viewGroup.setScrollCaptureCallbackInternalForTest(callback2);
 
         Rect localVisibleRect = new Rect(0, 0, 200, 200);
         Point windowOffset = new Point();
-        LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR);
 
         // Dispatch to the ViewGroup
-        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList);
-
-        // Confirm that framework support was not requested,
-        // because this view already had a callback set.
-        viewGroup.assertCreateScrollCaptureCallbackInternalCount(0);
+        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget);
+        callback.completeSearchRequest(new Rect(1, 2, 3, 4));
 
         // Verify the target is as expected.
-        assertEquals(1, targetList.size());
-        ScrollCaptureTarget target = targetList.get(0);
-        assertSame("Target has the wrong callback", mMockCallback, target.getCallback());
+        assertFalse(results.isEmpty());
+        assertTrue(results.isComplete());
+
+        // internal framework callback was not requested
+        callback2.verifyZeroInteractions();
+
+        ScrollCaptureTarget target = results.getTopResult();
+
+        assertNotNull("Target not found", target);
+        assertSame("Target has the wrong callback", callback, target.getCallback());
         assertSame("Target has the wrong View", viewGroup, target.getContainingView());
         assertEquals("Target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO,
                 target.getContainingView().getScrollCaptureHint());
@@ -201,22 +211,22 @@
         final Context context = getInstrumentation().getContext();
         MockViewGroup viewGroup =
                 new MockViewGroup(context, 0, 0, 200, 200, View.SCROLL_CAPTURE_HINT_EXCLUDE);
+
+        TestScrollCaptureCallback callback = new TestScrollCaptureCallback();
+
         // With an already provided scroll capture callback
-        viewGroup.setScrollCaptureCallback(mMockCallback);
+        viewGroup.setScrollCaptureCallback(callback);
 
         Rect localVisibleRect = new Rect(0, 0, 200, 200);
         Point windowOffset = new Point();
-        LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR);
 
         // Dispatch to the ViewGroup itself
-        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList);
-
-        // Confirm that framework support was not requested, because this view is excluded.
-        // (And because this view has a callback set.)
-        viewGroup.assertCreateScrollCaptureCallbackInternalCount(0);
+        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget);
+        callback.verifyZeroInteractions();
 
         // Has callback, but hint=excluded, so excluded.
-        assertTrue(targetList.isEmpty());
+        assertNull(results.getTopResult());
     }
 
     /**
@@ -252,37 +262,43 @@
         // |               |          |
         // +---------------+----------+ (200,200)
 
-        // View 1 is clipped and not visible.
+        // View 1 is fully clipped and not visible.
         final MockView view1 = new MockView(context, 0, 0, 200, 25);
         viewGroup.addView(view1);
 
-        // View 2 is partially visible.
+        // View 2 is partially visible. (75x75)
         final MockView view2 = new MockView(context, 0, 25, 150, 100);
         viewGroup.addView(view2);
 
-        // View 3 is partially visible.
+        TestScrollCaptureCallback callback1 = new TestScrollCaptureCallback();
+
+        // View 3 is partially visible (175x50)
         // Pretend View3 can scroll by having framework provide fallback support
         final MockView view3 = new MockView(context, 0, 100, 200, 200);
         // When system internal scroll capture is requested for this view, return this callback.
-        view3.setScrollCaptureCallbackInternalForTest(mMockCallback);
+        view3.setScrollCaptureCallbackInternalForTest(callback1);
         viewGroup.addView(view3);
 
         // View 4 is invisible and should be ignored.
         final MockView view4 = new MockView(context, 150, 25, 200, 100, View.INVISIBLE);
         viewGroup.addView(view4);
 
-        // View 4 is invisible and should be ignored.
+        TestScrollCaptureCallback callback2 = new TestScrollCaptureCallback();
+
+        // View 5 is partially visible and explicitly included via flag. (25x50)
         final MockView view5 = new MockView(context, 150, 100, 200, 200);
-        // When system internal scroll capture is requested for this view, return this callback.
-        view5.setScrollCaptureCallback(mMockCallback2);
+        view5.setScrollCaptureCallback(callback2);
         view5.setScrollCaptureHint(View.SCROLL_CAPTURE_HINT_INCLUDE);
         viewGroup.addView(view5);
 
         // Where targets are added
-        final LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        final ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR);
 
         // Dispatch to the ViewGroup
-        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList);
+        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget);
+        callback1.completeSearchRequest(new Rect(0, 0, 200, 100));
+        callback2.completeSearchRequest(new Rect(0, 0, 50, 100));
+        assertTrue(results.isComplete());
 
         // View 1 is entirely clipped by the parent and not visible, dispatch
         // skips this view entirely.
@@ -317,18 +333,14 @@
         view5.assertCreateScrollCaptureCallbackInternalCount(0);
 
         // 2 views should have been returned, view3 & view5
-        assertEquals(2, targetList.size());
+        assertFalse(results.isEmpty());
+        assertTrue(results.isComplete());
 
-        ScrollCaptureTarget target = targetList.get(0);
-        assertSame("First target has the wrong View", view3, target.getContainingView());
-        assertSame("First target has the wrong callback", mMockCallback, target.getCallback());
-        assertEquals("First target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO,
-                target.getContainingView().getScrollCaptureHint());
-
-        target = targetList.get(1);
-        assertSame("Second target has the wrong View", view5, target.getContainingView());
-        assertSame("Second target has the wrong callback", mMockCallback2, target.getCallback());
-        assertEquals("Second target hint is incorrect", View.SCROLL_CAPTURE_HINT_INCLUDE,
+        ScrollCaptureTarget target = results.getTopResult();
+        assertNotNull("Target not found", target);
+        assertSame("Result is the wrong View", view5, target.getContainingView());
+        assertSame("Result is the wrong callback", callback2, target.getCallback());
+        assertEquals("First target hint is incorrect", View.SCROLL_CAPTURE_HINT_INCLUDE,
                 target.getContainingView().getScrollCaptureHint());
     }
 
@@ -371,7 +383,7 @@
         }
 
         void assertCreateScrollCaptureCallbackInternalCount(int count) {
-            assertEquals("Unexpected number of calls to createScrollCaptureCallackInternal",
+            assertEquals("Unexpected number of calls to createScrollCaptureCallbackInternal",
                     count, mCreateScrollCaptureCallbackInternalCount);
         }
 
@@ -385,11 +397,11 @@
 
         @Override
         public void dispatchScrollCaptureSearch(Rect localVisibleRect, Point windowOffset,
-                Queue<ScrollCaptureTarget> targets) {
+                Consumer<ScrollCaptureTarget> results) {
             mDispatchScrollCaptureSearchNumCalls++;
             mDispatchScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect);
             mDispatchScrollCaptureSearchLastWindowOffset = new Point(windowOffset);
-            super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets);
+            super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results);
         }
 
         @Override
@@ -401,13 +413,31 @@
         }
     }
 
+    static class CallbackStub implements ScrollCaptureCallback {
+
+        @Override
+        public void onScrollCaptureSearch(@NonNull CancellationSignal signal,
+                @NonNull Consumer<Rect> onReady) {
+        }
+
+        @Override
+        public void onScrollCaptureStart(@NonNull ScrollCaptureSession session,
+                @NonNull CancellationSignal signal, @NonNull Runnable onReady) {
+        }
+
+        @Override
+        public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session,
+                @NonNull CancellationSignal signal, @NonNull Rect captureArea,
+                Consumer<Rect> onComplete) {
+        }
+
+        @Override
+        public void onScrollCaptureEnd(@NonNull Runnable onReady) {
+        }
+    };
+
     public static final class MockViewGroup extends ViewGroup {
         private ScrollCaptureCallback mInternalCallback;
-        private int mDispatchScrollCaptureSearchNumCalls;
-        private Rect mDispatchScrollCaptureSearchLastLocalVisibleRect;
-        private Point mDispatchScrollCaptureSearchLastWindowOffset;
-        private int mCreateScrollCaptureCallbackInternalCount;
-
 
         MockViewGroup(Context context) {
             this(context, /* left */ 0, /* top */0, /* right */ 0, /* bottom */0);
@@ -428,16 +458,10 @@
             mInternalCallback = internal;
         }
 
-        void assertDispatchScrollCaptureSearchCount(int count) {
-            assertEquals("Unexpected number of calls to dispatchScrollCaptureSearch",
-                    count, mDispatchScrollCaptureSearchNumCalls);
-        }
-
         @Override
         @Nullable
         public ScrollCaptureCallback createScrollCaptureCallbackInternal(Rect localVisibleRect,
                 Point offsetInWindow) {
-            mCreateScrollCaptureCallbackInternalCount++;
             return mInternalCallback;
         }
 
@@ -445,36 +469,5 @@
         protected void onLayout(boolean changed, int l, int t, int r, int b) {
             // We don't layout this view.
         }
-
-        void assertDispatchScrollCaptureCount(int count) {
-            assertEquals(count, mDispatchScrollCaptureSearchNumCalls);
-        }
-
-        void assertLastDispatchScrollCaptureArgs(Rect localVisibleRect, Point windowOffset) {
-            assertEquals("arg localVisibleRect to dispatchScrollCaptureCallback was incorrect.",
-                    localVisibleRect, mDispatchScrollCaptureSearchLastLocalVisibleRect);
-            assertEquals("arg windowOffset to dispatchScrollCaptureCallback was incorrect.",
-                    windowOffset, mDispatchScrollCaptureSearchLastWindowOffset);
-        }
-        void assertCreateScrollCaptureCallbackInternalCount(int count) {
-            assertEquals("Unexpected number of calls to createScrollCaptureCallackInternal",
-                    count, mCreateScrollCaptureCallbackInternalCount);
-        }
-
-        void reset() {
-            mDispatchScrollCaptureSearchNumCalls = 0;
-            mDispatchScrollCaptureSearchLastWindowOffset = null;
-            mDispatchScrollCaptureSearchLastLocalVisibleRect = null;
-            mCreateScrollCaptureCallbackInternalCount = 0;
-        }
-
-        @Override
-        public void dispatchScrollCaptureSearch(Rect localVisibleRect, Point windowOffset,
-                Queue<ScrollCaptureTarget> targets) {
-            mDispatchScrollCaptureSearchNumCalls++;
-            mDispatchScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect);
-            mDispatchScrollCaptureSearchLastWindowOffset = new Point(windowOffset);
-            super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets);
-        }
     }
 }
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index c67174f..7746bc2 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -207,7 +207,7 @@
         final CountDownLatch latch = new CountDownLatch(1);
         mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() {
             @Override
-            public void onUnavailable() {
+            public void onScrollCaptureResponse(ScrollCaptureResponse response) {
                 latch.countDown();
             }
         });
@@ -220,6 +220,37 @@
     }
 
     /**
+     * Ensure scroll capture request handles a ViewRootImpl with no view tree.
+     */
+    @Test
+    public void requestScrollCapture_timeout() {
+        final View view = new View(mContext);
+        view.setScrollCaptureCallback(new TestScrollCaptureCallback()); // Does nothing
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            WindowManager.LayoutParams wmlp =
+                    new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+            // Set a fake token to bypass 'is your activity running' check
+            wmlp.token = new Binder();
+            view.setLayoutParams(wmlp);
+            mViewRootImpl.setView(view, wmlp, null);
+        });
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        mViewRootImpl.setScrollCaptureRequestTimeout(100);
+        mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() {
+            @Override
+            public void onScrollCaptureResponse(ScrollCaptureResponse response) {
+                latch.countDown();
+            }
+        });
+        try {
+            if (!latch.await(2500, TimeUnit.MILLISECONDS)) {
+                fail("requestScrollCapture timeout did not occur");
+            }
+        } catch (InterruptedException e) { /* ignore */ }
+    }
+
+    /**
      * When window doesn't have focus, keys should be dropped.
      */
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 10aaf31..9cb7876 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -105,7 +105,8 @@
         mTracker = Mockito.spy(
                 new FrameTracker(session, handler, mRenderer, mViewRootWrapper,
                         mSurfaceControlWrapper, mChoreographer, mWrapper,
-                        /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1));
+                        /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1,
+                        null));
         doNothing().when(mTracker).triggerPerfetto();
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index c4c475b..8f4948c 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -96,7 +96,7 @@
                 new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
                 mock(FrameTracker.ChoreographerWrapper.class),
                 new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
-                /*traceThresholdFrameTimeMillis=*/ -1));
+                /*traceThresholdFrameTimeMillis=*/ -1, null));
         doReturn(tracker).when(monitor).createFrameTracker(any(), any());
 
         // Simulate a trace session and see if begin / end are invoked.
@@ -123,21 +123,12 @@
     @Test
     public void testCheckInitState() {
         InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker);
+        View view = new View(mActivity);
+        assertThat(view.isAttachedToWindow()).isFalse();
 
-        // Should return false if invoking begin / end without init invocation.
-        assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+        // Should return false if the view passed in is not attached to window yet.
+        assertThat(monitor.begin(view, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
         assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
-
-        // Everything should be fine if invoking init first.
-        boolean thrown = false;
-        try {
-            assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
-            assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
-        } catch (Exception ex) {
-            thrown = true;
-        } finally {
-            assertThat(thrown).isFalse();
-        }
     }
 
     @Test
@@ -152,7 +143,7 @@
                 new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
                 mock(FrameTracker.ChoreographerWrapper.class),
                 new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
-                /*traceThresholdFrameTimeMillis=*/ -1));
+                /*traceThresholdFrameTimeMillis=*/ -1, null));
         doReturn(tracker).when(monitor).createFrameTracker(any(), any());
 
         assertThat(monitor.begin(mView, session.getCuj())).isTrue();
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 3c7c983..d276bc3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -40,6 +40,7 @@
         BatteryStatsTimeBaseTest.class,
         BatteryStatsTimerTest.class,
         BatteryStatsUidTest.class,
+        BatteryUsageStatsProviderTest.class,
         BatteryUsageStatsTest.class,
         BatteryStatsUserLifecycleTests.class,
         BluetoothPowerCalculatorTest.class,
@@ -71,6 +72,7 @@
         UserPowerCalculatorTest.class,
         VideoPowerCalculatorTest.class,
         WakelockPowerCalculatorTest.class,
+        WifiPowerCalculatorTest.class,
 
         com.android.internal.power.MeasuredEnergyStatsTest.class
     })
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
new file mode 100644
index 0000000..c4b7796
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Process;
+import android.os.UidBatteryConsumer;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BatteryUsageStatsProviderTest {
+    private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+    private static final long MINUTE_IN_MS = 60 * 1000;
+
+    @Rule
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+
+    @Test
+    public void test_getBatteryUsageStats() {
+        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        batteryStats.noteActivityResumedLocked(APP_UID,
+                10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
+        batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_TOP,
+                10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
+        batteryStats.noteActivityPausedLocked(APP_UID,
+                30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+        batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_SERVICE,
+                30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+        batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
+                40 * MINUTE_IN_MS, 40 * MINUTE_IN_MS);
+
+        Context context = InstrumentationRegistry.getContext();
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats);
+
+        final BatteryUsageStats batteryUsageStats =
+                provider.getBatteryUsageStats(BatteryUsageStatsQuery.DEFAULT);
+
+        final List<UidBatteryConsumer> uidBatteryConsumers =
+                batteryUsageStats.getUidBatteryConsumers();
+        final UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(0);
+        assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND))
+                .isEqualTo(20 * MINUTE_IN_MS);
+        assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND))
+                .isEqualTo(10 * MINUTE_IN_MS);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 0cc94fc..23ea508 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -67,33 +67,36 @@
         final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
         final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000);
 
-        final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1);
-        builder.setDischargePercentage(20);
-        builder.setDischargedPowerRange(1000, 2000);
+        final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1)
+                .setDischargePercentage(20)
+                .setDischargedPowerRange(1000, 2000);
 
-        final UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
-                builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid);
-        uidBatteryConsumerBuilder.setPackageWithHighestDrain("foo");
-        uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 300);
-        uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
-        uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
-                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500);
-        uidBatteryConsumerBuilder.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, 600);
-        uidBatteryConsumerBuilder.setUsageDurationMillis(
-                BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700);
-        uidBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis(
-                BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 800);
+        builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid)
+                .setPackageWithHighestDrain("foo")
+                .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000)
+                .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000)
+                .setConsumedPower(
+                        BatteryConsumer.POWER_COMPONENT_USAGE, 300)
+                .setConsumedPower(
+                        BatteryConsumer.POWER_COMPONENT_CPU, 400)
+                .setConsumedPowerForCustomComponent(
+                        BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500)
+                .setUsageDurationMillis(
+                        BatteryConsumer.TIME_COMPONENT_CPU, 600)
+                .setUsageDurationMillis(
+                        BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700)
+                .setUsageDurationForCustomComponentMillis(
+                        BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 800);
 
-        final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
-                builder.getOrCreateSystemBatteryConsumerBuilder(
-                        SystemBatteryConsumer.DRAIN_TYPE_CAMERA);
-        systemBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100);
-        systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
-                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
-        systemBatteryConsumerBuilder.setUsageDurationMillis(
-                BatteryConsumer.TIME_COMPONENT_CPU, 10300);
-        systemBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis(
-                BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400);
+        builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_CAMERA)
+                .setConsumedPower(
+                        BatteryConsumer.POWER_COMPONENT_CPU, 10100)
+                .setConsumedPowerForCustomComponent(
+                        BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
+                .setUsageDurationMillis(
+                        BatteryConsumer.TIME_COMPONENT_CPU, 10300)
+                .setUsageDurationForCustomComponentMillis(
+                        BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400);
 
         return builder.build();
     }
@@ -109,6 +112,10 @@
         for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
             if (uidBatteryConsumer.getUid() == 2000) {
                 assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo");
+                assertThat(uidBatteryConsumer.getTimeInStateMs(
+                        UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(1000);
+                assertThat(uidBatteryConsumer.getTimeInStateMs(
+                        UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(2000);
                 assertThat(uidBatteryConsumer.getConsumedPower(
                         BatteryConsumer.POWER_COMPONENT_USAGE)).isEqualTo(300);
                 assertThat(uidBatteryConsumer.getConsumedPower(
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
new file mode 100644
index 0000000..e100545
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.NetworkCapabilities;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.Process;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
+import android.os.WorkSource;
+import android.os.connectivity.WifiActivityEnergyInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WifiPowerCalculatorTest {
+    private static final double PRECISION = 0.00001;
+
+    private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+
+    @Rule
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0)
+            .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX, 480.0)
+            .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX, 720.0)
+            .setAveragePower(PowerProfile.POWER_WIFI_ON, 360.0)
+            .setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0)
+            .setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0)
+            .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0);
+
+    @Test
+    public void testPowerControllerBasedModel() {
+        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        batteryStats.noteNetworkInterfaceForTransports("wifi",
+                new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+
+        NetworkStats networkStats = new NetworkStats(10000, 1)
+                .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)
+                .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
+        mStatsRule.setNetworkStats(networkStats);
+
+        WifiActivityEnergyInfo energyInfo = new WifiActivityEnergyInfo(10000,
+                WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000);
+
+        batteryStats.updateWifiState(energyInfo, 1000, 1000);
+
+        WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+        mStatsRule.apply(calculator);
+
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+                .isEqualTo(1423);
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(0.2214666);
+
+        SystemBatteryConsumer systemConsumer =
+                mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
+        assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+                .isEqualTo(5577);
+        assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(0.645200);
+    }
+
+    @Test
+    public void testTimerBasedModel() {
+        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        batteryStats.noteNetworkInterfaceForTransports("wifi",
+                new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+
+        NetworkStats networkStats = new NetworkStats(10000, 1)
+                .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)
+                .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
+        mStatsRule.setNetworkStats(networkStats);
+
+        batteryStats.noteWifiScanStartedLocked(APP_UID, 1000, 1000);
+        batteryStats.noteWifiScanStoppedLocked(APP_UID, 2000, 2000);
+        batteryStats.noteWifiRunningLocked(new WorkSource(APP_UID), 3000, 3000);
+        batteryStats.noteWifiStoppedLocked(new WorkSource(APP_UID), 4000, 4000);
+        batteryStats.noteWifiRunningLocked(new WorkSource(Process.WIFI_UID), 1111, 2222);
+        batteryStats.noteWifiStoppedLocked(new WorkSource(Process.WIFI_UID), 3333, 4444);
+
+        // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
+        // on the packet counts.
+        batteryStats.updateWifiState(/* energyInfo */ null, 1000, 1000);
+
+        WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+        mStatsRule.apply(calculator);
+
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+                .isEqualTo(1000);
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(0.8231573);
+
+        SystemBatteryConsumer systemConsumer =
+                mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
+        assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+                .isEqualTo(2222);
+        assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(0.8759216);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index 5fd5a78..d217bce 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -81,11 +81,11 @@
 
         final MeasuredEnergyStats stats
                 = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
-        stats.updateCustomBucket(0, 50, true);
-        stats.updateCustomBucket(1, 60, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+        stats.updateCustomBucket(0, 50);
+        stats.updateCustomBucket(1, 60);
 
         final MeasuredEnergyStats newStats = MeasuredEnergyStats.createFromTemplate(stats);
 
@@ -114,11 +114,11 @@
 
         final MeasuredEnergyStats stats
                 = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
-        stats.updateCustomBucket(0, 50, true);
-        stats.updateCustomBucket(1, 60, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+        stats.updateCustomBucket(0, 50);
+        stats.updateCustomBucket(1, 60);
 
         final Parcel parcel = Parcel.obtain();
         stats.writeToParcel(parcel);
@@ -149,11 +149,11 @@
 
         final MeasuredEnergyStats stats
                 = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
-        stats.updateCustomBucket(0, 50, true);
-        stats.updateCustomBucket(1, 60, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+        stats.updateCustomBucket(0, 50);
+        stats.updateCustomBucket(1, 60);
 
         final Parcel parcel = Parcel.obtain();
         MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
@@ -185,17 +185,17 @@
 
         final MeasuredEnergyStats template
                 = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
-        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
-        template.updateCustomBucket(0, 50, true);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+        template.updateCustomBucket(0, 50);
 
         final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true);
-        stats.updateCustomBucket(0, 315, true);
-        stats.updateCustomBucket(1, 316, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63);
+        stats.updateCustomBucket(0, 315);
+        stats.updateCustomBucket(1, 316);
 
         final Parcel parcel = Parcel.obtain();
         MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
@@ -243,8 +243,8 @@
         final MeasuredEnergyStats stats
                 = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
         // Accumulate energy in one bucket and one custom bucket, the rest should be zero
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
-        stats.updateCustomBucket(1, 60, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200);
+        stats.updateCustomBucket(1, 60);
 
         // Let's try parcelling with including zeros
         final Parcel includeZerosParcel = Parcel.obtain();
@@ -305,11 +305,11 @@
 
         final MeasuredEnergyStats stats
                 = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
-        stats.updateCustomBucket(0, 50, true);
-        stats.updateCustomBucket(1, 60, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+        stats.updateCustomBucket(0, 50);
+        stats.updateCustomBucket(1, 60);
 
         final Parcel parcel = Parcel.obtain();
         MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
@@ -331,14 +331,14 @@
 
         final MeasuredEnergyStats template
                 = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
-        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
-        template.updateCustomBucket(0, 50, true);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+        template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+        template.updateCustomBucket(0, 50);
 
         final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L);
 
         final Parcel parcel = Parcel.obtain();
         MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
@@ -369,14 +369,14 @@
 
         final MeasuredEnergyStats stats
                 = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
 
-        stats.updateCustomBucket(0, 50, true);
-        stats.updateCustomBucket(1, 60, true);
-        stats.updateCustomBucket(0, 3, true);
+        stats.updateCustomBucket(0, 50);
+        stats.updateCustomBucket(1, 60);
+        stats.updateCustomBucket(0, 3);
 
         assertEquals(15, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
         assertEquals(ENERGY_DATA_UNAVAILABLE,
@@ -409,10 +409,10 @@
         final MeasuredEnergyStats stats
                 = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 3);
 
-        stats.updateCustomBucket(0, 50, true);
-        stats.updateCustomBucket(1, 60, true);
-        stats.updateCustomBucket(2, 13, true);
-        stats.updateCustomBucket(1, 70, true);
+        stats.updateCustomBucket(0, 50);
+        stats.updateCustomBucket(1, 60);
+        stats.updateCustomBucket(2, 13);
+        stats.updateCustomBucket(1, 70);
 
         final long[] output = stats.getAccumulatedCustomBucketEnergies();
         assertEquals(3, output.length);
@@ -449,11 +449,11 @@
 
         final MeasuredEnergyStats stats
                 = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
-        stats.updateCustomBucket(0, 50, true);
-        stats.updateCustomBucket(1, 60, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+        stats.updateCustomBucket(0, 50);
+        stats.updateCustomBucket(1, 60);
 
         MeasuredEnergyStats.resetIfNotNull(stats);
         // All energy should be reset to 0
@@ -471,10 +471,10 @@
         }
 
         // Values should increase as usual.
-        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70, true);
+        stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70);
         assertEquals(70L, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
 
-        stats.updateCustomBucket(1, 12, true);
+        stats.updateCustomBucket(1, 12);
         assertEquals(12L, stats.getAccumulatedCustomBucketEnergy(1));
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/view/OWNERS b/core/tests/coretests/src/com/android/internal/view/OWNERS
new file mode 100644
index 0000000..1dad10d
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/view/OWNERS
@@ -0,0 +1,3 @@
+# Scroll Capture
+per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/tests/devicestatetests/Android.bp b/core/tests/devicestatetests/Android.bp
index 409b77b..f7b5932 100644
--- a/core/tests/devicestatetests/Android.bp
+++ b/core/tests/devicestatetests/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "FrameworksCoreDeviceStateManagerTests",
     // Include all test java files
diff --git a/data/etc/car/com.google.android.car.networking.preferenceupdater.xml b/data/etc/car/com.google.android.car.networking.preferenceupdater.xml
index 489ce1b..cdeb8e4 100644
--- a/data/etc/car/com.google.android.car.networking.preferenceupdater.xml
+++ b/data/etc/car/com.google.android.car.networking.preferenceupdater.xml
@@ -16,12 +16,21 @@
   -->
 <permissions>
     <privapp-permissions package="com.google.android.car.networking.preferenceupdater">
-        <permission name="android.permission.ACCESS_NETWORK_STATE"/>
+        <permission name="android.permission.ACCESS_NETWORK_STATE" />
         <permission name="android.permission.ACCESS_WIFI_STATE"/>
         <permission name="android.permission.ACTIVITY_EMBEDDING"/>
+        <permission name="android.permission.CHANGE_NETWORK_STATE" />
+        <permission name="android.permission.CONNECTIVITY_INTERNAL" />
+        <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+        <permission name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERNCE" />
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
-        <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+        <permission name="android.permission.INTERNET" />
         <permission name="android.permission.LOCATION_HARDWARE"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS" />
+        <permission name="android.permission.RECEIVE_BOOT_COMPLETED" />
+        <permission name="android.permission.WAKE_LOCK" />
+        <permission name="android.permission.WRITE_SETTINGS" />
+        <permission name="android.car.permission.CAR_DRIVING_STATE" />
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8fd5d80..3900d7e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -406,8 +406,6 @@
         <permission name="android.permission.SET_WALLPAPER" />
         <permission name="android.permission.SET_WALLPAPER_COMPONENT" />
         <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
-        <!-- Permissions required for Incremental CTS tests -->
-        <permission name="com.android.permission.USE_INSTALLER_V2"/>
         <permission name="android.permission.LOADER_USAGE_STATS"/>
         <!-- Permission required to test system only camera devices. -->
         <permission name="android.permission.SYSTEM_CAMERA" />
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 2db4c5d..4f188cc 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -223,7 +223,7 @@
     <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" />
 
     <family name="serif">
-        <font weight="400" style="normal">NotoSerif-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSerif.ttf</font>
         <font weight="700" style="normal">NotoSerif-Bold.ttf</font>
         <font weight="400" style="italic">NotoSerif-Italic.ttf</font>
         <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font>
@@ -275,144 +275,144 @@
 
     <!-- fallback fonts -->
     <family lang="und-Arab" variant="elegant">
-        <font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font>
+        <font weight="400" style="normal">NotoNaskhArabic.ttf</font>
         <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
     </family>
     <family lang="und-Arab" variant="compact">
-        <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
+        <font weight="400" style="normal">NotoNaskhArabicUI.ttf</font>
         <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
     </family>
     <family lang="und-Ethi">
-	      <font weight="400" style="normal">NotoSansEthiopic-VF.ttf
+	      <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf
 	          <axis tag="wght" stylevalue="400"/>
 	      </font>
-	      <font weight="500" style="normal">NotoSansEthiopic-VF.ttf
+	      <font weight="500" style="normal">NotoSansEthiopic-Regular.ttf
 	          <axis tag="wght" stylevalue="500"/>
 	      </font>
-	      <font weight="600" style="normal">NotoSansEthiopic-VF.ttf
+	      <font weight="600" style="normal">NotoSansEthiopic-Regular.ttf
 	          <axis tag="wght" stylevalue="600"/>
 	      </font>
-	      <font weight="700" style="normal">NotoSansEthiopic-VF.ttf
+	      <font weight="700" style="normal">NotoSansEthiopic-Regular.ttf
 	          <axis tag="wght" stylevalue="700"/>
 	      </font>
-	      <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+	      <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf
 	          <axis tag="wght" stylevalue="400"/>
 	      </font>
-	      <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+	      <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf
 	          <axis tag="wght" stylevalue="500"/>
 	      </font>
-	      <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+	      <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf
 	          <axis tag="wght" stylevalue="600"/>
 	      </font>
-	      <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+	      <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf
 	          <axis tag="wght" stylevalue="700"/>
 	      </font>
     </family>
     <family lang="und-Hebr">
-        <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansHebrew.ttf</font>
         <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
         <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font>
         <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font>
     </family>
     <family lang="und-Thai" variant="elegant">
-        <font weight="400" style="normal">NotoSansThai-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansThai.ttf</font>
         <font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai-Regular.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai.ttf</font>
         <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
     </family>
     <family lang="und-Thai" variant="compact">
-        <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansThaiUI.ttf</font>
         <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
     </family>
     <family lang="und-Armn">
-        <font weight="400" style="normal">NotoSansArmenian-VF.ttf
+        <font weight="400" style="normal">NotoSansArmenian-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansArmenian-VF.ttf
+        <font weight="500" style="normal">NotoSansArmenian-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansArmenian-VF.ttf
+        <font weight="600" style="normal">NotoSansArmenian-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansArmenian-VF.ttf
+        <font weight="700" style="normal">NotoSansArmenian-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+        <font weight="500" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+        <font weight="600" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Geor,und-Geok">
-        <font weight="400" style="normal">NotoSansGeorgian-VF.ttf
+        <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansGeorgian-VF.ttf
+        <font weight="500" style="normal">NotoSansGeorgian-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansGeorgian-VF.ttf
+        <font weight="600" style="normal">NotoSansGeorgian-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansGeorgian-VF.ttf
+        <font weight="700" style="normal">NotoSansGeorgian-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+        <font weight="500" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+        <font weight="600" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Deva" variant="elegant">
-        <font weight="400" style="normal">NotoSansDevanagari-VF.ttf
+        <font weight="400" style="normal">NotoSansDevanagari-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansDevanagari-VF.ttf
+        <font weight="500" style="normal">NotoSansDevanagari-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansDevanagari-VF.ttf
+        <font weight="600" style="normal">NotoSansDevanagari-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansDevanagari-VF.ttf
+        <font weight="700" style="normal">NotoSansDevanagari-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+        <font weight="500" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+        <font weight="600" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Deva" variant="compact">
-        <font weight="400" style="normal">NotoSansDevanagariUI-VF.ttf
+        <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansDevanagariUI-VF.ttf
+        <font weight="500" style="normal">NotoSansDevanagariUI-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansDevanagariUI-VF.ttf
+        <font weight="600" style="normal">NotoSansDevanagariUI-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansDevanagariUI-VF.ttf
+        <font weight="700" style="normal">NotoSansDevanagariUI-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
@@ -421,347 +421,347 @@
          danda characters.
     -->
     <family lang="und-Gujr" variant="elegant">
-        <font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansGujarati.ttf</font>
         <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+        <font weight="500" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+        <font weight="600" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Gujr" variant="compact">
-        <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansGujaratiUI.ttf</font>
         <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
     </family>
     <family lang="und-Guru" variant="elegant">
-        <font weight="400" style="normal">NotoSansGurmukhi-VF.ttf
+        <font weight="400" style="normal">NotoSansGurmukhi-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansGurmukhi-VF.ttf
+        <font weight="500" style="normal">NotoSansGurmukhi-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansGurmukhi-VF.ttf
+        <font weight="600" style="normal">NotoSansGurmukhi-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansGurmukhi-VF.ttf
+        <font weight="700" style="normal">NotoSansGurmukhi-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+        <font weight="500" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+        <font weight="600" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Guru" variant="compact">
-        <font weight="400" style="normal">NotoSansGurmukhiUI-VF.ttf
+        <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansGurmukhiUI-VF.ttf
+        <font weight="500" style="normal">NotoSansGurmukhiUI-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansGurmukhiUI-VF.ttf
+        <font weight="600" style="normal">NotoSansGurmukhiUI-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansGurmukhiUI-VF.ttf
+        <font weight="700" style="normal">NotoSansGurmukhiUI-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Taml" variant="elegant">
-        <font weight="400" style="normal">NotoSansTamil-VF.ttf
+        <font weight="400" style="normal">NotoSansTamil-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansTamil-VF.ttf
+        <font weight="500" style="normal">NotoSansTamil-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansTamil-VF.ttf
+        <font weight="600" style="normal">NotoSansTamil-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansTamil-VF.ttf
+        <font weight="700" style="normal">NotoSansTamil-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+        <font weight="500" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+        <font weight="600" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Taml" variant="compact">
-        <font weight="400" style="normal">NotoSansTamilUI-VF.ttf
+        <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansTamilUI-VF.ttf
+        <font weight="500" style="normal">NotoSansTamilUI-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansTamilUI-VF.ttf
+        <font weight="600" style="normal">NotoSansTamilUI-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansTamilUI-VF.ttf
+        <font weight="700" style="normal">NotoSansTamilUI-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Mlym" variant="elegant">
-        <font weight="400" style="normal">NotoSansMalayalam-VF.ttf
+        <font weight="400" style="normal">NotoSansMalayalam-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansMalayalam-VF.ttf
+        <font weight="500" style="normal">NotoSansMalayalam-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansMalayalam-VF.ttf
+        <font weight="600" style="normal">NotoSansMalayalam-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansMalayalam-VF.ttf
+        <font weight="700" style="normal">NotoSansMalayalam-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+        <font weight="500" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+        <font weight="600" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Mlym" variant="compact">
-        <font weight="400" style="normal">NotoSansMalayalamUI-VF.ttf
+        <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansMalayalamUI-VF.ttf
+        <font weight="500" style="normal">NotoSansMalayalamUI-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansMalayalamUI-VF.ttf
+        <font weight="600" style="normal">NotoSansMalayalamUI-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansMalayalamUI-VF.ttf
+        <font weight="700" style="normal">NotoSansMalayalamUI-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Beng" variant="elegant">
-        <font weight="400" style="normal">NotoSansBengali-VF.ttf
+        <font weight="400" style="normal">NotoSansBengali-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansBengali-VF.ttf
+        <font weight="500" style="normal">NotoSansBengali-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansBengali-VF.ttf
+        <font weight="600" style="normal">NotoSansBengali-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansBengali-VF.ttf
+        <font weight="700" style="normal">NotoSansBengali-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+        <font weight="500" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+        <font weight="600" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Beng" variant="compact">
-        <font weight="400" style="normal">NotoSansBengaliUI-VF.ttf
+        <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansBengaliUI-VF.ttf
+        <font weight="500" style="normal">NotoSansBengaliUI-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansBengaliUI-VF.ttf
+        <font weight="600" style="normal">NotoSansBengaliUI-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansBengaliUI-VF.ttf
+        <font weight="700" style="normal">NotoSansBengaliUI-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Telu" variant="elegant">
-        <font weight="400" style="normal">NotoSansTelugu-VF.ttf
+        <font weight="400" style="normal">NotoSansTelugu-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansTelugu-VF.ttf
+        <font weight="500" style="normal">NotoSansTelugu-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansTelugu-VF.ttf
+        <font weight="600" style="normal">NotoSansTelugu-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansTelugu-VF.ttf
+        <font weight="700" style="normal">NotoSansTelugu-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+        <font weight="500" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+        <font weight="600" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Telu" variant="compact">
-        <font weight="400" style="normal">NotoSansTeluguUI-VF.ttf
+        <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansTeluguUI-VF.ttf
+        <font weight="500" style="normal">NotoSansTeluguUI-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansTeluguUI-VF.ttf
+        <font weight="600" style="normal">NotoSansTeluguUI-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansTeluguUI-VF.ttf
+        <font weight="700" style="normal">NotoSansTeluguUI-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Knda" variant="elegant">
-        <font weight="400" style="normal">NotoSansKannada-VF.ttf
+        <font weight="400" style="normal">NotoSansKannada-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansKannada-VF.ttf
+        <font weight="500" style="normal">NotoSansKannada-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansKannada-VF.ttf
+        <font weight="600" style="normal">NotoSansKannada-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansKannada-VF.ttf
+        <font weight="700" style="normal">NotoSansKannada-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+        <font weight="500" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+        <font weight="600" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Knda" variant="compact">
-        <font weight="400" style="normal">NotoSansKannadaUI-VF.ttf
+        <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansKannadaUI-VF.ttf
+        <font weight="500" style="normal">NotoSansKannadaUI-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansKannadaUI-VF.ttf
+        <font weight="600" style="normal">NotoSansKannadaUI-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansKannadaUI-VF.ttf
+        <font weight="700" style="normal">NotoSansKannadaUI-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Orya" variant="elegant">
-        <font weight="400" style="normal">NotoSansOriya-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansOriya.ttf</font>
         <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font>
     </family>
     <family lang="und-Orya" variant="compact">
-        <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansOriyaUI.ttf</font>
         <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
     </family>
     <family lang="und-Sinh" variant="elegant">
-        <font weight="400" style="normal">NotoSansSinhala-VF.ttf
+        <font weight="400" style="normal">NotoSansSinhala-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansSinhala-VF.ttf
+        <font weight="500" style="normal">NotoSansSinhala-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansSinhala-VF.ttf
+        <font weight="600" style="normal">NotoSansSinhala-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansSinhala-VF.ttf
+        <font weight="700" style="normal">NotoSansSinhala-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+        <font weight="500" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+        <font weight="600" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Sinh" variant="compact">
-        <font weight="400" style="normal">NotoSansSinhalaUI-VF.ttf
+        <font weight="400" style="normal">NotoSansSinhalaUI-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansSinhalaUI-VF.ttf
+        <font weight="500" style="normal">NotoSansSinhalaUI-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansSinhalaUI-VF.ttf
+        <font weight="600" style="normal">NotoSansSinhalaUI-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansSinhalaUI-VF.ttf
+        <font weight="700" style="normal">NotoSansSinhalaUI-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Khmr" variant="elegant">
-        <font weight="100" style="normal">NotoSansKhmer-VF.ttf
+        <font weight="100" style="normal">NotoSansKhmer-Regular.ttf
             <axis tag="wdth" stylevalue="100.0"/>
             <axis tag="wght" stylevalue="26.0"/>
         </font>
-        <font weight="200" style="normal">NotoSansKhmer-VF.ttf
+        <font weight="200" style="normal">NotoSansKhmer-Regular.ttf
             <axis tag="wdth" stylevalue="100.0"/>
             <axis tag="wght" stylevalue="39.0"/>
         </font>
-        <font weight="300" style="normal">NotoSansKhmer-VF.ttf
+        <font weight="300" style="normal">NotoSansKhmer-Regular.ttf
             <axis tag="wdth" stylevalue="100.0"/>
             <axis tag="wght" stylevalue="58.0"/>
         </font>
-        <font weight="400" style="normal">NotoSansKhmer-VF.ttf
+        <font weight="400" style="normal">NotoSansKhmer-Regular.ttf
             <axis tag="wdth" stylevalue="100.0"/>
             <axis tag="wght" stylevalue="90.0"/>
         </font>
-        <font weight="500" style="normal">NotoSansKhmer-VF.ttf
+        <font weight="500" style="normal">NotoSansKhmer-Regular.ttf
             <axis tag="wdth" stylevalue="100.0"/>
             <axis tag="wght" stylevalue="108.0"/>
         </font>
-        <font weight="600" style="normal">NotoSansKhmer-VF.ttf
+        <font weight="600" style="normal">NotoSansKhmer-Regular.ttf
             <axis tag="wdth" stylevalue="100.0"/>
             <axis tag="wght" stylevalue="128.0"/>
         </font>
-        <font weight="700" style="normal">NotoSansKhmer-VF.ttf
+        <font weight="700" style="normal">NotoSansKhmer-Regular.ttf
             <axis tag="wdth" stylevalue="100.0"/>
             <axis tag="wght" stylevalue="151.0"/>
         </font>
-        <font weight="800" style="normal">NotoSansKhmer-VF.ttf
+        <font weight="800" style="normal">NotoSansKhmer-Regular.ttf
             <axis tag="wdth" stylevalue="100.0"/>
             <axis tag="wght" stylevalue="169.0"/>
         </font>
-        <font weight="900" style="normal">NotoSansKhmer-VF.ttf
+        <font weight="900" style="normal">NotoSansKhmer-Regular.ttf
             <axis tag="wdth" stylevalue="100.0"/>
             <axis tag="wght" stylevalue="190.0"/>
         </font>
@@ -769,17 +769,17 @@
         <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font>
     </family>
     <family lang="und-Khmr" variant="compact">
-        <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansKhmerUI.ttf</font>
         <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
     </family>
     <family lang="und-Laoo" variant="elegant">
-        <font weight="400" style="normal">NotoSansLao-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansLao.ttf</font>
         <font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao-Regular.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao.ttf</font>
         <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
     </family>
     <family lang="und-Laoo" variant="compact">
-        <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansLaoUI.ttf</font>
         <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
     </family>
     <family lang="und-Mymr" variant="elegant">
@@ -795,56 +795,56 @@
         <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
     </family>
     <family lang="und-Thaa">
-        <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansThaana.ttf</font>
         <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
     </family>
     <family lang="und-Cham">
-        <font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansCham.ttf</font>
         <font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
     </family>
     <family lang="und-Ahom">
         <font weight="400" style="normal">NotoSansAhom-Regular.otf</font>
     </family>
     <family lang="und-Adlm">
-        <font weight="400" style="normal">NotoSansAdlam-VF.ttf
+        <font weight="400" style="normal">NotoSansAdlam-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansAdlam-VF.ttf
+        <font weight="500" style="normal">NotoSansAdlam-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansAdlam-VF.ttf
+        <font weight="600" style="normal">NotoSansAdlam-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansAdlam-VF.ttf
+        <font weight="700" style="normal">NotoSansAdlam-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Avst">
-        <font weight="400" style="normal">NotoSansAvestan-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansAvestan.ttf</font>
     </family>
     <family lang="und-Bali">
-        <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansBalinese.ttf</font>
     </family>
     <family lang="und-Bamu">
-        <font weight="400" style="normal">NotoSansBamum-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansBamum.ttf</font>
     </family>
     <family lang="und-Batk">
-        <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansBatak.ttf</font>
     </family>
     <family lang="und-Brah">
-        <font weight="400" style="normal">NotoSansBrahmi-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansBrahmi.ttf</font>
     </family>
     <family lang="und-Bugi">
-        <font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansBuginese.ttf</font>
     </family>
     <family lang="und-Buhd">
-        <font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansBuhid.ttf</font>
     </family>
     <family lang="und-Cans">
-        <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansCanadianAboriginal.ttf</font>
     </family>
     <family lang="und-Cari">
-        <font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansCarian.ttf</font>
     </family>
     <family lang="und-Cakm">
         <font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
@@ -853,164 +853,164 @@
         <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
     </family>
     <family lang="und-Copt">
-        <font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansCoptic.ttf</font>
     </family>
     <family lang="und-Xsux">
-        <font weight="400" style="normal">NotoSansCuneiform-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansCuneiform.ttf</font>
     </family>
     <family lang="und-Cprt">
-        <font weight="400" style="normal">NotoSansCypriot-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansCypriot.ttf</font>
     </family>
     <family lang="und-Dsrt">
-        <font weight="400" style="normal">NotoSansDeseret-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansDeseret.ttf</font>
     </family>
     <family lang="und-Egyp">
-        <font weight="400" style="normal">NotoSansEgyptianHieroglyphs-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansEgyptianHieroglyphs.ttf</font>
     </family>
     <family lang="und-Elba">
         <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font>
     </family>
     <family lang="und-Glag">
-        <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansGlagolitic.ttf</font>
     </family>
     <family lang="und-Goth">
-        <font weight="400" style="normal">NotoSansGothic-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansGothic.ttf</font>
     </family>
     <family lang="und-Hano">
-        <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansHanunoo.ttf</font>
     </family>
     <family lang="und-Armi">
-        <font weight="400" style="normal">NotoSansImperialAramaic-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansImperialAramaic.ttf</font>
     </family>
     <family lang="und-Phli">
-        <font weight="400" style="normal">NotoSansInscriptionalPahlavi-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansInscriptionalPahlavi.ttf</font>
     </family>
     <family lang="und-Prti">
-        <font weight="400" style="normal">NotoSansInscriptionalParthian-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansInscriptionalParthian.ttf</font>
     </family>
     <family lang="und-Java">
         <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font>
     </family>
     <family lang="und-Kthi">
-        <font weight="400" style="normal">NotoSansKaithi-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansKaithi.ttf</font>
     </family>
     <family lang="und-Kali">
-        <font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansKayahLi.ttf</font>
     </family>
     <family lang="und-Khar">
-        <font weight="400" style="normal">NotoSansKharoshthi-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansKharoshthi.ttf</font>
     </family>
     <family lang="und-Lepc">
-        <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansLepcha.ttf</font>
     </family>
     <family lang="und-Limb">
-        <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansLimbu.ttf</font>
     </family>
     <family lang="und-Linb">
-        <font weight="400" style="normal">NotoSansLinearB-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansLinearB.ttf</font>
     </family>
     <family lang="und-Lisu">
-        <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansLisu.ttf</font>
     </family>
     <family lang="und-Lyci">
-        <font weight="400" style="normal">NotoSansLycian-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansLycian.ttf</font>
     </family>
     <family lang="und-Lydi">
-        <font weight="400" style="normal">NotoSansLydian-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansLydian.ttf</font>
     </family>
     <family lang="und-Mand">
-        <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansMandaic.ttf</font>
     </family>
     <family lang="und-Mtei">
-        <font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansMeeteiMayek.ttf</font>
     </family>
     <family lang="und-Talu">
-        <font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansNewTaiLue.ttf</font>
     </family>
     <family lang="und-Nkoo">
-        <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansNKo.ttf</font>
     </family>
     <family lang="und-Ogam">
-        <font weight="400" style="normal">NotoSansOgham-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansOgham.ttf</font>
     </family>
     <family lang="und-Olck">
-        <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansOlChiki.ttf</font>
     </family>
     <family lang="und-Ital">
-        <font weight="400" style="normal">NotoSansOldItalic-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansOldItalic.ttf</font>
     </family>
     <family lang="und-Xpeo">
-        <font weight="400" style="normal">NotoSansOldPersian-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansOldPersian.ttf</font>
     </family>
     <family lang="und-Sarb">
-        <font weight="400" style="normal">NotoSansOldSouthArabian-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansOldSouthArabian.ttf</font>
     </family>
     <family lang="und-Orkh">
-        <font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansOldTurkic.ttf</font>
     </family>
     <family lang="und-Osge">
         <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
     </family>
     <family lang="und-Osma">
-        <font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansOsmanya.ttf</font>
     </family>
     <family lang="und-Phnx">
-        <font weight="400" style="normal">NotoSansPhoenician-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansPhoenician.ttf</font>
     </family>
     <family lang="und-Rjng">
-        <font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansRejang.ttf</font>
     </family>
     <family lang="und-Runr">
-        <font weight="400" style="normal">NotoSansRunic-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansRunic.ttf</font>
     </family>
     <family lang="und-Samr">
-        <font weight="400" style="normal">NotoSansSamaritan-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansSamaritan.ttf</font>
     </family>
     <family lang="und-Saur">
-        <font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansSaurashtra.ttf</font>
     </family>
     <family lang="und-Shaw">
-        <font weight="400" style="normal">NotoSansShavian-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansShavian.ttf</font>
     </family>
     <family lang="und-Sund">
-        <font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansSundanese.ttf</font>
     </family>
     <family lang="und-Sylo">
-        <font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansSylotiNagri.ttf</font>
     </family>
     <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. -->
     <family lang="und-Syre">
-        <font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansSyriacEstrangela.ttf</font>
     </family>
     <family lang="und-Syrn">
-        <font weight="400" style="normal">NotoSansSyriacEastern-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansSyriacEastern.ttf</font>
     </family>
     <family lang="und-Syrj">
-        <font weight="400" style="normal">NotoSansSyriacWestern-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansSyriacWestern.ttf</font>
     </family>
     <family lang="und-Tglg">
-        <font weight="400" style="normal">NotoSansTagalog-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansTagalog.ttf</font>
     </family>
     <family lang="und-Tagb">
-        <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansTagbanwa.ttf</font>
     </family>
     <family lang="und-Lana">
-        <font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansTaiTham.ttf</font>
     </family>
     <family lang="und-Tavt">
-        <font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansTaiViet.ttf</font>
     </family>
     <family lang="und-Tibt">
-        <font weight="400" style="normal">NotoSerifTibetan-VF.ttf
+        <font weight="400" style="normal">NotoSerifTibetan-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSerifTibetan-VF.ttf
+        <font weight="500" style="normal">NotoSerifTibetan-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSerifTibetan-VF.ttf
+        <font weight="600" style="normal">NotoSerifTibetan-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSerifTibetan-VF.ttf
+        <font weight="700" style="normal">NotoSerifTibetan-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
@@ -1018,29 +1018,29 @@
         <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font>
     </family>
     <family lang="und-Ugar">
-        <font weight="400" style="normal">NotoSansUgaritic-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansUgaritic.ttf</font>
     </family>
     <family lang="und-Vaii">
-        <font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansVai.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
     </family>
     <family lang="zh-Hans">
-        <font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
-        <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+        <font weight="400" style="normal" index="2">NotoSansCJKjp-Regular.otc</font>
+        <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
     </family>
     <family lang="zh-Hant,zh-Bopo">
-        <font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
-        <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+        <font weight="400" style="normal" index="3">NotoSansCJKjp-Regular.otc</font>
+        <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
     </family>
     <family lang="ja">
-        <font weight="400" style="normal" index="0">NotoSansCJK-Regular.ttc</font>
-        <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+        <font weight="400" style="normal" index="0">NotoSansCJKjp-Regular.otc</font>
+        <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
     </family>
     <family lang="ko">
-        <font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
-        <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+        <font weight="400" style="normal" index="1">NotoSansCJKjp-Regular.otc</font>
+        <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
     </family>
     <family lang="und-Zsye">
         <font weight="400" style="normal">NotoColorEmoji.ttf</font>
@@ -1053,16 +1053,16 @@
         override the East Asian punctuation for Chinese.
     -->
     <family lang="und-Tale">
-        <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansTaiLe.ttf</font>
     </family>
     <family lang="und-Yiii">
-        <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansYi.ttf</font>
     </family>
     <family lang="und-Mong">
-        <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansMongolian.ttf</font>
     </family>
     <family lang="und-Phag">
-        <font weight="400" style="normal">NotoSansPhagsPa-Regular.ttf</font>
+        <font weight="400" style="normal">NotoSansPhagsPa.ttf</font>
     </family>
     <family lang="und-Hluw">
         <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font>
@@ -1152,72 +1152,72 @@
         <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font>
     </family>
     <family lang="und-Medf">
-        <font weight="400" style="normal">NotoSansMedefaidrin-VF.ttf
+        <font weight="400" style="normal">NotoSansMedefaidrin-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansMedefaidrin-VF.ttf
+        <font weight="500" style="normal">NotoSansMedefaidrin-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansMedefaidrin-VF.ttf
+        <font weight="600" style="normal">NotoSansMedefaidrin-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansMedefaidrin-VF.ttf
+        <font weight="700" style="normal">NotoSansMedefaidrin-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Soyo">
-        <font weight="400" style="normal">NotoSansSoyombo-VF.ttf
+        <font weight="400" style="normal">NotoSansSoyombo-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansSoyombo-VF.ttf
+        <font weight="500" style="normal">NotoSansSoyombo-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansSoyombo-VF.ttf
+        <font weight="600" style="normal">NotoSansSoyombo-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansSoyombo-VF.ttf
+        <font weight="700" style="normal">NotoSansSoyombo-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Takr">
-        <font weight="400" style="normal">NotoSansTakri-VF.ttf
+        <font weight="400" style="normal">NotoSansTakri-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSansTakri-VF.ttf
+        <font weight="500" style="normal">NotoSansTakri-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSansTakri-VF.ttf
+        <font weight="600" style="normal">NotoSansTakri-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSansTakri-VF.ttf
+        <font weight="700" style="normal">NotoSansTakri-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Hmnp">
-        <font weight="400" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+        <font weight="400" style="normal">NotoSerifHmongNyiakeng-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+        <font weight="500" style="normal">NotoSerifHmongNyiakeng-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+        <font weight="600" style="normal">NotoSerifHmongNyiakeng-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+        <font weight="700" style="normal">NotoSerifHmongNyiakeng-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
     <family lang="und-Yezi">
-        <font weight="400" style="normal">NotoSerifYezidi-VF.ttf
+        <font weight="400" style="normal">NotoSerifYezidi-Regular.ttf
             <axis tag="wght" stylevalue="400"/>
         </font>
-        <font weight="500" style="normal">NotoSerifYezidi-VF.ttf
+        <font weight="500" style="normal">NotoSerifYezidi-Regular.ttf
             <axis tag="wght" stylevalue="500"/>
         </font>
-        <font weight="600" style="normal">NotoSerifYezidi-VF.ttf
+        <font weight="600" style="normal">NotoSerifYezidi-Regular.ttf
             <axis tag="wght" stylevalue="600"/>
         </font>
-        <font weight="700" style="normal">NotoSerifYezidi-VF.ttf
+        <font weight="700" style="normal">NotoSerifYezidi-Regular.ttf
             <axis tag="wght" stylevalue="700"/>
         </font>
     </family>
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index d59abb5..189be53 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -69,28 +69,26 @@
     // animation & drawing system
     public static final int VSYNC = 3;
 
-    // The time of the oldest input event
-    public static final int OLDEST_INPUT_EVENT = 4;
-
-    // The time of the newest input event
-    public static final int NEWEST_INPUT_EVENT = 5;
+    // The id of the input event that caused the current frame
+    public static final int INPUT_EVENT_ID = 4;
 
     // When input event handling started
-    public static final int HANDLE_INPUT_START = 6;
+    public static final int HANDLE_INPUT_START = 5;
 
     // When animation evaluations started
-    public static final int ANIMATION_START = 7;
+    public static final int ANIMATION_START = 6;
 
     // When ViewRootImpl#performTraversals() started
-    public static final int PERFORM_TRAVERSALS_START = 8;
+    public static final int PERFORM_TRAVERSALS_START = 7;
 
     // When View:draw() started
-    public static final int DRAW_START = 9;
+    public static final int DRAW_START = 8;
 
     // When the frame needs to be ready by
-    public static final int FRAME_DEADLINE = 10;
+    public static final int FRAME_DEADLINE = 9;
 
     // Must be the last one
+    // This value must be in sync with `UI_THREAD_FRAME_INFO_SIZE` in FrameInfo.h
     private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1;
 
     /** checkstyle */
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 32c777c..c807882 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -172,9 +172,9 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public long native_instance;
+    public final long native_instance;
 
-    private Runnable mCleaner;
+    private final Runnable mCleaner;
 
     /** @hide */
     @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC})
@@ -189,9 +189,9 @@
     /** @hide */ public static final int STYLE_MASK = 0x03;
 
     @UnsupportedAppUsage
-    private @Style int mStyle = 0;
+    private @Style final int mStyle;
 
-    private @IntRange(from = 0, to = FontStyle.FONT_WEIGHT_MAX) int mWeight = 0;
+    private @IntRange(from = 0, to = FontStyle.FONT_WEIGHT_MAX) final int mWeight;
 
     // Value for weight and italic. Indicates the value is resolved by font metadata.
     // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
@@ -207,6 +207,7 @@
     private static final int STYLE_NORMAL = 0;
     private static final int STYLE_ITALIC = 1;
 
+    @GuardedBy("this")
     private int[] mSupportedAxes;
     private static final int[] EMPTY_AXES = {};
 
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index f708298..684eebe 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -37,8 +37,6 @@
     void setUserSelectable(String alias, boolean isUserSelectable);
 
     int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
-    int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
-            out KeymasterCertificateChain chain);
     boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain);
 
     // APIs used by CertInstaller and DevicePolicyManager
@@ -65,6 +63,7 @@
     AppUriAuthenticationPolicy getCredentialManagementAppPolicy();
     String getPredefinedAliasForPackageAndUri(String packageName, in Uri uri);
     void removeCredentialManagementApp();
+    boolean isCredentialManagementApp(String packageName);
 
     // APIs used by KeyChainActivity
     void setGrant(int uid, String alias, boolean value);
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 97819c5..65a81cd 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -44,6 +44,8 @@
 import android.security.keystore.AndroidKeyStoreProvider;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
 import android.security.keystore.KeyProperties;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
 import android.util.Log;
 
 import com.android.org.conscrypt.TrustedCertificateStore;
@@ -421,6 +423,15 @@
      * credentials. This is limited to unmanaged devices. The authentication policy must be
      * provided to be able to make this request successfully.
      *
+     * <p> This intent should be started using {@link Activity#startActivityForResult(Intent, int)}
+     * to verify whether the request was successful and whether the user accepted or denied the
+     * request. If the user successfully receives and accepts the request, the result code will be
+     * {@link Activity#RESULT_OK}, otherwise the result code will be
+     * {@link Activity#RESULT_CANCELED}.
+     *
+     * <p> {@link KeyChain#isCredentialManagementApp(Context)} should be used to determine whether
+     * an app is already the credential management app.
+     *
      * @param policy The authentication policy determines which alias for a private key and
      *               certificate pair should be used for authentication.
      */
@@ -589,6 +600,55 @@
     }
 
     /**
+     * Check whether the caller is the credential management app {@link CredentialManagementApp}.
+     * The credential management app has the ability to manage the user's KeyChain credentials
+     * on unmanaged devices.
+     *
+     * <p> {@link KeyChain#createManageCredentialsIntent} should be used by an app to request to
+     * become the credential management app. The user must approve this request before the app can
+     * manage the user's credentials. There can only be one credential management on the device.
+     *
+     * @return {@code true} if the caller is the credential management app.
+     */
+    public static boolean isCredentialManagementApp(@NonNull Context context) {
+        boolean isCredentialManagementApp = false;
+        try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
+            isCredentialManagementApp = keyChainConnection.getService()
+                    .isCredentialManagementApp(context.getPackageName());
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while checking whether the caller is the "
+                    + "credential management app.", e);
+        } catch (SecurityException e) {
+            isCredentialManagementApp = false;
+        }
+        return isCredentialManagementApp;
+    }
+
+    /**
+     * Called by the credential management app to get the authentication policy
+     * {@link AppUriAuthenticationPolicy}.
+     *
+     * @return the credential management app's authentication policy.
+     * @throws SecurityException if the caller is not the credential management app.
+     */
+    @NonNull
+    public static AppUriAuthenticationPolicy getCredentialManagementAppPolicy(
+            @NonNull Context context) throws SecurityException {
+        AppUriAuthenticationPolicy policy = null;
+        try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) {
+            policy = keyChainConnection.getService().getCredentialManagementAppPolicy();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(
+                    "Interrupted while getting credential management app policy.", e);
+        }
+        return policy;
+    }
+
+    /**
      * Set a credential management app. The credential management app has the ability to manage
      * the user's KeyChain credentials on unmanaged devices.
      *
@@ -682,6 +742,33 @@
         return null;
     }
 
+    /**
+     * This prefix is used to disambiguate grant aliase strings from normal key alias strings.
+     * Technically, a key alias string can use the same prefix. However, a collision does not
+     * lead to privilege escalation, because grants are access controlled in the Keystore daemon.
+     * @hide
+     */
+    public static final String GRANT_ALIAS_PREFIX = "ks2_keychain_grant_id:";
+
+    private static KeyDescriptor getGrantDescriptor(String keyid) {
+        KeyDescriptor result = new KeyDescriptor();
+        result.domain = Domain.GRANT;
+        result.blob = null;
+        result.alias = null;
+        try {
+            result.nspace = Long.parseUnsignedLong(
+                    keyid.substring(GRANT_ALIAS_PREFIX.length()), 16 /* radix */);
+        } catch (NumberFormatException e) {
+            return null;
+        }
+        return result;
+    }
+
+    /** @hide */
+    public static String getGrantString(KeyDescriptor key) {
+        return String.format(GRANT_ALIAS_PREFIX + "%016X", key.nspace);
+    }
+
     /** @hide */
     @Nullable @WorkerThread
     public static KeyPair getKeyPair(@NonNull Context context, @NonNull String alias)
@@ -705,11 +792,23 @@
 
         if (keyId == null) {
             return null;
+        }
+
+        if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+            try {
+                return android.security.keystore2.AndroidKeyStoreProvider
+                        .loadAndroidKeyStoreKeyPairFromKeystore(
+                                KeyStore2.getInstance(),
+                                getGrantDescriptor(keyId));
+            } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
+                throw new KeyChainException(e);
+            }
         } else {
             try {
                 return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
                         KeyStore.getInstance(), keyId, KeyStore.UID_SELF);
-            } catch (RuntimeException | UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
+            } catch (RuntimeException | UnrecoverableKeyException
+                    | KeyPermanentlyInvalidatedException e) {
                 throw new KeyChainException(e);
             }
         }
@@ -827,11 +926,8 @@
     @Deprecated
     public static boolean isBoundKeyAlgorithm(
             @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) {
-        if (!isKeyAlgorithmSupported(algorithm)) {
-            return false;
-        }
-
-        return KeyStore.getInstance().isHardwareBacked(algorithm);
+        // All supported algorithms are hardware backed. Individual keys may not be.
+        return true;
     }
 
     /** @hide */
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index e101115..35059ac 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -273,10 +273,10 @@
     /** @hide **/
     @NonNull
     public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
-            @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)
+            @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
         AndroidKeyStoreKey key =
-                loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace);
+                loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
         if (key instanceof AndroidKeyStorePublicKey) {
             AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key;
             return new KeyPair(publicKey, publicKey.getPrivateKey());
@@ -336,7 +336,7 @@
     @NonNull
     public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
             @NonNull KeyStore2 keyStore, @NonNull String alias, int namespace)
-            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException  {
+            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
 
         KeyDescriptor descriptor = new KeyDescriptor();
         if (namespace == KeyProperties.NAMESPACE_APPLICATION) {
@@ -348,6 +348,18 @@
         }
         descriptor.alias = alias;
         descriptor.blob = null;
+
+        final AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
+        if (key instanceof AndroidKeyStorePublicKey) {
+            return ((AndroidKeyStorePublicKey) key).getPrivateKey();
+        } else {
+            return key;
+        }
+    }
+
+    private static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
+            @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
+            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
         KeyEntryResponse response = null;
         try {
             response = keyStore.getKeyEntry(descriptor);
@@ -397,7 +409,7 @@
                 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
             return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata,
                     new KeyStoreSecurityLevel(response.iSecurityLevel),
-                    keymasterAlgorithm).getPrivateKey();
+                    keymasterAlgorithm);
         } else {
             throw new UnrecoverableKeyException("Key algorithm unknown");
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
index 59271e9..e6e6d4a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
@@ -22,7 +22,7 @@
 
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
-import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedStackListener;
+import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedTaskListener;
 
 /**
  * The singleton wrapper to communicate between WindowManagerService and WMShell features
@@ -46,7 +46,7 @@
      * Adds a pinned stack listener, which will receive updates from the window manager service
      * along with any other pinned stack listeners that were added via this method.
      */
-    public void addPinnedStackListener(PinnedStackListener listener)
+    public void addPinnedStackListener(PinnedTaskListener listener)
             throws RemoteException {
         mPinnedStackListenerForwarder.addListener(listener);
         mPinnedStackListenerForwarder.register(DEFAULT_DISPLAY);
@@ -55,7 +55,7 @@
     /**
      * Removes a pinned stack listener.
      */
-    public void removePinnedStackListener(PinnedStackListener listener) {
+    public void removePinnedStackListener(PinnedTaskListener listener) {
         mPinnedStackListenerForwarder.removeListener(listener);
     }
 
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 79f9dcd..562b32b 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
@@ -34,6 +34,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.split.SplitLayout;
 
@@ -57,12 +58,14 @@
     private final AppPairsController mController;
     private final SyncTransactionQueue mSyncQueue;
     private final DisplayController mDisplayController;
+    private final DisplayImeController mDisplayImeController;
     private SplitLayout mSplitLayout;
 
     AppPair(AppPairsController controller) {
         mController = controller;
         mSyncQueue = controller.getSyncTransactionQueue();
         mDisplayController = controller.getDisplayController();
+        mDisplayImeController = controller.getDisplayImeController();
     }
 
     int getRootTaskId() {
@@ -97,7 +100,7 @@
         mSplitLayout = new SplitLayout(TAG + "SplitDivider",
                 mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
                 mRootTaskInfo.configuration, this /* layoutChangeListener */,
-                b -> b.setParent(mRootTaskLeash));
+                b -> b.setParent(mRootTaskLeash), mDisplayImeController);
 
         final WindowContainerToken token1 = task1.token;
         final WindowContainerToken token2 = task2.token;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
index 0415f12..b159333 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
@@ -23,17 +23,16 @@
 import android.util.SparseArray;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 
 import java.io.PrintWriter;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}.
@@ -50,12 +49,15 @@
     // Active app-pairs mapped by root task id key.
     private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>();
     private final DisplayController mDisplayController;
+    private final DisplayImeController mDisplayImeController;
 
     public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
-                DisplayController displayController, ShellExecutor mainExecutor) {
+            DisplayController displayController, ShellExecutor mainExecutor,
+            DisplayImeController displayImeController) {
         mTaskOrganizer = organizer;
         mSyncQueue = syncQueue;
         mDisplayController = displayController;
+        mDisplayImeController = displayImeController;
         mMainExecutor = mainExecutor;
     }
 
@@ -130,18 +132,22 @@
         }
     }
 
-    public ShellTaskOrganizer getTaskOrganizer() {
+    ShellTaskOrganizer getTaskOrganizer() {
         return mTaskOrganizer;
     }
 
-    public SyncTransactionQueue getSyncTransactionQueue() {
+    SyncTransactionQueue getSyncTransactionQueue() {
         return mSyncQueue;
     }
 
-    public DisplayController getDisplayController() {
+    DisplayController getDisplayController() {
         return mDisplayController;
     }
 
+    DisplayImeController getDisplayImeController() {
+        return mDisplayImeController;
+    }
+
     public void dump(@NonNull PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         final String childPrefix = innerPrefix + "  ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 047df5b..1320780 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1213,7 +1213,7 @@
 
     /** PinnedStackListener that dispatches IME visibility updates to the stack. */
     //TODO(b/170442945): Better way to do this / insets listener?
-    private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener {
+    private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedTaskListener {
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
             if (mStackView != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index 19c3cf9..7d5c9f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -227,24 +227,29 @@
     /*
      * Fade animation for consecutive flyouts.
      */
-    void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, float stackY) {
+    void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, PointF stackPos) {
         final Runnable afterFadeOut = () -> {
             updateFlyoutMessage(flyoutMessage, parentWidth);
             // Wait for TextViews to layout with updated height.
             post(() -> {
-                mFlyoutY = stackY + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f;
-                fade(true /* in */, () -> {} /* after */);
+                fade(true /* in */, stackPos, () -> {} /* after */);
             } /* after */ );
         };
-        fade(false /* in */, afterFadeOut);
+        fade(false /* in */, stackPos, afterFadeOut);
     }
 
     /*
      * Fade-out above or fade-in from below.
      */
-    private void fade(boolean in, Runnable afterFade) {
+    private void fade(boolean in, PointF stackPos, Runnable afterFade) {
+        mFlyoutY = stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f;
+
         setAlpha(in ? 0f : 1f);
         setTranslationY(in ? mFlyoutY + FLYOUT_FADE_Y : mFlyoutY);
+        mRestingTranslationX = mArrowPointingLeft
+                ? stackPos.x + mBubbleSize + mFlyoutSpaceFromBubble
+                : stackPos.x - getWidth() - mFlyoutSpaceFromBubble;
+        setTranslationX(mRestingTranslationX);
         animate()
                 .alpha(in ? 1f : 0f)
                 .setDuration(in ? FLYOUT_FADE_IN_DURATION : FLYOUT_FADE_OUT_DURATION)
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 a3edc20..e99669f5 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
@@ -1539,19 +1539,16 @@
      * Update bubble order and pointer position.
      */
     public void updateBubbleOrder(List<Bubble> bubbles) {
-        if (isExpansionAnimating()) {
-            return;
-        }
         final Runnable reorder = () -> {
             for (int i = 0; i < bubbles.size(); i++) {
                 Bubble bubble = bubbles.get(i);
                 mBubbleContainer.reorderView(bubble.getIconView(), i);
             }
         };
-        if (mIsExpanded) {
+        if (mIsExpanded || isExpansionAnimating()) {
             reorder.run();
             updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
-        } else {
+        } else if (!isExpansionAnimating()) {
             List<View> bubbleViews = bubbles.stream()
                     .map(b -> b.getIconView()).collect(Collectors.toList());
             mStackAnimationController.animateReorder(bubbleViews, reorder);
@@ -2434,7 +2431,7 @@
 
             if (mFlyout.getVisibility() == View.VISIBLE) {
                 mFlyout.animateUpdate(bubble.getFlyoutMessage(), getWidth(),
-                        mStackAnimationController.getStackPosition().y);
+                        mStackAnimationController.getStackPosition());
             } else {
                 mFlyout.setVisibility(INVISIBLE);
                 mFlyout.setupFlyoutStartingAsDot(bubble.getFlyoutMessage(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 58a4baf..f118b1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -22,6 +22,8 @@
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
 import static android.os.Process.SYSTEM_UID;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
+import static android.util.RotationUtils.rotateBounds;
+import static android.util.RotationUtils.rotateInsets;
 import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_270;
@@ -37,7 +39,6 @@
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
-import android.util.RotationUtils;
 import android.util.Size;
 import android.view.Display;
 import android.view.DisplayCutout;
@@ -49,6 +50,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * Contains information about the layout-properties of a display. This refers to internal layout
@@ -81,6 +83,31 @@
     private boolean mHasStatusBar = false;
     private int mNavBarFrameHeight = 0;
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof DisplayLayout)) return false;
+        final DisplayLayout other = (DisplayLayout) o;
+        return mUiMode == other.mUiMode
+                && mWidth == other.mWidth
+                && mHeight == other.mHeight
+                && Objects.equals(mCutout, other.mCutout)
+                && mRotation == other.mRotation
+                && mDensityDpi == other.mDensityDpi
+                && Objects.equals(mNonDecorInsets, other.mNonDecorInsets)
+                && Objects.equals(mStableInsets, other.mStableInsets)
+                && mHasNavigationBar == other.mHasNavigationBar
+                && mHasStatusBar == other.mHasStatusBar
+                && mNavBarFrameHeight == other.mNavBarFrameHeight;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi,
+                mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar,
+                mNavBarFrameHeight);
+    }
+
     /**
      * Create empty layout.
      */
@@ -241,38 +268,6 @@
     }
 
     /**
-     * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta`
-     * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and
-     * remains at 0,0 after rotation.
-     *
-     * Only 'bounds' is mutated.
-     */
-    public static void rotateBounds(Rect inOutBounds, Rect parentBounds, int delta) {
-        int rdelta = ((delta % 4) + 4) % 4;
-        int origLeft = inOutBounds.left;
-        switch (rdelta) {
-            case 0:
-                return;
-            case 1:
-                inOutBounds.left = inOutBounds.top;
-                inOutBounds.top = parentBounds.right - inOutBounds.right;
-                inOutBounds.right = inOutBounds.bottom;
-                inOutBounds.bottom = parentBounds.right - origLeft;
-                return;
-            case 2:
-                inOutBounds.left = parentBounds.right - inOutBounds.right;
-                inOutBounds.right = parentBounds.right - origLeft;
-                return;
-            case 3:
-                inOutBounds.left = parentBounds.bottom - inOutBounds.bottom;
-                inOutBounds.bottom = inOutBounds.right;
-                inOutBounds.right = parentBounds.bottom - inOutBounds.top;
-                inOutBounds.top = origLeft;
-                return;
-        }
-    }
-
-    /**
      * Calculates the stable insets if we already have the non-decor insets.
      */
     private static void convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets,
@@ -359,8 +354,7 @@
         if (rotation == ROTATION_0) {
             return computeSafeInsets(cutout, displayWidth, displayHeight);
         }
-        final Insets waterfallInsets =
-                RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
+        final Insets waterfallInsets = rotateInsets(cutout.getWaterfallInsets(), rotation);
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
         Rect[] cutoutRects = cutout.getBoundingRectsAll();
         final Rect[] newBounds = new Rect[cutoutRects.length];
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index fb70cbe5..4bb8e9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -39,12 +39,12 @@
 import android.view.IWindowSessionCallback;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.view.ScrollCaptureResponse;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewRootImpl;
 import android.view.WindowManager;
 import android.view.WindowlessWindowManager;
 import android.window.ClientWindowFrames;
@@ -371,7 +371,11 @@
         @Override
         public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
             try {
-                callbacks.onUnavailable();
+                callbacks.onScrollCaptureResponse(
+                        new ScrollCaptureResponse.Builder()
+                                .setDescription("Not Implemented")
+                                .build());
+
             } catch (RemoteException ex) {
                 // ignore
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
index e94080a..3b67005 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
@@ -227,7 +227,7 @@
     }
 
     @Override
-    public void onActivityDismissingDockedStack() {
+    public void onActivityDismissingDockedTask() {
         mMainHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK);
     }
 
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 c27c929..b9fdaa1 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
@@ -36,11 +36,13 @@
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.DisplayImeController;
 
 /**
- * Stack divider for app pair.
+ * Divider for multi window splits.
  */
-public class DividerView extends FrameLayout implements View.OnTouchListener {
+public class DividerView extends FrameLayout implements View.OnTouchListener,
+        DisplayImeController.ImePositionProcessor {
     public static final long TOUCH_ANIMATION_DURATION = 150;
     public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
 
@@ -56,6 +58,7 @@
     private boolean mMoving;
     private int mStartPos;
     private GestureDetector mDoubleTapDetector;
+    private boolean mInteractive;
 
     public DividerView(@NonNull Context context) {
         super(context);
@@ -91,12 +94,19 @@
         mTouchElevation = getResources().getDimensionPixelSize(
                 R.dimen.docked_stack_divider_lift_elevation);
         mDoubleTapDetector = new GestureDetector(getContext(), new DoubleTapListener());
+        mInteractive = true;
         setOnTouchListener(this);
     }
 
     @Override
+    public void onImeVisibilityChanged(int displayId, boolean isShowing) {
+        if (displayId != getDisplay().getDisplayId()) return;
+        setInteractive(!isShowing);
+    }
+
+    @Override
     public boolean onTouch(View v, MotionEvent event) {
-        if (mSplitLayout == null) {
+        if (mSplitLayout == null || !mInteractive) {
             return false;
         }
 
@@ -202,6 +212,13 @@
         mViewHost.relayout(lp);
     }
 
+    private void setInteractive(boolean interactive) {
+        if (interactive == mInteractive) return;
+        mInteractive = interactive;
+        releaseTouching();
+        mHandle.setVisibility(mInteractive ? View.VISIBLE : View.INVISIBLE);
+    }
+
     private boolean isLandscape() {
         return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
     }
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 60231df..bacff78 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
@@ -35,6 +35,7 @@
 
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.DisplayImeController;
 
 /**
  * Records and handles layout of splits. Helps to calculate proper bounds when configuration or
@@ -59,11 +60,13 @@
 
     public SplitLayout(String windowName, Context context, Configuration configuration,
             LayoutChangeListener layoutChangeListener,
-            SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks) {
+            SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
+            DisplayImeController displayImeController) {
         mContext = context.createConfigurationContext(configuration);
         mLayoutChangeListener = layoutChangeListener;
         mSplitWindowManager = new SplitWindowManager(
-                windowName, mContext, configuration, parentContainerCallbacks);
+                windowName, mContext, configuration, parentContainerCallbacks,
+                displayImeController);
 
         mDividerWindowWidth = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 87f0c25..f6efb01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -46,6 +46,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayImeController;
 
 /**
  * Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split.
@@ -53,23 +54,27 @@
 public final class SplitWindowManager extends WindowlessWindowManager {
     private static final String TAG = SplitWindowManager.class.getSimpleName();
 
+    private final String mWindowName;
+    private final DisplayImeController mDisplayImeController;
     private final ParentContainerCallbacks mParentContainerCallbacks;
     private Context mContext;
     private SurfaceControlViewHost mViewHost;
     private SurfaceControl mLeash;
     private boolean mResizingSplits;
-    private final String mWindowName;
+    private DividerView mDividerView;
 
     public interface ParentContainerCallbacks {
         void attachToParentSurface(SurfaceControl.Builder b);
     }
 
     public SplitWindowManager(String windowName, Context context, Configuration config,
-            ParentContainerCallbacks parentContainerCallbacks) {
+            ParentContainerCallbacks parentContainerCallbacks,
+            DisplayImeController displayImeController) {
         super(config, null /* rootSurface */, null /* hostInputToken */);
         mContext = context.createConfigurationContext(config);
         mParentContainerCallbacks = parentContainerCallbacks;
         mWindowName = windowName;
+        mDisplayImeController = displayImeController;
     }
 
     @Override
@@ -103,14 +108,16 @@
 
     /** Inflates {@link DividerView} on to the root surface. */
     void init(SplitLayout splitLayout) {
-        if (mViewHost == null) {
-            mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+        if (mDividerView != null || mViewHost != null) {
+            throw new UnsupportedOperationException(
+                    "Try to inflate divider view again without release first");
         }
 
-        final Rect dividerBounds = splitLayout.getDividerBounds();
-        final DividerView dividerView = (DividerView) LayoutInflater.from(mContext)
+        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+        mDividerView = (DividerView) LayoutInflater.from(mContext)
                 .inflate(R.layout.split_divider, null /* root */);
 
+        final Rect dividerBounds = splitLayout.getDividerBounds();
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 dividerBounds.width(), dividerBounds.height(), TYPE_DOCK_DIVIDER,
                 FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH
@@ -119,8 +126,9 @@
         lp.token = new Binder();
         lp.setTitle(mWindowName);
         lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
-        mViewHost.setView(dividerView, lp);
-        dividerView.setup(splitLayout, mViewHost);
+        mViewHost.setView(mDividerView, lp);
+        mDividerView.setup(splitLayout, mViewHost);
+        mDisplayImeController.addPositionProcessor(mDividerView);
     }
 
     /**
@@ -128,6 +136,11 @@
      * hierarchy.
      */
     void release() {
+        if (mDividerView != null) {
+            mDisplayImeController.removePositionProcessor(mDividerView);
+            mDividerView = null;
+        }
+
         if (mViewHost != null){
             mViewHost.release();
             mViewHost = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java
index 477ec33..40244fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java
@@ -18,6 +18,7 @@
 
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.util.RotationUtils.rotateBounds;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_LEFT;
@@ -244,7 +245,7 @@
             DividerSnapAlgorithm snap = initSnapAlgorithmForRotation(context, tmpDL, dividerSize);
 
             tmpRect.set(bounds);
-            DisplayLayout.rotateBounds(tmpRect, displayRect, rotation - dl.rotation());
+            rotateBounds(tmpRect, displayRect, dl.rotation(), rotation);
             rotatedDisplayRect.set(0, 0, tmpDL.width(), tmpDL.height());
             final int dockSide = getPrimarySplitSide(tmpRect, rotatedDisplayRect,
                     tmpDL.getOrientation());
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 c8f8987..82468ad 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
@@ -105,7 +105,7 @@
             synchronized (mDockedRect) {
                 mTouchableRegion.set(region);
             }
-            WindowManagerGlobal.getWindowManagerService().setDockedStackDividerTouchRegion(
+            WindowManagerGlobal.getWindowManagerService().setDockedTaskDividerTouchRegion(
                     mTouchableRegion);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to set touchable region: " + e);
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 afc8a09..4c5cc22 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
@@ -202,6 +202,7 @@
                     animateWindows(token, leash, fromBounds, toBounds, direction,
                             mEnterExitAnimationDurationMs);
                     wct.setBounds(token, toBounds);
+                    wct.setAppBounds(token, toBounds);
                 });
         applyTransaction(wct);
     }
@@ -231,6 +232,7 @@
                     // DisplayRotationController will applyTransaction() after finish rotating
                     if (wct != null) {
                         wct.setBounds(token, null/* reset */);
+                        wct.setAppBounds(token, null/* reset */);
                     }
                 });
         tx.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
index 8f8ec47..b3b1ba7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
@@ -20,7 +20,7 @@
 import android.content.ComponentName;
 import android.content.pm.ParceledListSlice;
 import android.os.RemoteException;
-import android.view.IPinnedStackListener;
+import android.view.IPinnedTaskListener;
 import android.view.WindowManagerGlobal;
 
 import androidx.annotation.BinderThread;
@@ -32,66 +32,66 @@
 /**
  * PinnedStackListener that simply forwards all calls to each listener added via
  * {@link #addListener}. This is necessary since calling
- * {@link com.android.server.wm.WindowManagerService#registerPinnedStackListener} replaces any
+ * {@link com.android.server.wm.WindowManagerService#registerPinnedTaskListener} replaces any
  * previously set listener.
  */
 public class PinnedStackListenerForwarder {
 
-    private final IPinnedStackListener mListenerImpl = new PinnedStackListenerImpl();
+    private final IPinnedTaskListener mListenerImpl = new PinnedTaskListenerImpl();
     private final ShellExecutor mMainExecutor;
-    private final ArrayList<PinnedStackListener> mListeners = new ArrayList<>();
+    private final ArrayList<PinnedTaskListener> mListeners = new ArrayList<>();
 
     public PinnedStackListenerForwarder(ShellExecutor mainExecutor) {
         mMainExecutor = mainExecutor;
     }
 
     /** Adds a listener to receive updates from the WindowManagerService. */
-    public void addListener(PinnedStackListener listener) {
+    public void addListener(PinnedTaskListener listener) {
         mListeners.add(listener);
     }
 
     /** Removes a listener so it will no longer receive updates from the WindowManagerService. */
-    public void removeListener(PinnedStackListener listener) {
+    public void removeListener(PinnedTaskListener listener) {
         mListeners.remove(listener);
     }
 
     public void register(int displayId) throws RemoteException {
-        WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
+        WindowManagerGlobal.getWindowManagerService().registerPinnedTaskListener(
                 displayId, mListenerImpl);
     }
 
     private void onMovementBoundsChanged(boolean fromImeAdjustment) {
-        for (PinnedStackListener listener : mListeners) {
+        for (PinnedTaskListener listener : mListeners) {
             listener.onMovementBoundsChanged(fromImeAdjustment);
         }
     }
 
     private void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
-        for (PinnedStackListener listener : mListeners) {
+        for (PinnedTaskListener listener : mListeners) {
             listener.onImeVisibilityChanged(imeVisible, imeHeight);
         }
     }
 
     private void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
-        for (PinnedStackListener listener : mListeners) {
+        for (PinnedTaskListener listener : mListeners) {
             listener.onActionsChanged(actions);
         }
     }
 
     private void onActivityHidden(ComponentName componentName) {
-        for (PinnedStackListener listener : mListeners) {
+        for (PinnedTaskListener listener : mListeners) {
             listener.onActivityHidden(componentName);
         }
     }
 
     private void onAspectRatioChanged(float aspectRatio) {
-        for (PinnedStackListener listener : mListeners) {
+        for (PinnedTaskListener listener : mListeners) {
             listener.onAspectRatioChanged(aspectRatio);
         }
     }
 
     @BinderThread
-    private class PinnedStackListenerImpl extends IPinnedStackListener.Stub {
+    private class PinnedTaskListenerImpl extends IPinnedTaskListener.Stub {
         @Override
         public void onMovementBoundsChanged(boolean fromImeAdjustment) {
             mMainExecutor.execute(() -> {
@@ -129,10 +129,10 @@
     }
 
     /**
-     * A counterpart of {@link IPinnedStackListener} with empty implementations.
+     * A counterpart of {@link IPinnedTaskListener} with empty implementations.
      * Subclasses can ignore those methods they do not intend to take action upon.
      */
-    public static class PinnedStackListener {
+    public static class PinnedTaskListener {
         public void onMovementBoundsChanged(boolean fromImeAdjustment) {}
 
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 5ffa988..a52db24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.pip;
 
+import static android.util.RotationUtils.rotateBounds;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
@@ -33,7 +34,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.wm.shell.animation.Interpolators;
-import com.android.wm.shell.common.DisplayLayout;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -90,15 +90,15 @@
 
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
 
-    private PipTransitionAnimator mCurrentAnimator;
-
-    private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
+    private final ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
             ThreadLocal.withInitial(() -> {
                 AnimationHandler handler = new AnimationHandler();
                 handler.setProvider(new SfVsyncFrameCallbackProvider());
                 return handler;
             });
 
+    private PipTransitionAnimator mCurrentAnimator;
+
     public PipAnimationController(PipSurfaceTransactionHelper helper) {
         mSurfaceTransactionHelper = helper;
     }
@@ -268,6 +268,7 @@
             if (mPipAnimationCallback != null) {
                 mPipAnimationCallback.onPipAnimationEnd(mTaskInfo, tx, this);
             }
+            mTransitionDirection = TRANSITION_DIRECTION_NONE;
         }
 
         @Override
@@ -275,6 +276,7 @@
             if (mPipAnimationCallback != null) {
                 mPipAnimationCallback.onPipAnimationCancel(mTaskInfo, this);
             }
+            mTransitionDirection = TRANSITION_DIRECTION_NONE;
         }
 
         @Override public void onAnimationRepeat(Animator animation) {}
@@ -448,7 +450,7 @@
                 // Rotate the end bounds according to the rotation delta because the display will
                 // be rotated to the same orientation.
                 rotatedEndRect = new Rect(endValue);
-                DisplayLayout.rotateBounds(rotatedEndRect, endValue, rotationDelta);
+                rotateBounds(rotatedEndRect, endValue, rotationDelta);
             } else {
                 rotatedEndRect = null;
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 4a2a032..9a584c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -65,6 +65,7 @@
 import com.android.wm.shell.pip.PipUtils;
 
 import java.io.PrintWriter;
+import java.util.Objects;
 import java.util.function.Consumer;
 
 /**
@@ -93,17 +94,20 @@
 
     protected PhonePipMenuController mMenuController;
     protected PipTaskOrganizer mPipTaskOrganizer;
-    protected PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener =
-            new PipControllerPinnedStackListener();
+    protected PinnedStackListenerForwarder.PinnedTaskListener mPinnedTaskListener =
+            new PipControllerPinnedTaskListener();
 
     /**
      * Handler for display rotation changes.
      */
     private final DisplayChangeController.OnDisplayChangingListener mRotationController = (
             int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> {
-        if (!mPipTaskOrganizer.isInPip() || mPipTaskOrganizer.isDeferringEnterPipAnimation()) {
-            // Skip if we aren't in PIP or haven't actually entered PIP yet. We still need to update
-            // the display layout in the bounds handler in this case.
+        if (!mPipTaskOrganizer.isInPip()
+                || mPipBoundsState.getDisplayLayout().rotation() == toRotation
+                || mPipTaskOrganizer.isDeferringEnterPipAnimation()) {
+            // Skip if the same rotation has been set or we aren't in PIP or haven't actually
+            // entered PIP yet. We still need to update the display layout in the bounds handler
+            // in this case.
             onDisplayRotationChangedNotInPip(mContext, toRotation);
             // do not forget to update the movement bounds as well.
             updateMovementBounds(mPipBoundsState.getNormalBounds(), true /* fromRotation */,
@@ -178,8 +182,8 @@
     /**
      * Handler for messages from the PIP controller.
      */
-    private class PipControllerPinnedStackListener extends
-            PinnedStackListenerForwarder.PinnedStackListener {
+    private class PipControllerPinnedTaskListener extends
+            PinnedStackListenerForwarder.PinnedTaskListener {
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
             mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
@@ -310,7 +314,7 @@
         mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
 
         try {
-            mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener);
+            mWindowManagerShellWrapper.addPinnedStackListener(mPinnedTaskListener);
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to register pinned stack listener", e);
         }
@@ -378,6 +382,9 @@
     }
 
     private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) {
+        if (Objects.equals(layout, mPipBoundsState.getDisplayLayout())) {
+            return;
+        }
         Runnable updateDisplayLayout = () -> {
             mPipBoundsState.setDisplayLayout(layout);
             updateMovementBounds(null /* toBounds */,
@@ -476,8 +483,11 @@
             int launcherRotation, int shelfHeight) {
         setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight);
         onDisplayRotationChangedNotInPip(mContext, launcherRotation);
-        return mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
+        final Rect entryBounds = mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
                 pictureInPictureParams);
+        // sync mPipBoundsState with the newly calculated bounds.
+        mPipBoundsState.setNormalBounds(entryBounds);
+        return entryBounds;
     }
 
     private void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 56f183f..7098019 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -377,7 +377,7 @@
 
     private void registerWmShellPinnedStackListener(WindowManagerShellWrapper wmShell) {
         try {
-            wmShell.addPinnedStackListener(new PinnedStackListenerForwarder.PinnedStackListener() {
+            wmShell.addPinnedStackListener(new PinnedStackListenerForwarder.PinnedTaskListener() {
                 @Override
                 public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
                     if (DEBUG) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index b0167af..11548ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -42,6 +42,7 @@
 
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.draganddrop.DragAndDropPolicy;
@@ -62,18 +63,20 @@
     private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
     private final ShellExecutor mMainExecutor;
     private final SplitScreenImpl mImpl = new SplitScreenImpl();
+    private final DisplayImeController mDisplayImeController;
 
     private StageCoordinator mStageCoordinator;
 
     public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
             SyncTransactionQueue syncQueue, Context context,
             RootTaskDisplayAreaOrganizer rootTDAOrganizer,
-            ShellExecutor mainExecutor) {
+            ShellExecutor mainExecutor, DisplayImeController displayImeController) {
         mTaskOrganizer = shellTaskOrganizer;
         mSyncQueue = syncQueue;
         mContext = context;
         mRootTDAOrganizer = rootTDAOrganizer;
         mMainExecutor = mainExecutor;
+        mDisplayImeController = displayImeController;
     }
 
     public SplitScreen asSplitScreen() {
@@ -84,7 +87,7 @@
         if (mStageCoordinator == null) {
             // TODO: Multi-display
             mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
-                    mRootTDAOrganizer, mTaskOrganizer);
+                    mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index e44c820..22c9751 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -39,6 +39,7 @@
 
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.split.SplitLayout;
 
@@ -79,10 +80,12 @@
     private DisplayAreaInfo mDisplayAreaInfo;
     private final Context mContext;
     private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
+    private final DisplayImeController mDisplayImeController;
     private boolean mExitSplitScreenOnHide = true;
 
     StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
-            RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) {
+            RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
+            DisplayImeController displayImeController) {
         mContext = context;
         mDisplayId = displayId;
         mSyncQueue = syncQueue;
@@ -90,13 +93,14 @@
         mTaskOrganizer = taskOrganizer;
         mMainStage = new MainStage(mTaskOrganizer, mDisplayId, mMainStageListener, mSyncQueue);
         mSideStage = new SideStage(mTaskOrganizer, mDisplayId, mSideStageListener, mSyncQueue);
+        mDisplayImeController = displayImeController;
         mRootTDAOrganizer.registerListener(displayId, this);
     }
 
     @VisibleForTesting
     StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
             RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
-            MainStage mainStage, SideStage sideStage) {
+            MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController) {
         mContext = context;
         mDisplayId = displayId;
         mSyncQueue = syncQueue;
@@ -104,6 +108,7 @@
         mTaskOrganizer = taskOrganizer;
         mMainStage = mainStage;
         mSideStage = sideStage;
+        mDisplayImeController = displayImeController;
         mRootTDAOrganizer.registerListener(displayId, this);
     }
 
@@ -420,7 +425,8 @@
         if (mSplitLayout == null) {
             mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
                     mDisplayAreaInfo.configuration, this,
-                    b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b));
+                    b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b),
+                    mDisplayImeController);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 45d5515..7649770 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -43,6 +43,7 @@
 
 /**
  * Util class to create the view for a splash screen content.
+ *
  * @hide
  */
 public class SplashscreenContentDrawer {
@@ -349,7 +350,7 @@
 
         // Calculate the difference between two colors based on the HSV dimensions.
         final float normalizeH = minAngle / 180f;
-        final double square =  Math.pow(normalizeH, 2)
+        final double square = Math.pow(normalizeH, 2)
                 + Math.pow(aHsv[1] - bHsv[1], 2)
                 + Math.pow(aHsv[2] - bHsv[2], 2);
         final double mean = square / 3;
@@ -433,8 +434,11 @@
          */
         private interface ColorTester {
             float nonTransparentRatio();
+
             boolean isComplexColor();
+
             int getDominantColor();
+
             boolean isGrayscale();
         }
 
@@ -511,14 +515,17 @@
                 // restore to original bounds
                 drawable.setBounds(initialBounds);
 
-                final Palette.Builder builder = new Palette.Builder(bitmap)
-                        .maximumColorCount(5).clearFilters();
+                final Palette.Builder builder;
                 // The Palette API will ignore Alpha, so it cannot handle transparent pixels, but
                 // sometimes we will need this information to know if this Drawable object is
                 // transparent.
                 mFilterTransparent = filterTransparent;
                 if (mFilterTransparent) {
-                    builder.setQuantizer(TRANSPARENT_FILTER_QUANTIZER);
+                    builder = new Palette.Builder(bitmap, TRANSPARENT_FILTER_QUANTIZER)
+                            .maximumColorCount(5);
+                } else {
+                    builder = new Palette.Builder(bitmap, null)
+                            .maximumColorCount(5);
                 }
                 mPalette = builder.generate();
                 bitmap.recycle();
@@ -538,7 +545,7 @@
             public int getDominantColor() {
                 final Palette.Swatch mainSwatch = mPalette.getDominantSwatch();
                 if (mainSwatch != null) {
-                    return mainSwatch.getRgb();
+                    return mainSwatch.getInt();
                 }
                 return Color.BLACK;
             }
@@ -549,7 +556,7 @@
                 if (swatches != null) {
                     for (int i = swatches.size() - 1; i >= 0; i--) {
                         Palette.Swatch swatch = swatches.get(i);
-                        if (!isGrayscaleColor(swatch.getRgb())) {
+                        if (!isGrayscaleColor(swatch.getInt())) {
                             return false;
                         }
                     }
@@ -561,9 +568,9 @@
                 private static final int NON_TRANSPARENT = 0xFF000000;
                 private final Quantizer mInnerQuantizer = new VariationalKMeansQuantizer();
                 private float mNonTransparentRatio;
+
                 @Override
-                public void quantize(final int[] pixels, final int maxColors,
-                        final Palette.Filter[] filters) {
+                public void quantize(final int[] pixels, final int maxColors) {
                     mNonTransparentRatio = 0;
                     int realSize = 0;
                     for (int i = pixels.length - 1; i > 0; i--) {
@@ -575,7 +582,7 @@
                         if (DEBUG) {
                             Slog.d(TAG, "quantize: this is pure transparent image");
                         }
-                        mInnerQuantizer.quantize(pixels, maxColors, filters);
+                        mInnerQuantizer.quantize(pixels, maxColors);
                         return;
                     }
                     mNonTransparentRatio = (float) realSize / pixels.length;
@@ -587,7 +594,7 @@
                             rowIndex++;
                         }
                     }
-                    mInnerQuantizer.quantize(samplePixels, maxColors, filters);
+                    mInnerQuantizer.quantize(samplePixels, maxColors);
                 }
 
                 @Override
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index a57ac35..9dd25fe 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "WMShellFlickerTests",
     srcs: ["src/**/*.java", "src/**/*.kt"],
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 10aea51..35bab7a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -18,8 +18,6 @@
 
 import android.graphics.Region
 import android.view.Surface
-import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
-import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.traces.layers.getVisibleBounds
@@ -32,15 +30,15 @@
 
 fun FlickerTestParameter.appPairsDividerIsInvisible() {
     assertLayersEnd {
-        this.notExists(APP_PAIR_SPLIT_DIVIDER)
+        this.notContains(APP_PAIR_SPLIT_DIVIDER)
     }
 }
 
 fun FlickerTestParameter.appPairsDividerBecomesVisible() {
     assertLayers {
-        this.hidesLayer(DOCKED_STACK_DIVIDER)
+        this.isInvisible(DOCKED_STACK_DIVIDER)
             .then()
-            .showsLayer(DOCKED_STACK_DIVIDER)
+            .isVisible(DOCKED_STACK_DIVIDER)
     }
 }
 
@@ -52,30 +50,30 @@
 
 fun FlickerTestParameter.dockedStackDividerBecomesVisible() {
     assertLayers {
-        this.hidesLayer(DOCKED_STACK_DIVIDER)
+        this.isInvisible(DOCKED_STACK_DIVIDER)
             .then()
-            .showsLayer(DOCKED_STACK_DIVIDER)
+            .isVisible(DOCKED_STACK_DIVIDER)
     }
 }
 
 fun FlickerTestParameter.dockedStackDividerBecomesInvisible() {
     assertLayers {
-        this.showsLayer(DOCKED_STACK_DIVIDER)
+        this.isVisible(DOCKED_STACK_DIVIDER)
             .then()
-            .hidesLayer(DOCKED_STACK_DIVIDER)
+            .isInvisible(DOCKED_STACK_DIVIDER)
     }
 }
 
 fun FlickerTestParameter.dockedStackDividerIsInvisible() {
     assertLayersEnd {
-        this.notExists(DOCKED_STACK_DIVIDER)
+        this.notContains(DOCKED_STACK_DIVIDER)
     }
 }
 
 fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) {
     assertLayersEnd {
         val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-        this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
+        this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName)
     }
 }
 
@@ -85,7 +83,7 @@
 ) {
     assertLayersEnd {
         val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
-        this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
+        this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName)
     }
 }
 
@@ -95,7 +93,7 @@
 ) {
     assertLayersEnd {
         val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-        this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
+        this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName)
     }
 }
 
@@ -105,7 +103,7 @@
 ) {
     assertLayersEnd {
         val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
-        this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
+        this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName)
     }
 }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index d2cfb0f..03b93c7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -18,3 +18,5 @@
 
 const val IME_WINDOW_NAME = "InputMethod"
 const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
+const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider"
+const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 5d51b2f..90e7137 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.Bundle
 import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
@@ -48,7 +47,7 @@
     testSpec: FlickerTestParameter
 ) : AppPairsTransition(testSpec) {
 
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
             super.transition(this, it)
             transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 77890ba..dc51b4f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -16,17 +16,16 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.Bundle
 import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.traces.layers.getVisibleBounds
+import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER
 import com.android.wm.shell.flicker.appPairsDividerIsVisible
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import org.junit.FixMethodOrder
@@ -46,7 +45,7 @@
 class AppPairsTestPairPrimaryAndSecondaryApps(
     testSpec: FlickerTestParameter
 ) : AppPairsTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
             super.transition(this, it)
             transitions {
@@ -75,10 +74,10 @@
     fun appsEndingBounds() {
         testSpec.assertLayersEnd {
             val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-            this.hasVisibleRegion(primaryApp.defaultWindowName,
-                appPairsHelper.getPrimaryBounds(dividerRegion))
-                .hasVisibleRegion(secondaryApp.defaultWindowName,
-                    appPairsHelper.getSecondaryBounds(dividerRegion))
+            this.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion),
+                primaryApp.defaultWindowName)
+                .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion),
+                    secondaryApp.defaultWindowName)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 3d3ca0c..5bb9b2f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -16,17 +16,16 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.Bundle
 import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.traces.layers.getVisibleBounds
+import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER
 import com.android.wm.shell.flicker.appPairsDividerIsInvisible
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import org.junit.FixMethodOrder
@@ -46,7 +45,7 @@
 class AppPairsTestUnpairPrimaryAndSecondaryApps(
     testSpec: FlickerTestParameter
 ) : AppPairsTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
             super.transition(this, it)
             setup {
@@ -80,10 +79,10 @@
     fun appsStartingBounds() {
         testSpec.assertLayersStart {
             val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-            hasVisibleRegion(primaryApp.defaultWindowName,
-                appPairsHelper.getPrimaryBounds(dividerRegion))
-            hasVisibleRegion(secondaryApp.defaultWindowName,
-                appPairsHelper.getSecondaryBounds(dividerRegion))
+            coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion),
+                primaryApp.defaultWindowName)
+            coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion),
+                secondaryApp.defaultWindowName)
         }
     }
 
@@ -91,8 +90,8 @@
     @Test
     fun appsEndingBounds() {
         testSpec.assertLayersEnd {
-            notExists(primaryApp.defaultWindowName)
-            notExists(secondaryApp.defaultWindowName)
+            notContains(primaryApp.defaultWindowName)
+            notContains(secondaryApp.defaultWindowName)
         }
     }
 
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 9e6752d..91e080f 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
@@ -17,7 +17,6 @@
 package com.android.wm.shell.flicker.apppairs
 
 import android.app.Instrumentation
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.system.helpers.ActivityHelper
 import android.util.Log
@@ -66,7 +65,7 @@
         }
     }
 
-    internal open val transition: FlickerBuilder.(Bundle) -> Unit
+    internal open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             setup {
                 test {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 35a0020..5f003ba 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.Bundle
 import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
@@ -52,7 +51,7 @@
 class RotateTwoLaunchedAppsInAppPairsMode(
     testSpec: FlickerTestParameter
 ) : RotateTwoLaunchedAppsTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
             super.transition(this, it)
             transitions {
@@ -72,16 +71,16 @@
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
         testSpec.config.endRotation)
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
         testSpec.config.endRotation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index 326a775..d479208 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.Bundle
 import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
@@ -55,7 +54,7 @@
 class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
     testSpec: FlickerTestParameter
 ) : RotateTwoLaunchedAppsTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
             super.transition(this, it)
             transitions {
@@ -77,17 +76,8 @@
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(isRotated)
-        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(isRotated)
-        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
-    }
+    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
+        Surface.ROTATION_0, testSpec.config.endRotation)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
index 271b25f..83853e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.Bundle
 import android.view.Surface
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -30,7 +29,7 @@
     override val nonResizeableApp: SplitScreenHelper?
         get() = null
 
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
             setup {
                 test {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 9b70fac..17c51fb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -50,11 +49,10 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-// @FlakyTest(bugId = 179116910)
 class EnterSplitScreenDockActivity(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             super.transition(this, configuration)
             transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index bd57a59..a94fd46 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -54,7 +53,7 @@
 class EnterSplitScreenLaunchToSide(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             super.transition(this, configuration)
             transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
index 67578b2..238059b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -50,7 +49,7 @@
 class EnterSplitScreenNonResizableNotDock(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             super.transition(this, configuration)
             teardown {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index 5d42a4a..acd570a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -16,12 +16,10 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
@@ -34,6 +32,7 @@
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
 import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -52,7 +51,7 @@
 class ExitLegacySplitScreenFromBottom(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             super.transition(this, configuration)
             setup {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index ff8f9c6..cef1886 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -53,7 +52,7 @@
 class ExitPrimarySplitScreenShowSecondaryFullscreen(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             super.transition(this, configuration)
             teardown {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
index 893b101..1e89a25 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.view.Surface
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -27,7 +26,7 @@
 abstract class LegacySplitScreenRotateTransition(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
             setup {
                 eachRun {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 09a7e31..7f69a66 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.support.test.launcherhelper.LauncherStrategyFactory
 import android.view.Surface
@@ -66,7 +65,7 @@
         .launcherStrategy.supportedLauncherPackage
     private val testApp = SimpleAppHelper(instrumentation)
 
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             setup {
                 test {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 6ab1f0b..91ea871 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -17,7 +17,6 @@
 package com.android.wm.shell.flicker.legacysplitscreen
 
 import android.app.Instrumentation
-import android.os.Bundle
 import android.support.test.launcherhelper.LauncherStrategyFactory
 import android.view.Surface
 import androidx.test.platform.app.InstrumentationRegistry
@@ -41,7 +40,7 @@
     protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
         .launcherStrategy.supportedLauncherPackage
 
-    protected open val transition: FlickerBuilder.(Bundle) -> Unit
+    protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             setup {
                 eachRun {
@@ -70,7 +69,7 @@
         }
     }
 
-    internal open val cleanSetup: FlickerBuilder.(Bundle) -> Unit
+    internal open val cleanSetup: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             setup {
                 eachRun {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
index 1b4b54a..caafa27 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
@@ -16,12 +16,10 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
@@ -34,6 +32,7 @@
 import com.android.server.wm.flicker.layerBecomesVisible
 import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
 import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -53,7 +52,7 @@
 class NonResizableDismissInLegacySplitScreen(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             cleanSetup(this, configuration)
             setup {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
index 2365e3b..543484a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
@@ -16,12 +16,10 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
@@ -33,6 +31,7 @@
 import com.android.server.wm.flicker.layerBecomesVisible
 import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
 import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -53,7 +52,7 @@
 class NonResizableLaunchInLegacySplitScreen(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             cleanSetup(this, configuration)
             setup {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index b369a3d..d228337 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -53,7 +52,7 @@
 class OpenAppToLegacySplitScreen(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             super.transition(this, configuration)
             transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index ffffa19..f5174bc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -17,13 +17,11 @@
 package com.android.wm.shell.flicker.legacysplitscreen
 
 import android.graphics.Region
-import android.os.Bundle
 import android.util.Rational
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.uiautomator.By
-import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
@@ -46,6 +44,7 @@
 import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.traces.layers.getVisibleBounds
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
 import com.android.wm.shell.flicker.helpers.SimpleAppHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -70,7 +69,7 @@
     private val testAppTop = SimpleAppHelper(instrumentation)
     private val testAppBottom = ImeAppHelper(instrumentation)
 
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             setup {
                 eachRun {
@@ -151,21 +150,21 @@
     @Test
     fun topAppLayerIsAlwaysVisible() {
         testSpec.assertLayers {
-            this.showsLayer(sSimpleActivity)
+            this.isVisible(sSimpleActivity)
         }
     }
 
     @Test
     fun bottomAppLayerIsAlwaysVisible() {
         testSpec.assertLayers {
-            this.showsLayer(sImeActivity)
+            this.isVisible(sImeActivity)
         }
     }
 
     @Test
     fun dividerLayerIsAlwaysVisible() {
         testSpec.assertLayers {
-            this.showsLayer(DOCKED_STACK_DIVIDER)
+            this.isVisible(DOCKED_STACK_DIVIDER)
         }
     }
 
@@ -183,8 +182,8 @@
                 dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
                 displayBounds.right,
                 displayBounds.bottom - WindowUtils.navigationBarHeight)
-            this.hasVisibleRegion("SimpleActivity", topAppBounds)
-                .hasVisibleRegion("ImeActivity", bottomAppBounds)
+            this.coversExactly(topAppBounds, "SimpleActivity")
+                .coversExactly(bottomAppBounds, "ImeActivity")
         }
     }
 
@@ -203,8 +202,8 @@
                 displayBounds.right,
                 displayBounds.bottom - WindowUtils.navigationBarHeight)
 
-            this.hasVisibleRegion(sSimpleActivity, topAppBounds)
-                .hasVisibleRegion(sImeActivity, bottomAppBounds)
+            this.coversExactly(topAppBounds, sSimpleActivity)
+                .coversExactly(bottomAppBounds, sImeActivity)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index c538008..c914ada 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -54,7 +53,7 @@
 class RotateOneLaunchedAppAndEnterSplitScreen(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenRotateTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             super.transition(this, configuration)
             transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index c116256..ffb20a4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -54,7 +53,7 @@
 class RotateOneLaunchedAppInSplitScreenMode(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenRotateTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             super.transition(this, configuration)
             transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 273925c..8cf1990 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -56,7 +55,7 @@
 class RotateTwoLaunchedAppAndEnterSplitScreen(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenRotateTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             super.transition(this, configuration)
             transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index c7188dc..9c798d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -56,7 +55,7 @@
 class RotateTwoLaunchedAppInSplitScreenMode(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenRotateTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             super.transition(this, configuration)
             setup {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index ca48eaa..75c33c6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -51,7 +50,7 @@
     private val testApp = FixedAppHelper(instrumentation)
     private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
 
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = buildTransition(eachRun = true) {
             setup {
                 eachRun {
@@ -68,7 +67,7 @@
     @Test
     fun pipAppRemainInsideVisibleBounds() {
         testSpec.assertWm {
-            coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
+            coversAtMost(displayBounds, pipApp.defaultWindowName)
         }
     }
 
@@ -95,10 +94,10 @@
     @Test
     fun showBothAppLayersThenHidePip() {
         testSpec.assertLayers {
-            showsLayer(testApp.defaultWindowName)
-                .showsLayer(pipApp.defaultWindowName)
+            isVisible(testApp.defaultWindowName)
+                .isVisible(pipApp.defaultWindowName)
                 .then()
-                .hidesLayer(testApp.defaultWindowName)
+                .isInvisible(testApp.defaultWindowName)
         }
     }
 
@@ -106,8 +105,8 @@
     @Test
     fun testAppCoversFullScreenWithPipOnDisplay() {
         testSpec.assertLayersStart {
-            hasVisibleRegion(testApp.defaultWindowName, displayBounds)
-            coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
+            coversExactly(displayBounds, testApp.defaultWindowName)
+            coversAtMost(displayBounds, pipApp.defaultWindowName)
         }
     }
 
@@ -115,7 +114,7 @@
     @Test
     fun pipAppCoversFullScreen() {
         testSpec.assertLayersEnd {
-            hasVisibleRegion(pipApp.defaultWindowName, displayBounds)
+            coversExactly(displayBounds, pipApp.defaultWindowName)
         }
     }
 
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 e1fbc16..83dca53 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
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -48,7 +47,7 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
             transitions {
                 pipApp.clickEnterPipButton()
@@ -85,7 +84,7 @@
     @Test
     fun pipLayerBecomesVisible() {
         testSpec.assertLayers {
-            this.showsLayer(pipApp.launcherName)
+            this.isVisible(pipApp.launcherName)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 215b97b..9011f1a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -16,10 +16,8 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -56,7 +54,7 @@
     private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
     private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
 
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             setupAndTeardown(this, configuration)
 
@@ -121,24 +119,24 @@
     @Test
     fun pipAppLayerHidesTestApp() {
         testSpec.assertLayersStart {
-            hasVisibleRegion(pipApp.defaultWindowName, startingBounds)
+            coversExactly(startingBounds, pipApp.defaultWindowName)
             isInvisible(testApp.defaultWindowName)
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun testAppLayerCoversFullScreen() {
         testSpec.assertLayersEnd {
-            hasVisibleRegion(testApp.defaultWindowName, endingBounds)
+            coversExactly(endingBounds, testApp.defaultWindowName)
         }
     }
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
index 707d28d..96eb66c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
@@ -48,7 +48,7 @@
     activity: ComponentName
 ): WindowManagerStateSubject = apply {
     val windowName = activity.toWindowName()
-    hasWindow(windowName)
+    contains(windowName)
     val pinnedWindows = wmState.pinnedWindows
         .map { it.title }
     Truth.assertWithMessage("Window not in PIP mode")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
similarity index 63%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
index 968a11d..3e33176 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
@@ -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.
@@ -16,41 +16,27 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.os.Bundle
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import org.junit.FixMethodOrder
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-/**
- * Test Pip launch.
- * To run this test: `atest WMShellFlickerTests:PipToHomeTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = buildTransition(eachRun = true) { configuration ->
             setup {
                 eachRun {
@@ -62,30 +48,27 @@
                     this.setRotation(Surface.ROTATION_0)
                 }
             }
-            transitions {
-                pipApp.closePipWindow(wmHelper)
-            }
         }
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
 
     @Presubmit
     @Test
-    fun pipWindowBecomesInvisible() {
+    open fun pipWindowBecomesInvisible() {
         testSpec.assertWm {
             this.showsAppWindow(PIP_WINDOW_TITLE)
                 .then()
@@ -95,32 +78,32 @@
 
     @Presubmit
     @Test
-    fun pipLayerBecomesInvisible() {
+    open fun pipLayerBecomesInvisible() {
         testSpec.assertLayers {
-            this.showsLayer(PIP_WINDOW_TITLE)
+            this.isVisible(PIP_WINDOW_TITLE)
                 .then()
-                .hidesLayer(PIP_WINDOW_TITLE)
+                .isInvisible(PIP_WINDOW_TITLE)
         }
     }
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() =
+    open fun statusBarLayerRotatesScales() =
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun noUncoveredRegions() =
+    open fun noUncoveredRegions() =
         testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() =
-        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+    open fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
 
     @FlakyTest(bugId = 151179149)
     @Test
-    fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+    open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
new file mode 100644
index 0000000..0408421
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip launch.
+ * To run this test: `atest WMShellFlickerTests:PipCloseWithDismissButton`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipCloseWithDismissButtonTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
+            transitions {
+                pipApp.closePipWindow(wmHelper)
+            }
+        }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
new file mode 100644
index 0000000..afaf33a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.flicker.pip
+
+import android.platform.test.annotations.Postsubmit
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip launch.
+ * To run this test: `atest WMShellFlickerTests:PipCloseWithSwipe`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
+            transitions {
+                val pipRegion = wmHelper.getWindowRegion(pipApp.component).bounds
+                val pipCenterX = pipRegion.centerX()
+                val pipCenterY = pipRegion.centerY()
+                val displayCenterX = device.displayWidth / 2
+                device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 5)
+            }
+        }
+
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
+
+    @Postsubmit
+    @Test
+    override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
+
+    @Postsubmit
+    @Test
+    override fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+    @Postsubmit
+    @Test
+    override fun noUncoveredRegions() = super.noUncoveredRegions()
+
+    @Postsubmit
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index f3b9ea1..4633960 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -46,7 +45,7 @@
 class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
     private val imeApp = ImeAppHelper(instrumentation)
 
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = buildTransition(eachRun = false) { configuration ->
             setup {
                 test {
@@ -78,7 +77,7 @@
     fun pipInVisibleBounds() {
         testSpec.assertWm {
             val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
-            coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
+            coversAtMost(displayBounds, pipApp.defaultWindowName)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index daf381e..97afc65 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.os.Bundle
 import android.platform.test.annotations.Postsubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -58,7 +57,7 @@
     private val testApp = FixedAppHelper(instrumentation)
     private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
 
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
             withTestName { testSpec.name }
             repeat { testSpec.config.repetitions }
@@ -90,7 +89,7 @@
     @Test
     fun pipWindowInsideDisplayBounds() {
         testSpec.assertWm {
-            coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
+            coversAtMost(displayBounds, pipApp.defaultWindowName)
         }
     }
 
@@ -100,7 +99,7 @@
         testSpec.assertWmEnd {
             isVisible(testApp.defaultWindowName)
             isVisible(imeApp.defaultWindowName)
-            noWindowsOverlap(setOf(testApp.defaultWindowName, imeApp.defaultWindowName))
+            noWindowsOverlap(testApp.defaultWindowName, imeApp.defaultWindowName)
         }
     }
 
@@ -116,7 +115,7 @@
     @Test
     fun pipLayerInsideDisplayBounds() {
         testSpec.assertLayers {
-            coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
+            coversAtMost(displayBounds, pipApp.defaultWindowName)
         }
     }
 
@@ -124,8 +123,8 @@
     @Test
     fun bothAppLayersVisible() {
         testSpec.assertLayersEnd {
-            coversAtMostRegion(displayBounds, testApp.defaultWindowName)
-            coversAtMostRegion(displayBounds, imeApp.defaultWindowName)
+            coversAtMost(displayBounds, testApp.defaultWindowName)
+            coversAtMost(displayBounds, imeApp.defaultWindowName)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
new file mode 100644
index 0000000..4c95da2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.flicker.pip
+
+import android.platform.test.annotations.Postsubmit
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.google.common.truth.Truth
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip launch.
+ * To run this test: `atest WMShellFlickerTests:PipMovesInAllApps`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+    private val taplInstrumentation = LauncherInstrumentation()
+
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = buildTransition(eachRun = false) {
+            teardown {
+                eachRun {
+                    taplInstrumentation.pressHome()
+                }
+            }
+            transitions {
+                taplInstrumentation.pressHome().switchToAllApps()
+                wmHelper.waitForAppTransitionIdle()
+            }
+        }
+
+    @Postsubmit
+    @Test
+    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    fun pipAlwaysVisible() = testSpec.assertWm { this.showsAppWindow(pipApp.windowName) }
+
+    @Postsubmit
+    @Test
+    fun pipWindowMovesUp() = testSpec.assertWmEnd {
+        val initialState = this.trace?.first()?.wmState
+            ?: error("Trace should not be empty")
+        val startPos = initialState.pinnedWindows.first().frame
+        val currPos = this.wmState.pinnedWindows.first().frame
+        val subject = Truth.assertWithMessage("Pip should have moved up")
+        subject.that(currPos.top).isGreaterThan(startPos.top)
+        subject.that(currPos.bottom).isGreaterThan(startPos.bottom)
+        subject.that(currPos.left).isEqualTo(startPos.left)
+        subject.that(currPos.right).isEqualTo(startPos.right)
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<FlickerTestParameter> {
+            return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+                supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
+        }
+    }
+}
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 43c12ac..df835d2 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
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -56,7 +55,7 @@
     private val startingBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
     private val endingBounds = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
 
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = buildTransition(eachRun = false) { configuration ->
             setup {
                 test {
@@ -89,11 +88,11 @@
     fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
         testSpec.config.endRotation, allStates = false)
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
@@ -113,8 +112,8 @@
     @Test
     fun appLayerRotates_StartingBounds() {
         testSpec.assertLayersStart {
-            hasVisibleRegion(fixedApp.defaultWindowName, startingBounds)
-            coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
+            coversExactly(startingBounds, fixedApp.defaultWindowName)
+            coversAtMost(startingBounds, pipApp.defaultWindowName)
         }
     }
 
@@ -122,8 +121,8 @@
     @Test
     fun appLayerRotates_EndingBounds() {
         testSpec.assertLayersEnd {
-            hasVisibleRegion(fixedApp.defaultWindowName, endingBounds)
-            coversAtMostRegion(endingBounds, pipApp.defaultWindowName)
+            coversExactly(endingBounds, fixedApp.defaultWindowName)
+            coversAtMost(endingBounds, pipApp.defaultWindowName)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 02389a9..1bb1d28 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -50,7 +49,7 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = buildTransition(eachRun = true) { configuration ->
             setup {
                 eachRun {
@@ -67,7 +66,7 @@
             }
         }
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
@@ -102,18 +101,18 @@
     @Test
     fun appReplacesPipLayer() {
         testSpec.assertLayers {
-            this.showsLayer(PIP_WINDOW_TITLE)
+            this.isVisible(PIP_WINDOW_TITLE)
                 .then()
-                .showsLayer(pipApp.launcherName)
+                .isVisible(pipApp.launcherName)
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun noUncoveredRegions() =
         testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index a94483e..b0a9afe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -18,7 +18,6 @@
 
 import android.app.Instrumentation
 import android.content.Intent
-import android.os.Bundle
 import android.view.Surface
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -38,7 +37,7 @@
     protected val isRotated = testSpec.config.startRotation.isRotated()
     protected val pipApp = PipAppHelper(instrumentation)
     protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
-    protected abstract val transition: FlickerBuilder.(Bundle) -> Unit
+    protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
 
     // Helper class to process test actions by broadcast.
     protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
@@ -81,7 +80,7 @@
     /**
      * Gets a configuration that handles basic setup and teardown of pip tests
      */
-    protected val setupAndTeardown: FlickerBuilder.(Bundle) -> Unit
+    protected val setupAndTeardown: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
             setup {
                 test {
@@ -112,8 +111,8 @@
     protected open fun buildTransition(
         eachRun: Boolean,
         stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"),
-        extraSpec: FlickerBuilder.(Bundle) -> Unit = {}
-    ): FlickerBuilder.(Bundle) -> Unit {
+        extraSpec: FlickerBuilder.(Map<String, Any?>) -> Unit = {}
+    ): FlickerBuilder.(Map<String, Any?>) -> Unit {
         return { configuration ->
             setupAndTeardown(this, configuration)
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 1f0370d..7916ce5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -54,7 +53,7 @@
     private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
     private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
 
-    override val transition: FlickerBuilder.(Bundle) -> Unit
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
             setupAndTeardown(this, configuration)
 
@@ -88,7 +87,7 @@
     @Test
     fun pipWindowInsideDisplay() {
         testSpec.assertWmStart {
-            coversAtMostRegion(pipApp.defaultWindowName, startingBounds)
+            coversAtMost(startingBounds, pipApp.defaultWindowName)
         }
     }
 
@@ -112,7 +111,7 @@
     @Test
     fun pipLayerInsideDisplay() {
         testSpec.assertLayersStart {
-            coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
+            coversAtMost(startingBounds, pipApp.defaultWindowName)
         }
     }
 
@@ -120,15 +119,15 @@
     @Test
     fun pipAppLayerCoversFullScreen() {
         testSpec.assertLayersEnd {
-            hasVisibleRegion(pipApp.defaultWindowName, endingBounds)
+            coversExactly(endingBounds, pipApp.defaultWindowName)
         }
     }
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
index 26627a4..ea606df 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "WMShellFlickerTestApp",
     srcs: ["**/*.java"],
@@ -23,4 +32,4 @@
     name: "wmshell-flicker-test-components",
     srcs: ["src/**/Components.java"],
     sdk_version: "test_current",
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
index e094158..27c6261 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
@@ -20,17 +20,17 @@
 
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 
-import org.mockito.Mock;
-
 public class TestAppPairsController extends AppPairsController {
     private TestAppPairsPool mPool;
 
     public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
             DisplayController displayController) {
-        super(organizer, syncQueue, displayController, mock(ShellExecutor.class));
+        super(organizer, syncQueue, displayController, mock(ShellExecutor.class),
+                mock(DisplayImeController.class));
         mPool = new TestAppPairsPool(this);
         setPairsPool(mPool);
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
index 2b5b77e..88e754c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
@@ -38,6 +38,12 @@
 
 import org.junit.Test;
 
+/**
+ * Tests for {@link DisplayLayout}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:DisplayLayoutTest
+ */
 @SmallTest
 public class DisplayLayoutTest {
 
@@ -70,18 +76,6 @@
     @Test
     public void testRotate() {
         // Basic rotate utility
-        Rect testParent = new Rect(0, 0, 1000, 600);
-        Rect testInner = new Rect(40, 20, 120, 80);
-        Rect testResult = new Rect(testInner);
-        DisplayLayout.rotateBounds(testResult, testParent, 1);
-        assertEquals(new Rect(20, 880, 80, 960), testResult);
-        testResult.set(testInner);
-        DisplayLayout.rotateBounds(testResult, testParent, 2);
-        assertEquals(new Rect(880, 20, 960, 80), testResult);
-        testResult.set(testInner);
-        DisplayLayout.rotateBounds(testResult, testParent, 3);
-        assertEquals(new Rect(520, 40, 580, 120), testResult);
-
         Resources res = createResources(40, 50, false, 30, 40);
         DisplayInfo info = createDisplayInfo(1000, 1500, 60, ROTATION_0);
         DisplayLayout dl = new DisplayLayout(info, res, true, true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
index 21bc32c..d8aebc2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
@@ -198,7 +198,7 @@
 
     @Test
     public void testOnActivityDismissingDockedStack() {
-        mImpl.onActivityDismissingDockedStack();
+        mImpl.onActivityDismissingDockedTask();
         verify(mCallback).onActivityDismissingDockedStack();
         verify(mOtherCallback).onActivityDismissingDockedStack();
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 5821eed..7b0e6b9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -36,6 +36,7 @@
 
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayImeController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -49,6 +50,7 @@
 public class SplitLayoutTests extends ShellTestCase {
     @Mock SplitLayout.LayoutChangeListener mLayoutChangeListener;
     @Mock SurfaceControl mRootLeash;
+    @Mock DisplayImeController mDisplayImeController;
     private SplitLayout mSplitLayout;
 
     @Before
@@ -59,7 +61,8 @@
                 mContext,
                 getConfiguration(false),
                 mLayoutChangeListener,
-                b -> b.setParent(mRootLeash));
+                b -> b.setParent(mRootLeash),
+                mDisplayImeController);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
index 698315a..86d0d82 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
@@ -29,6 +29,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayImeController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -42,6 +43,7 @@
 public class SplitWindowManagerTests extends ShellTestCase {
     @Mock SurfaceControl mSurfaceControl;
     @Mock SplitLayout mSplitLayout;
+    @Mock DisplayImeController mDisplayImeController;
     private SplitWindowManager mSplitWindowManager;
 
     @Before
@@ -50,7 +52,7 @@
         final Configuration configuration = new Configuration();
         configuration.setToDefaults();
         mSplitWindowManager = new SplitWindowManager("TestSplitDivider", mContext, configuration,
-                b -> b.setParent(mSurfaceControl));
+                b -> b.setParent(mSurfaceControl), mDisplayImeController);
         when(mSplitLayout.getDividerBounds()).thenReturn(
                 new Rect(0, 0, configuration.windowConfiguration.getBounds().width(),
                         configuration.windowConfiguration.getBounds().height()));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 3147dab..63b9413 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.pip;
 
+import static android.util.RotationUtils.rotateBounds;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_90;
 
@@ -37,7 +38,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -141,7 +141,7 @@
         // Apply fraction 1 to compute the end value.
         animator.applySurfaceControlTransaction(mLeash, new DummySurfaceControlTx(), 1);
         final Rect rotatedEndBounds = new Rect(endBounds);
-        DisplayLayout.rotateBounds(rotatedEndBounds, endBounds, ROTATION_90);
+        rotateBounds(rotatedEndBounds, endBounds, ROTATION_90);
 
         assertEquals("Expect 90 degree rotated bounds", rotatedEndBounds, animator.mCurrentValue);
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index cfe8463..f2b4e97 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -124,7 +124,7 @@
         final ComponentName component1 = new ComponentName(mContext, "component1");
         when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1);
 
-        mPipController.mPinnedStackListener.onActivityHidden(component1);
+        mPipController.mPinnedTaskListener.onActivityHidden(component1);
 
         verify(mMockPipBoundsState).setLastPipComponentName(null);
     }
@@ -135,7 +135,7 @@
         final ComponentName component2 = new ComponentName(mContext, "component2");
         when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1);
 
-        mPipController.mPinnedStackListener.onActivityHidden(component2);
+        mPipController.mPinnedTaskListener.onActivityHidden(component2);
 
         verify(mMockPipBoundsState, never()).setLastPipComponentName(null);
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index d2d1812..74753aa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -41,6 +41,7 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.SyncTransactionQueue;
 
 import org.junit.Before;
@@ -58,13 +59,14 @@
     @Mock private RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
     @Mock private MainStage mMainStage;
     @Mock private SideStage mSideStage;
+    @Mock private DisplayImeController mDisplayImeController;
     private StageCoordinator mStageCoordinator;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mStageCoordinator = new TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
-                mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage);
+                mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, mDisplayImeController);
     }
 
     @Test
@@ -94,9 +96,9 @@
 
         TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
                 RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
-                MainStage mainStage, SideStage sideStage) {
+                MainStage mainStage, SideStage sideStage, DisplayImeController imeController) {
             super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage,
-                    sideStage);
+                    sideStage, imeController);
 
             // Prepare default TaskDisplayArea for testing.
             mDisplayAreaInfo = new DisplayAreaInfo(
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
index b36ff09..3035a79 100644
--- a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_libs_androidfw_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_libs_androidfw_license"],
+}
+
 cc_fuzz {
     name: "cursorwindow_fuzzer",
     srcs: [
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 5d3f6f2..2448cc9 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -20,13 +20,12 @@
 namespace android {
 namespace uirenderer {
 
-const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames = {
+const std::array FrameInfoNames{
         "Flags",
         "FrameTimelineVsyncId",
         "IntendedVsync",
         "Vsync",
-        "OldestInputEvent",
-        "NewestInputEvent",
+        "InputEventId",
         "HandleInputStart",
         "AnimationStart",
         "PerformTraversalsStart",
@@ -40,7 +39,8 @@
         "DequeueBufferDuration",
         "QueueBufferDuration",
         "GpuCompleted",
-        "SwapBuffersCompleted"
+        "SwapBuffersCompleted",
+        "DisplayPresentTime",
 };
 
 static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 20,
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 45a367f..912d04c5 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -28,15 +28,14 @@
 namespace android {
 namespace uirenderer {
 
-#define UI_THREAD_FRAME_INFO_SIZE 11
+static constexpr size_t UI_THREAD_FRAME_INFO_SIZE = 10;
 
 enum class FrameInfoIndex {
     Flags = 0,
     FrameTimelineVsyncId,
     IntendedVsync,
     Vsync,
-    OldestInputEvent,
-    NewestInputEvent,
+    InputEventId,
     HandleInputStart,
     AnimationStart,
     PerformTraversalsStart,
@@ -56,13 +55,14 @@
 
     GpuCompleted,
     SwapBuffersCompleted,
+    DisplayPresentTime,
 
     // Must be the last value!
     // Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT
     NumIndexes
 };
 
-extern const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames;
+extern const std::array<const char*, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames;
 
 namespace FrameInfoFlags {
 enum {
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 2cd9b7b..4eefe92 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -112,7 +112,7 @@
     std::lock_guard lock(mDataMutex);
 
     // Fast-path for jank-free frames
-    int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted);
+    int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::SwapBuffersCompleted);
     if (mDequeueTimeForgiveness && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) {
         nsecs_t expectedDequeueDuration = mDequeueTimeForgiveness + frame[FrameInfoIndex::Vsync] -
                                           frame[FrameInfoIndex::IssueDrawCommandsStart];
@@ -219,7 +219,7 @@
 void JankTracker::dumpFrames(int fd) {
     dprintf(fd, "\n\n---PROFILEDATA---\n");
     for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
-        dprintf(fd, "%s", FrameInfoNames[i].c_str());
+        dprintf(fd, "%s", FrameInfoNames[i]);
         dprintf(fd, ",");
     }
     for (size_t i = 0; i < mFrames.size(); i++) {
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index df66981..f24ba5c 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -240,8 +240,8 @@
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
     LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
-            "Mismatched size expectations, given %d expected %d",
-            frameInfoSize, UI_THREAD_FRAME_INFO_SIZE);
+                        "Mismatched size expectations, given %d expected %zu", frameInfoSize,
+                        UI_THREAD_FRAME_INFO_SIZE);
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
     env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
     return proxy->syncAndDrawFrame();
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b760db2..f69ddac 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -484,7 +484,8 @@
         // TODO(b/165985262): measure performance impact
         const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
         if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
-            const auto inputEventId = mCurrentFrameInfo->get(FrameInfoIndex::NewestInputEvent);
+            const auto inputEventId =
+                    static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
             native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), vsyncId,
                                                   inputEventId);
         }
@@ -591,7 +592,6 @@
 }
 
 void CanvasContext::finishFrame(FrameInfo* frameInfo) {
-
     // TODO (b/169858044): Consolidate this into a single call.
     mJankTracker.finishFrame(*frameInfo);
     mJankTracker.finishGpuDraw(*frameInfo);
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index ab23448..1a3dbe7 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -33,7 +33,7 @@
 
 constexpr char kRobotoVariable[] = "/system/fonts/Roboto-Regular.ttf";
 
-constexpr char kRegularFont[] = "/system/fonts/NotoSerif-Regular.ttf";
+constexpr char kRegularFont[] = "/system/fonts/NotoSerif.ttf";
 constexpr char kBoldFont[] = "/system/fonts/NotoSerif-Bold.ttf";
 constexpr char kItalicFont[] = "/system/fonts/NotoSerif-Italic.ttf";
 constexpr char kBoldItalicFont[] = "/system/fonts/NotoSerif-BoldItalic.ttf";
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 5e39660..38b48e9 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -117,7 +117,8 @@
     boolean isLocationEnabledForUser(int userId);
     void setLocationEnabledForUser(boolean enabled, int userId);
 
-    void addTestProvider(String name, in ProviderProperties properties, String packageName, @nullable String attributionTag);
+    void addTestProvider(String name, in ProviderProperties properties,
+        in List<String> locationTags, String packageName, @nullable String attributionTag);
     void removeTestProvider(String provider, String packageName, @nullable String attributionTag);
     void setTestProviderLocation(String provider, in Location location, String packageName, @nullable String attributionTag);
     void setTestProviderEnabled(String provider, boolean enabled, String packageName, @nullable String attributionTag);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 562b2e3..95bae5a 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -51,7 +51,7 @@
 import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
-import android.location.provider.ProviderRequest.Listener;
+import android.location.provider.ProviderRequest.ChangedListener;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -76,6 +76,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -1957,12 +1958,32 @@
      * allowed} for your app.
      */
     public void addTestProvider(@NonNull String provider, @NonNull ProviderProperties properties) {
+        addTestProvider(provider, properties, Collections.emptySet());
+    }
+
+    /**
+     * Creates a test location provider and adds it to the set of active providers. This provider
+     * will replace any provider with the same name that exists prior to this call.
+     *
+     * @param provider the provider name
+     * @param properties the provider properties
+     * @param locationTags the attribution tags for accessing location from the provider
+     *
+     * @throws IllegalArgumentException if provider is null
+     * @throws IllegalArgumentException if properties is null
+     * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
+     * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
+     * allowed} for your app.
+     */
+    public void addTestProvider(@NonNull String provider, @NonNull ProviderProperties properties,
+            @NonNull Set<String> locationTags) {
         Preconditions.checkArgument(provider != null, "invalid null provider");
         Preconditions.checkArgument(properties != null, "invalid null properties");
+        Preconditions.checkArgument(locationTags != null, "invalid null location tags");
 
         try {
-            mService.addTestProvider(provider, properties, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
+            mService.addTestProvider(provider, properties, new ArrayList<>(locationTags),
+                    mContext.getOpPackageName(), mContext.getFeatureId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2779,33 +2800,32 @@
     }
 
     /**
-     * Registers a {@link ProviderRequest.Listener} to all providers.
+     * Adds a {@link ProviderRequest.ChangedListener} for listening to all providers'
+     * {@link ProviderRequest} changed events.
      *
      * @param executor the executor that the callback runs on
      * @param listener the listener to register
-     * @return {@code true} always
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
-    public boolean registerProviderRequestListener(
+    public void addProviderRequestChangedListener(
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull Listener listener) {
+            @NonNull ChangedListener listener) {
         ProviderRequestLazyLoader.sProviderRequestListeners.addListener(listener,
                 new ProviderRequestTransport(executor, listener));
-        return true;
     }
 
     /**
-     * Unregisters a {@link ProviderRequest.Listener}.
+     * Removes a {@link ProviderRequest.ChangedListener} that has been added.
      *
      * @param listener the listener to remove.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
-    public void unregisterProviderRequestListener(
-            @NonNull Listener listener) {
+    public void removeProviderRequestChangedListener(
+            @NonNull ProviderRequest.ChangedListener listener) {
         ProviderRequestLazyLoader.sProviderRequestListeners.removeListener(listener);
     }
 
@@ -3446,13 +3466,13 @@
     }
 
     private static class ProviderRequestTransport extends IProviderRequestListener.Stub
-            implements ListenerTransport<ProviderRequest.Listener> {
+            implements ListenerTransport<ChangedListener> {
 
         private final Executor mExecutor;
 
-        private volatile @Nullable ProviderRequest.Listener mListener;
+        private volatile @Nullable ProviderRequest.ChangedListener mListener;
 
-        ProviderRequestTransport(Executor executor, ProviderRequest.Listener listener) {
+        ProviderRequestTransport(Executor executor, ChangedListener listener) {
             Preconditions.checkArgument(executor != null, "invalid null executor");
             Preconditions.checkArgument(listener != null, "invalid null callback");
             mExecutor = executor;
@@ -3465,7 +3485,7 @@
         }
 
         @Override
-        public @Nullable ProviderRequest.Listener getListener() {
+        public @Nullable ProviderRequest.ChangedListener getListener() {
             return mListener;
         }
 
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
index a6a0e7a..763835c 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -21,6 +21,10 @@
 import android.annotation.Nullable;
 import android.location.util.identity.CallerIdentity;
 
+import com.android.internal.annotations.Immutable;
+
+import java.util.Set;
+
 /**
  * Location manager local system service interface.
  *
@@ -39,6 +43,21 @@
     }
 
     /**
+     * Interface for getting callbacks when a location provider's location tags change.
+     *
+     * @see LocationTagInfo
+     */
+    public interface OnProviderLocationTagsChangeListener {
+
+        /**
+         * Called when the location tags for a provider change.
+         *
+         * @param providerLocationTagInfo The tag info for a provider.
+         */
+        void onLocationTagsChanged(@NonNull LocationTagInfo providerLocationTagInfo);
+    }
+
+    /**
      * Returns true if the given provider is enabled for the given user.
      *
      * @param provider A location provider as listed by {@link LocationManager#getAllProviders()}
@@ -88,4 +107,60 @@
      * provider, and the elapsed nanos since boot the current time was computed at.
      */
     public abstract @Nullable LocationTime getGnssTimeMillis();
+
+    /**
+     * Sets a listener for changes in the location providers' tags. Passing
+     * {@code null} clears the current listener.
+     *
+     * @param listener The listener.
+     */
+    public abstract void setOnProviderLocationTagsChangeListener(
+            @Nullable OnProviderLocationTagsChangeListener listener);
+
+    /**
+     * This class represents the location permission tags used by the location provider
+     * packages in a given UID. These tags are strictly used for accessing state guarded
+     * by the location permission(s) by a location provider which are required for the
+     * provider to fulfill its function as being a location provider.
+     */
+    @Immutable
+    public static class LocationTagInfo {
+        private final int mUid;
+
+        @NonNull
+        private final String mPackageName;
+
+        @Nullable
+        private final Set<String> mLocationTags;
+
+        public LocationTagInfo(int uid, @NonNull String packageName,
+                @Nullable Set<String> locationTags) {
+            mUid = uid;
+            mPackageName = packageName;
+            mLocationTags = locationTags;
+        }
+
+        /**
+         * @return The UID for which tags are related.
+         */
+        public int getUid() {
+            return mUid;
+        }
+
+        /**
+         * @return The package for which tags are related.
+         */
+        @NonNull
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        /**
+         * @return The tags for the package used for location related accesses.
+         */
+        @Nullable
+        public Set<String> getTags() {
+            return mLocationTags;
+        }
+    }
 }
diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java
index b6ec323..b72d365 100644
--- a/location/java/android/location/provider/ProviderRequest.java
+++ b/location/java/android/location/provider/ProviderRequest.java
@@ -56,10 +56,13 @@
     /**
      * Listener to be invoked when a new request is set to the provider.
      */
-    public interface Listener {
+    public interface ChangedListener {
 
         /**
          * Invoked when a new request is set.
+         *
+         * @param provider the location provider associated with the request
+         * @param request the new {@link ProviderRequest}
          */
         void onProviderRequestChanged(@NonNull String provider, @NonNull ProviderRequest request);
     }
diff --git a/location/java/android/location/util/identity/CallerIdentity.java b/location/java/android/location/util/identity/CallerIdentity.java
index 0bb7dbb..85a083e 100644
--- a/location/java/android/location/util/identity/CallerIdentity.java
+++ b/location/java/android/location/util/identity/CallerIdentity.java
@@ -42,7 +42,16 @@
     @VisibleForTesting
     public static CallerIdentity forTest(int uid, int pid, String packageName,
             @Nullable String attributionTag) {
-        return new CallerIdentity(uid, pid, packageName, attributionTag, null);
+        return forTest(uid, pid, packageName, attributionTag, null);
+    }
+
+    /**
+     * Construct a CallerIdentity for test purposes.
+     */
+    @VisibleForTesting
+    public static CallerIdentity forTest(int uid, int pid, String packageName,
+            @Nullable String attributionTag, @Nullable String listenerId) {
+        return new CallerIdentity(uid, pid, packageName, attributionTag, listenerId);
     }
 
     /**
@@ -145,7 +154,10 @@
         return mAttributionTag;
     }
 
-    /** The calling listener id. */
+    /**
+     * The calling listener id. A null listener id will match any other listener id for the purposes
+     * of {@link #equals(Object)}.
+     */
     public String getListenerId() {
         return mListenerId;
     }
@@ -168,6 +180,17 @@
         }
     }
 
+    /**
+     * Returns a CallerIdentity corrosponding to this CallerIdentity but with a null listener id.
+     */
+    public CallerIdentity stripListenerId() {
+        if (mListenerId == null) {
+            return this;
+        } else {
+            return new CallerIdentity(mUid, mPid, mPackageName, mAttributionTag, null);
+        }
+    }
+
     @Override
     public String toString() {
         int length = 10 + mPackageName.length();
@@ -201,15 +224,12 @@
             return false;
         }
         CallerIdentity that = (CallerIdentity) o;
-        return equalsIgnoringListenerId(that) && Objects.equals(mListenerId, that.mListenerId);
-    }
-
-    public boolean equalsIgnoringListenerId(CallerIdentity that) {
-        return that != null
-                && mUid == that.mUid
+        return mUid == that.mUid
                 && mPid == that.mPid
                 && mPackageName.equals(that.mPackageName)
-                && Objects.equals(mAttributionTag, that.mAttributionTag);
+                && Objects.equals(mAttributionTag, that.mAttributionTag)
+                && (mListenerId == null || that.mListenerId == null || mListenerId.equals(
+                that.mListenerId));
     }
 
     @Override
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index f87f90d..b2de49d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -568,7 +568,7 @@
     public static final int FLAG_FROM_KEY = 1 << 12;
 
     /** @hide */
-    @IntDef(flag = false, prefix = "FLAG", value = {
+    @IntDef(flag = true, prefix = "FLAG", value = {
             FLAG_SHOW_UI,
             FLAG_ALLOW_RINGER_MODES,
             FLAG_PLAY_SOUND,
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 6fcb756..58174a0 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -3526,8 +3526,9 @@
                 native_enableDeviceCallback();
                 return true;
             } catch (IllegalStateException e) {
-                // Fail silently as track state could have changed in between start
-                // and enabling routing callback, return false to indicate not enabled
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "testEnableNativeRoutingCallbacks failed", e);
+                }
             }
         }
         return false;
@@ -3577,7 +3578,7 @@
             Handler handler) {
         synchronized (mRoutingChangeListeners) {
             if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
-                testEnableNativeRoutingCallbacksLocked();
+                mEnableSelfRoutingMonitor = testEnableNativeRoutingCallbacksLocked();
                 mRoutingChangeListeners.put(
                         listener, new NativeRoutingEventHandlerDelegate(this, listener,
                                 handler != null ? handler : new Handler(mInitializationLooper)));
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 67f1660..f8a642a 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -980,6 +980,74 @@
     public static final String KEY_BITRATE_MODE = "bitrate-mode";
 
     /**
+     * A key describing the maximum Quantization Parameter allowed for encoding video.
+     * This key applies to all three video frame types (I, P, and B). This value fills
+     * in for any of the frame-specific #KEY_VIDEO_QP_I_MAX, #KEY_VIDEO_QP_P_MAX, or
+     * #KEY_VIDEO_QP_B_MAX keys that are not specified
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_QP_MAX = "video-qp-max";
+
+    /**
+     * A key describing the maximum Quantization Parameter allowed for encoding video.
+     * This key applies to all three video frame types (I, P, and B). This value fills
+     * in for any of the frame-specific #KEY_VIDEO_QP_I_MIN, #KEY_VIDEO_QP_P_MIN, or
+     * #KEY_VIDEO_QP_B_MIN keys that are not specified
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_QP_MIN = "video-qp-min";
+
+    /**
+     * A key describing the maximum Quantization Parameter allowed for encoding video.
+     * This value applies to video I-frames.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_QP_I_MAX = "video-qp-i-max";
+
+    /**
+     * A key describing the minimum Quantization Parameter allowed for encoding video.
+     * This value applies to video I-frames.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_QP_I_MIN = "video-qp-i-min";
+
+    /**
+     * A key describing the maximum Quantization Parameter allowed for encoding video.
+     * This value applies to video P-frames.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_QP_P_MAX = "video-qp-p-max";
+
+    /**
+     * A key describing the minimum Quantization Parameter allowed for encoding video.
+     * This value applies to video P-frames.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_QP_P_MIN = "video-qp-p-min";
+
+    /**
+     * A key describing the maximum Quantization Parameter allowed for encoding video.
+     * This value applies to video B-frames.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_QP_B_MAX = "video-qp-b-max";
+
+    /**
+     * A key describing the minimum Quantization Parameter allowed for encoding video.
+     * This value applies to video B-frames.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min";
+
+    /**
      * A key describing the audio session ID of the AudioTrack associated
      * to a tunneled video codec.
      * The associated value is an integer.
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index c51c9dd..9176dae 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.BIND_IMS_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -91,6 +92,7 @@
 import java.util.Set;
 import java.util.UUID;
 import java.util.Vector;
+import java.util.concurrent.Executor;
 
 
 /**
@@ -1358,6 +1360,7 @@
     private void startImpl() {
         baseStart(0); // unknown device at this point
         stayAwake(true);
+        tryToEnableNativeRoutingCallback();
         _start();
     }
 
@@ -1383,6 +1386,7 @@
         stayAwake(false);
         _stop();
         baseStop();
+        tryToDisableNativeRoutingCallback();
     }
 
     private native void _stop() throws IllegalStateException;
@@ -1524,8 +1528,9 @@
                 native_enableDeviceCallback(true);
                 return true;
             } catch (IllegalStateException e) {
-                // Fail silently as media player state could have changed in between start
-                // and enabling routing callback, return false to indicate not enabled
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "testEnableNativeRoutingCallbacks failed", e);
+                }
             }
         }
         return false;
@@ -1588,7 +1593,7 @@
             Handler handler) {
         synchronized (mRoutingChangeListeners) {
             if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
-                testEnableNativeRoutingCallbacksLocked();
+                mEnableSelfRoutingMonitor = testEnableNativeRoutingCallbacksLocked();
                 mRoutingChangeListeners.put(
                         listener, new NativeRoutingEventHandlerDelegate(this, listener,
                                 handler != null ? handler : mEventHandler));
@@ -2172,7 +2177,7 @@
         mOnVideoSizeChangedListener = null;
         mOnTimedTextListener = null;
         mOnRtpRxNoticeListener = null;
-        mOnRtpRxNoticeHandler = null;
+        mOnRtpRxNoticeExecutor = null;
         synchronized (mTimeProviderLock) {
             if (mTimeProvider != null) {
                 mTimeProvider.close();
@@ -3481,9 +3486,6 @@
 
             case MEDIA_STOPPED:
                 {
-                    tryToDisableNativeRoutingCallback();
-                    // FIXME see b/179218630
-                    //baseStop();
                     TimeProvider timeProvider = mTimeProvider;
                     if (timeProvider != null) {
                         timeProvider.onStopped();
@@ -3492,18 +3494,9 @@
                 break;
 
             case MEDIA_STARTED:
-                {
-                    // FIXME see b/179218630
-                    //baseStart(native_getRoutedDeviceId());
-                    tryToEnableNativeRoutingCallback();
-                }
                 // fall through
             case MEDIA_PAUSED:
                 {
-                    // FIXME see b/179218630
-                    //if (msg.what == MEDIA_PAUSED) {
-                    //    basePause();
-                    //}
                     TimeProvider timeProvider = mTimeProvider;
                     if (timeProvider != null) {
                         timeProvider.onPaused(msg.what == MEDIA_PAUSED);
@@ -3711,7 +3704,6 @@
 
             case MEDIA_RTP_RX_NOTICE:
                 final OnRtpRxNoticeListener rtpRxNoticeListener = mOnRtpRxNoticeListener;
-                final Handler rtpRxNoticeHandler = mOnRtpRxNoticeHandler;
                 if (rtpRxNoticeListener == null) {
                     return;
                 }
@@ -3730,14 +3722,9 @@
                     } finally {
                         parcel.recycle();
                     }
-                    if (rtpRxNoticeHandler == null) {
-                        rtpRxNoticeListener.onRtpRxNotice(mMediaPlayer, noticeType, data);
-                    } else {
-                        rtpRxNoticeHandler.post(
-                                () ->
-                                        rtpRxNoticeListener
-                                                .onRtpRxNotice(mMediaPlayer, noticeType, data));
-                    }
+                    mOnRtpRxNoticeExecutor.execute(() ->
+                            rtpRxNoticeListener
+                                    .onRtpRxNotice(mMediaPlayer, noticeType, data));
                 }
                 return;
 
@@ -4305,28 +4292,26 @@
      *
      * @see OnRtpRxNoticeListener
      *
-     * @param listener the listener called after a notice from RTP Rx
-     * @param handler the {@link Handler} that receives RTP Tx events. If null is passed,
-     *                notifications will be posted on the thread that created this MediaPlayer
-     *                instance. If the creating thread does not have a {@link Looper}, then
-     *                notifications will be posted on the main thread.
+     * @param listener the listener called after a notice from RTP Rx.
+     * @param executor the {@link Executor} on which to post RTP Tx events.
      * @hide
      */
     @SystemApi
     @RequiresPermission(BIND_IMS_SERVICE)
     public void setOnRtpRxNoticeListener(
             @NonNull Context context,
-            @NonNull OnRtpRxNoticeListener listener, @Nullable Handler handler) {
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnRtpRxNoticeListener listener) {
         Objects.requireNonNull(context);
         Preconditions.checkArgument(
                 context.checkSelfPermission(BIND_IMS_SERVICE) == PERMISSION_GRANTED,
                 BIND_IMS_SERVICE + " permission not granted.");
         mOnRtpRxNoticeListener = Objects.requireNonNull(listener);
-        mOnRtpRxNoticeHandler = handler;
+        mOnRtpRxNoticeExecutor = Objects.requireNonNull(executor);
     }
 
     private OnRtpRxNoticeListener mOnRtpRxNoticeListener;
-    private Handler mOnRtpRxNoticeHandler;
+    private Executor mOnRtpRxNoticeExecutor;
 
     /**
      * Register a callback to be invoked when a selected track has timed metadata available.
diff --git a/media/java/android/media/soundtrigger/OWNERS b/media/java/android/media/soundtrigger/OWNERS
index 6a351d3..e5d0370 100644
--- a/media/java/android/media/soundtrigger/OWNERS
+++ b/media/java/android/media/soundtrigger/OWNERS
@@ -1 +1,2 @@
+ytai@google.com
 elaurent@google.com
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index 042d02f..1d2657a 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -144,11 +144,6 @@
         native_remove_storage(storage.getStorageId());
     }
 
-    public static void configure(boolean usePtp) {
-        native_configure(usePtp);
-    }
-
-    public static native final void native_configure(boolean usePtp);
     private native final void native_setup(
             MtpDatabase database,
             FileDescriptor controlFd,
diff --git a/media/jni/android_media_JetPlayer.cpp b/media/jni/android_media_JetPlayer.cpp
index 481f80b..10a5b58 100644
--- a/media/jni/android_media_JetPlayer.cpp
+++ b/media/jni/android_media_JetPlayer.cpp
@@ -43,12 +43,34 @@
     jfieldID  nativePlayerInJavaObj; // stores in Java the native JetPlayer object
 };
 
-static fields_t javaJetPlayerFields;
+static fields_t javaJetPlayerFields {};
 
+#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj"
+#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative"
+
+static void initializeJavaIDs(JNIEnv* env) {
+    static std::once_flag sJniInitialized;
+
+    std::call_once(sJniInitialized, [](JNIEnv* env) {
+        // Get the JetPlayer java class
+        jclass jetPlayerClass = FindClassOrDie(env, kClassPathName);
+        javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass);
+
+        // Get the mNativePlayerInJavaObj variable field
+        javaJetPlayerFields.nativePlayerInJavaObj =
+                GetFieldIDOrDie(env, jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J");
+
+        // Get the callback to post events from this native code to Java
+        javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
+                javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME,
+                "(Ljava/lang/Object;III)V");
+    }, env);
+}
 
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 
+
 /*
  * This function is called from JetPlayer instance's render thread
  */
@@ -79,6 +101,8 @@
 android_media_JetPlayer_setup(JNIEnv *env, jobject thiz, jobject weak_this,
     jint maxTracks, jint trackBufferSize)
 {
+    initializeJavaIDs(env);
+
     //ALOGV("android_media_JetPlayer_setup(): entering.");
     JetPlayer* lpJet = new JetPlayer(env->NewGlobalRef(weak_this), maxTracks, trackBufferSize);
 
@@ -511,28 +535,9 @@
     {"native_clearQueue",  "()Z",                   (void *)android_media_JetPlayer_clearQueue},
 };
 
-#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj"
-#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative"
 
 
 int register_android_media_JetPlayer(JNIEnv *env)
 {
-    javaJetPlayerFields.jetClass = NULL;
-    javaJetPlayerFields.postNativeEventInJava = NULL;
-    javaJetPlayerFields.nativePlayerInJavaObj = NULL;
-
-    // Get the JetPlayer java class
-    jclass jetPlayerClass = FindClassOrDie(env, kClassPathName);
-    javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass);
-
-    // Get the mNativePlayerInJavaObj variable field
-    javaJetPlayerFields.nativePlayerInJavaObj = GetFieldIDOrDie(env,
-            jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J");
-
-    // Get the callback to post events from this native code to Java
-    javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
-            javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME,
-            "(Ljava/lang/Object;III)V");
-
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index 517672e..f491be8 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -202,10 +202,11 @@
     uuid = NULL;
 
     if (err != OK) {
-        jniThrowException(
+        std::string strerr(StrCryptoError(err));
+        jniThrowExceptionFmt(
                 env,
                 "android/media/MediaCryptoException",
-                "Failed to instantiate crypto object.");
+                "Failed to instantiate crypto object: %s", strerr.c_str());
         return;
     }
 
@@ -295,7 +296,8 @@
         } else if (err == NO_INIT) {
             msg += ": crypto plugin not initialized";
         } else {
-            msg.appendFormat(": general failure (%d)", err);
+            std::string strerr(StrCryptoError(err));
+            msg.appendFormat(": general failure (%s)", strerr.c_str());
         }
         jniThrowException(env, "android/media/MediaCryptoException", msg.string());
     }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 177b00a..b7cde57 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -226,7 +226,6 @@
     } else {
         ALOGE("LnbClientCallbackImpl::onEvent:"
                 "Lnb object has been freed. Ignoring callback.");
-        env->DeleteWeakGlobalRef(mLnbObj);
     }
 }
 
@@ -245,7 +244,6 @@
     } else {
         ALOGE("LnbClientCallbackImpl::onDiseqcMessage:"
                 "Lnb object has been freed. Ignoring callback.");
-        env->DeleteWeakGlobalRef(mLnbObj);
     }
 }
 
@@ -276,7 +274,6 @@
     } else {
         ALOGE("DvrClientCallbackImpl::onRecordStatus:"
                 "Dvr object has been freed. Ignoring callback.");
-        env->DeleteWeakGlobalRef(mDvrObj);
     }
 }
 
@@ -292,7 +289,6 @@
     } else {
         ALOGE("DvrClientCallbackImpl::onPlaybackStatus:"
                 "Dvr object has been freed. Ignoring callback.");
-        env->DeleteWeakGlobalRef(mDvrObj);
     }
 }
 
@@ -565,7 +561,7 @@
                 const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TsRecordEvent");
-    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJJ)V");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJJI)V");
 
     for (int i = 0; i < events.size(); i++) {
         auto event = events[i];
@@ -618,7 +614,7 @@
                 const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent");
-    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJIJ)V");
+    jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJIJII)V");
 
     for (int i = 0; i < events.size(); i++) {
         auto event = events[i];
@@ -846,7 +842,6 @@
     } else {
         ALOGE("FilterClientCallbackImpl::onFilterEvent_1_1:"
                 "Filter object has been freed. Ignoring callback.");
-        env->DeleteWeakGlobalRef(mFilterObj);
     }
 }
 
@@ -871,7 +866,6 @@
     } else {
         ALOGE("FilterClientCallbackImpl::onFilterStatus:"
                 "Filter object has been freed. Ignoring callback.");
-        env->DeleteWeakGlobalRef(mFilterObj);
     }
 }
 
@@ -907,7 +901,6 @@
     } else {
         ALOGE("FrontendClientCallbackImpl::onEvent:"
                 "Frontend object has been freed. Ignoring callback.");
-        env->DeleteWeakGlobalRef(mObject);
     }
 }
 
@@ -920,7 +913,6 @@
     if (env->IsSameObject(frontend, nullptr)) {
         ALOGE("FrontendClientCallbackImpl::onScanMessage:"
                 "Frontend object has been freed. Ignoring callback.");
-        env->DeleteWeakGlobalRef(mObject);
         return;
     }
     switch(type) {
@@ -1078,7 +1070,6 @@
     if (env->IsSameObject(frontend, nullptr)) {
         ALOGE("FrontendClientCallbackImpl::onScanMessageExt1_1:"
                 "Frontend object has been freed. Ignoring callback.");
-        env->DeleteWeakGlobalRef(mObject);
         return;
     }
     switch(type) {
@@ -1243,7 +1234,6 @@
     if (env->IsSameObject(tuner, nullptr)) {
         ALOGE("openFrontendByHandle"
                 "Tuner object has been freed. Failed to open frontend.");
-        env->DeleteWeakGlobalRef(mObject);
         return NULL;
     }
 
@@ -2211,6 +2201,10 @@
                         intBandwidth = static_cast<jint>(bandwidth.dvbt());
                         break;
                     }
+                    case FrontendBandwidth::hidl_discriminator::dvbc: {
+                        intBandwidth = static_cast<jint>(bandwidth.dvbc());
+                        break;
+                    }
                     case FrontendBandwidth::hidl_discriminator::isdbt: {
                         intBandwidth = static_cast<jint>(bandwidth.isdbt());
                         break;
@@ -2731,12 +2725,10 @@
     FrontendDvbsVcmMode vcmMode =
             static_cast<FrontendDvbsVcmMode>(
                     env->GetIntField(settings, env->GetFieldID(clazz, "mVcmMode", "I")));
-    FrontendDvbsCodeRate coderate = getDvbsCodeRate(env, settings);
 
     FrontendDvbsSettings frontendDvbsSettings {
             .frequency = freq,
             .modulation = modulation,
-            .coderate = coderate,
             .symbolRate = symbolRate,
             .rolloff = rolloff,
             .pilot = pilot,
@@ -2744,6 +2736,13 @@
             .standard = standard,
             .vcmMode = vcmMode,
     };
+
+    jobject jcodeRate = env->GetObjectField(settings, env->GetFieldID(clazz, "mCodeRate",
+            "Landroid/media/tv/tuner/frontend/DvbsCodeRate;"));
+    if (jcodeRate != NULL) {
+        frontendDvbsSettings.coderate = getDvbsCodeRate(env, settings);
+    }
+
     frontendSettings.dvbs(frontendDvbsSettings);
     return frontendSettings;
 }
@@ -2755,7 +2754,7 @@
             static_cast<FrontendDvbsScanType>(
                     env->GetIntField(settings, env->GetFieldID(clazz, "mScanType", "I")));
     bool isDiseqcRxMessage = static_cast<bool>(env->GetBooleanField(
-            settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "B")));
+            settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "Z")));
 
     FrontendDvbsSettingsExt1_1 dvbsExt1_1 {
         .scanType = scanType,
@@ -2995,6 +2994,10 @@
 static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) {
     ALOGD("getFrontendSettings %d", type);
 
+    if (type == static_cast<int>(::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) {
+        return FrontendSettings();
+    }
+
     FrontendType feType = static_cast<FrontendType>(type);
     switch(feType) {
         case FrontendType::ANALOG:
@@ -3568,26 +3571,28 @@
                 .tpid = tpid,
             };
 
-            DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype);
-            switch (tsType) {
-                case DemuxTsFilterType::SECTION:
-                    tsFilterSettings.filterSettings.section(
-                            getFilterSectionSettings(env, settingsObj));
-                    break;
-                case DemuxTsFilterType::AUDIO:
-                case DemuxTsFilterType::VIDEO:
-                    tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
-                    break;
-                case DemuxTsFilterType::PES:
-                    tsFilterSettings.filterSettings.pesData(
-                            getFilterPesDataSettings(env, settingsObj));
-                    break;
-                case DemuxTsFilterType::RECORD:
-                    tsFilterSettings.filterSettings.record(
-                            getFilterRecordSettings(env, settingsObj));
-                    break;
-                default:
-                    break;
+            if (settingsObj != NULL) {
+                DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype);
+                switch (tsType) {
+                    case DemuxTsFilterType::SECTION:
+                        tsFilterSettings.filterSettings.section(
+                                getFilterSectionSettings(env, settingsObj));
+                        break;
+                    case DemuxTsFilterType::AUDIO:
+                    case DemuxTsFilterType::VIDEO:
+                        tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
+                        break;
+                    case DemuxTsFilterType::PES:
+                        tsFilterSettings.filterSettings.pesData(
+                                getFilterPesDataSettings(env, settingsObj));
+                        break;
+                    case DemuxTsFilterType::RECORD:
+                        tsFilterSettings.filterSettings.record(
+                                getFilterRecordSettings(env, settingsObj));
+                        break;
+                    default:
+                        break;
+                }
             }
             filterSettings.ts(tsFilterSettings);
             break;
@@ -3599,60 +3604,55 @@
             DemuxMmtpFilterSettings mmtpFilterSettings {
                 .mmtpPid = mmtpPid,
             };
-            DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype);
-            switch (mmtpType) {
-                case DemuxMmtpFilterType::SECTION:
-                    mmtpFilterSettings.filterSettings.section(
-                            getFilterSectionSettings(env, settingsObj));
-                    break;
-                case DemuxMmtpFilterType::AUDIO:
-                case DemuxMmtpFilterType::VIDEO:
-                    mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
-                    break;
-                case DemuxMmtpFilterType::PES:
-                    mmtpFilterSettings.filterSettings.pesData(
-                            getFilterPesDataSettings(env, settingsObj));
-                    break;
-                case DemuxMmtpFilterType::RECORD:
-                    mmtpFilterSettings.filterSettings.record(
-                            getFilterRecordSettings(env, settingsObj));
-                    break;
-                case DemuxMmtpFilterType::DOWNLOAD:
-                    mmtpFilterSettings.filterSettings.download(
-                            getFilterDownloadSettings(env, settingsObj));
-                    break;
-                default:
-                    break;
+
+            if (settingsObj != NULL) {
+                DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype);
+                switch (mmtpType) {
+                    case DemuxMmtpFilterType::SECTION:
+                        mmtpFilterSettings.filterSettings.section(
+                                getFilterSectionSettings(env, settingsObj));
+                        break;
+                    case DemuxMmtpFilterType::AUDIO:
+                    case DemuxMmtpFilterType::VIDEO:
+                        mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
+                        break;
+                    case DemuxMmtpFilterType::PES:
+                        mmtpFilterSettings.filterSettings.pesData(
+                                getFilterPesDataSettings(env, settingsObj));
+                        break;
+                    case DemuxMmtpFilterType::RECORD:
+                        mmtpFilterSettings.filterSettings.record(
+                                getFilterRecordSettings(env, settingsObj));
+                        break;
+                    case DemuxMmtpFilterType::DOWNLOAD:
+                        mmtpFilterSettings.filterSettings.download(
+                                getFilterDownloadSettings(env, settingsObj));
+                        break;
+                    default:
+                        break;
+                }
             }
             filterSettings.mmtp(mmtpFilterSettings);
             break;
         }
         case DemuxFilterMainType::IP: {
             DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj);
-
             DemuxIpFilterSettings ipFilterSettings {
                 .ipAddr = ipAddr,
             };
+
             DemuxIpFilterType ipType = static_cast<DemuxIpFilterType>(subtype);
-            switch (ipType) {
-                case DemuxIpFilterType::SECTION: {
-                    ipFilterSettings.filterSettings.section(
-                            getFilterSectionSettings(env, settingsObj));
-                    break;
-                }
-                case DemuxIpFilterType::IP: {
-                    jclass clazz = env->FindClass(
-                            "android/media/tv/tuner/filter/IpFilterConfiguration");
-                    bool bPassthrough = static_cast<bool>(
-                            env->GetBooleanField(
-                                    filterConfigObj, env->GetFieldID(
-                                            clazz, "mPassthrough", "Z")));
-                    ipFilterSettings.filterSettings.bPassthrough(bPassthrough);
-                    break;
-                }
-                default: {
-                    break;
-                }
+            if (ipType == DemuxIpFilterType::SECTION && settingsObj != NULL) {
+                ipFilterSettings.filterSettings.section(
+                                getFilterSectionSettings(env, settingsObj));
+            } else if (ipType == DemuxIpFilterType::IP) {
+                jclass clazz = env->FindClass(
+                        "android/media/tv/tuner/filter/IpFilterConfiguration");
+                bool bPassthrough = static_cast<bool>(
+                        env->GetBooleanField(
+                                filterConfigObj, env->GetFieldID(
+                                        clazz, "mPassthrough", "Z")));
+                ipFilterSettings.filterSettings.bPassthrough(bPassthrough);
             }
             filterSettings.ip(ipFilterSettings);
             break;
@@ -3669,24 +3669,17 @@
                 .packetType = packetType,
                 .isCompressedIpPacket = isCompressedIpPacket,
             };
+
             DemuxTlvFilterType tlvType = static_cast<DemuxTlvFilterType>(subtype);
-            switch (tlvType) {
-                case DemuxTlvFilterType::SECTION: {
-                    tlvFilterSettings.filterSettings.section(
-                            getFilterSectionSettings(env, settingsObj));
-                    break;
-                }
-                case DemuxTlvFilterType::TLV: {
-                    bool bPassthrough = static_cast<bool>(
-                            env->GetBooleanField(
-                                    filterConfigObj, env->GetFieldID(
-                                            clazz, "mPassthrough", "Z")));
-                    tlvFilterSettings.filterSettings.bPassthrough(bPassthrough);
-                    break;
-                }
-                default: {
-                    break;
-                }
+            if (tlvType == DemuxTlvFilterType::SECTION && settingsObj != NULL) {
+                tlvFilterSettings.filterSettings.section(
+                        getFilterSectionSettings(env, settingsObj));
+            } else if (tlvType == DemuxTlvFilterType::TLV) {
+                bool bPassthrough = static_cast<bool>(
+                env->GetBooleanField(
+                        filterConfigObj, env->GetFieldID(
+                                clazz, "mPassthrough", "Z")));
+                tlvFilterSettings.filterSettings.bPassthrough(bPassthrough);
             }
             filterSettings.tlv(tlvFilterSettings);
             break;
@@ -3701,14 +3694,17 @@
                 .packetType = packetType,
                 .lengthType = lengthType,
             };
-            DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype);
-            switch (alpType) {
-                case DemuxAlpFilterType::SECTION:
-                    alpFilterSettings.filterSettings.section(
-                            getFilterSectionSettings(env, settingsObj));
-                    break;
-                default:
-                    break;
+
+            if (settingsObj != NULL) {
+                DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype);
+                switch (alpType) {
+                    case DemuxAlpFilterType::SECTION:
+                        alpFilterSettings.filterSettings.section(
+                                getFilterSectionSettings(env, settingsObj));
+                        break;
+                    default:
+                        break;
+                }
             }
             filterSettings.alp(alpFilterSettings);
             break;
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 4efdcac..ffed474 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -32,6 +32,7 @@
 
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
+#include "core_jni_helpers.h"
 #include <jni.h>
 #include <media/stagefright/NuMediaExtractor.h>
 #include <nativehelper/JNIHelp.h>
@@ -48,6 +49,7 @@
 
 // ----------------------------------------------------------------------------
 
+// MtpDatabase methods
 static jmethodID method_beginSendObject;
 static jmethodID method_endSendObject;
 static jmethodID method_rescanFile;
@@ -75,6 +77,7 @@
 static jmethodID method_getObjectReferences;
 static jmethodID method_setObjectReferences;
 
+// MtpDatabase fields.
 static jfieldID field_context;
 
 // MtpPropertyList methods
@@ -86,6 +89,59 @@
 static jmethodID method_getLongValues;
 static jmethodID method_getStringValues;
 
+// Initializer for the jfieldIDs and jmethodIDs above. This method must be invoked
+// before using these static fields and methods for JNI accesses.
+static void initializeJavaIDs(JNIEnv* env) {
+    static std::once_flag sJniInitialized;
+
+#define GET_METHOD_ID(name, jclass, signature)                              \
+    method_##name = GetMethodIDOrDie(env, jclass, #name, signature);
+
+    std::call_once(sJniInitialized, [](JNIEnv* env) {
+        const jclass mdb_class = FindClassOrDie(env, "android/mtp/MtpDatabase");
+        GET_METHOD_ID(beginSendObject, mdb_class, "(Ljava/lang/String;III)I");
+        GET_METHOD_ID(endSendObject, mdb_class, "(IZ)V");
+        GET_METHOD_ID(rescanFile, mdb_class, "(Ljava/lang/String;II)V");
+        GET_METHOD_ID(getObjectList, mdb_class, "(III)[I");
+        GET_METHOD_ID(getNumObjects, mdb_class, "(III)I");
+        GET_METHOD_ID(getSupportedPlaybackFormats, mdb_class, "()[I");
+        GET_METHOD_ID(getSupportedCaptureFormats, mdb_class, "()[I");
+        GET_METHOD_ID(getSupportedObjectProperties, mdb_class, "(I)[I");
+        GET_METHOD_ID(getSupportedDeviceProperties, mdb_class, "()[I");
+        GET_METHOD_ID(setObjectProperty, mdb_class, "(IIJLjava/lang/String;)I");
+        GET_METHOD_ID(getDeviceProperty, mdb_class, "(I[J[C)I");
+        GET_METHOD_ID(setDeviceProperty, mdb_class, "(IJLjava/lang/String;)I");
+        GET_METHOD_ID(getObjectPropertyList, mdb_class, "(IIIII)Landroid/mtp/MtpPropertyList;");
+        GET_METHOD_ID(getObjectInfo, mdb_class, "(I[I[C[J)Z");
+        GET_METHOD_ID(getObjectFilePath, mdb_class, "(I[C[J)I");
+        GET_METHOD_ID(openFilePath, mdb_class, "(Ljava/lang/String;Z)I");
+        GET_METHOD_ID(getThumbnailInfo, mdb_class, "(I[J)Z");
+        GET_METHOD_ID(getThumbnailData, mdb_class, "(I)[B");
+        GET_METHOD_ID(beginDeleteObject, mdb_class, "(I)I");
+        GET_METHOD_ID(endDeleteObject, mdb_class, "(IZ)V");
+        GET_METHOD_ID(beginMoveObject, mdb_class, "(III)I");
+        GET_METHOD_ID(endMoveObject, mdb_class, "(IIIIIZ)V");
+        GET_METHOD_ID(beginCopyObject, mdb_class, "(III)I");
+        GET_METHOD_ID(endCopyObject, mdb_class, "(IZ)V");
+        GET_METHOD_ID(getObjectReferences, mdb_class, "(I)[I");
+        GET_METHOD_ID(setObjectReferences, mdb_class, "(I[I)I");
+        field_context = GetFieldIDOrDie(env, mdb_class, "mNativeContext", "J");
+
+        const jclass mpl_class = FindClassOrDie(env, "android/mtp/MtpPropertyList");
+        GET_METHOD_ID(getCode, mpl_class, "()I");
+        GET_METHOD_ID(getCount, mpl_class, "()I");
+        GET_METHOD_ID(getObjectHandles, mpl_class, "()[I");
+        GET_METHOD_ID(getPropertyCodes, mpl_class, "()[I");
+        GET_METHOD_ID(getDataTypes, mpl_class, "()[I");
+        GET_METHOD_ID(getLongValues, mpl_class, "()[J");
+        GET_METHOD_ID(getStringValues, mpl_class, "()[Ljava/lang/String;");
+
+        return 0;
+    }, env);
+
+#undef GET_METHOD_ID
+}
+
 
 IMtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
     return (IMtpDatabase *)env->GetLongField(database, field_context);
@@ -1280,6 +1336,7 @@
 static void
 android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz)
 {
+    initializeJavaIDs(env);
     MtpDatabase* database = new MtpDatabase(env, thiz);
     env->SetLongField(thiz, field_context, (jlong)database);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -1314,69 +1371,9 @@
     {"format_date_time",        "(J)Ljava/lang/String;",
                                         (void *)android_mtp_MtpPropertyGroup_format_date_time},
 };
-
-#define GET_METHOD_ID(name, jclass, signature)                              \
-    method_##name = env->GetMethodID(jclass, #name, signature);             \
-    if (method_##name == NULL) {                                            \
-        ALOGE("Can't find " #name);                                         \
-        return -1;                                                          \
-    }                                                                       \
-
+                                                                   \
 int register_android_mtp_MtpDatabase(JNIEnv *env)
 {
-    jclass clazz;
-
-    clazz = env->FindClass("android/mtp/MtpDatabase");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/mtp/MtpDatabase");
-        return -1;
-    }
-    GET_METHOD_ID(beginSendObject, clazz, "(Ljava/lang/String;III)I");
-    GET_METHOD_ID(endSendObject, clazz, "(IZ)V");
-    GET_METHOD_ID(rescanFile, clazz, "(Ljava/lang/String;II)V");
-    GET_METHOD_ID(getObjectList, clazz, "(III)[I");
-    GET_METHOD_ID(getNumObjects, clazz, "(III)I");
-    GET_METHOD_ID(getSupportedPlaybackFormats, clazz, "()[I");
-    GET_METHOD_ID(getSupportedCaptureFormats, clazz, "()[I");
-    GET_METHOD_ID(getSupportedObjectProperties, clazz, "(I)[I");
-    GET_METHOD_ID(getSupportedDeviceProperties, clazz, "()[I");
-    GET_METHOD_ID(setObjectProperty, clazz, "(IIJLjava/lang/String;)I");
-    GET_METHOD_ID(getDeviceProperty, clazz, "(I[J[C)I");
-    GET_METHOD_ID(setDeviceProperty, clazz, "(IJLjava/lang/String;)I");
-    GET_METHOD_ID(getObjectPropertyList, clazz, "(IIIII)Landroid/mtp/MtpPropertyList;");
-    GET_METHOD_ID(getObjectInfo, clazz, "(I[I[C[J)Z");
-    GET_METHOD_ID(getObjectFilePath, clazz, "(I[C[J)I");
-    GET_METHOD_ID(openFilePath, clazz, "(Ljava/lang/String;Z)I");
-    GET_METHOD_ID(getThumbnailInfo, clazz, "(I[J)Z");
-    GET_METHOD_ID(getThumbnailData, clazz, "(I)[B");
-    GET_METHOD_ID(beginDeleteObject, clazz, "(I)I");
-    GET_METHOD_ID(endDeleteObject, clazz, "(IZ)V");
-    GET_METHOD_ID(beginMoveObject, clazz, "(III)I");
-    GET_METHOD_ID(endMoveObject, clazz, "(IIIIIZ)V");
-    GET_METHOD_ID(beginCopyObject, clazz, "(III)I");
-    GET_METHOD_ID(endCopyObject, clazz, "(IZ)V");
-    GET_METHOD_ID(getObjectReferences, clazz, "(I)[I");
-    GET_METHOD_ID(setObjectReferences, clazz, "(I[I)I");
-
-    field_context = env->GetFieldID(clazz, "mNativeContext", "J");
-    if (field_context == NULL) {
-        ALOGE("Can't find MtpDatabase.mNativeContext");
-        return -1;
-    }
-
-    clazz = env->FindClass("android/mtp/MtpPropertyList");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/mtp/MtpPropertyList");
-        return -1;
-    }
-    GET_METHOD_ID(getCode, clazz, "()I");
-    GET_METHOD_ID(getCount, clazz, "()I");
-    GET_METHOD_ID(getObjectHandles, clazz, "()[I");
-    GET_METHOD_ID(getPropertyCodes, clazz, "()[I");
-    GET_METHOD_ID(getDataTypes, clazz, "()[I");
-    GET_METHOD_ID(getLongValues, clazz, "()[J");
-    GET_METHOD_ID(getStringValues, clazz, "()[Ljava/lang/String;");
-
     if (AndroidRuntime::registerNativeMethods(env,
                 "android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods)))
         return -1;
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 060eaf9..3d2b00fe 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -34,6 +34,7 @@
 
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
+#include "core_jni_helpers.h"
 #include "nativehelper/ScopedLocalRef.h"
 #include "private/android_filesystem_config.h"
 
@@ -107,6 +108,95 @@
 static jfieldID field_event_parameter2;
 static jfieldID field_event_parameter3;
 
+// Initializer for  the jclasses, jfieldIDs and jmethodIDs declared above. This method must be
+// invoked before using these static fields for JNI accesses.
+static void initializeJavaIDs(JNIEnv* env) {
+    static std::once_flag sJniInitialized;
+
+    std::call_once(sJniInitialized, [](JNIEnv* env) {
+        clazz_deviceInfo =
+            (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpDeviceInfo"));
+        constructor_deviceInfo = GetMethodIDOrDie(env, clazz_deviceInfo, "<init>", "()V");
+        field_deviceInfo_manufacturer =
+            GetFieldIDOrDie(env, clazz_deviceInfo, "mManufacturer", "Ljava/lang/String;");
+        field_deviceInfo_model =
+            GetFieldIDOrDie(env, clazz_deviceInfo, "mModel", "Ljava/lang/String;");
+        field_deviceInfo_version =
+            GetFieldIDOrDie(env, clazz_deviceInfo, "mVersion", "Ljava/lang/String;");
+        field_deviceInfo_serialNumber =
+            GetFieldIDOrDie(env, clazz_deviceInfo, "mSerialNumber", "Ljava/lang/String;");
+        field_deviceInfo_operationsSupported =
+            GetFieldIDOrDie(env, clazz_deviceInfo, "mOperationsSupported", "[I");
+        field_deviceInfo_eventsSupported =
+            GetFieldIDOrDie(env, clazz_deviceInfo, "mEventsSupported", "[I");
+
+        clazz_storageInfo =
+            (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpStorageInfo"));
+        constructor_storageInfo = GetMethodIDOrDie(env, clazz_storageInfo, "<init>", "()V");
+        field_storageInfo_storageId = GetFieldIDOrDie(env, clazz_storageInfo, "mStorageId", "I");
+        field_storageInfo_maxCapacity =
+            GetFieldIDOrDie(env, clazz_storageInfo, "mMaxCapacity", "J");
+        field_storageInfo_freeSpace =
+            GetFieldIDOrDie(env, clazz_storageInfo, "mFreeSpace", "J");
+        field_storageInfo_description =
+            GetFieldIDOrDie(env, clazz_storageInfo, "mDescription", "Ljava/lang/String;");
+        field_storageInfo_volumeIdentifier =
+            GetFieldIDOrDie(env, clazz_storageInfo, "mVolumeIdentifier", "Ljava/lang/String;");
+
+        clazz_objectInfo =
+            (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpObjectInfo"));
+        constructor_objectInfo = GetMethodIDOrDie(env, clazz_objectInfo, "<init>", "()V");
+        field_objectInfo_handle = GetFieldIDOrDie(env, clazz_objectInfo, "mHandle", "I");
+        field_objectInfo_storageId = GetFieldIDOrDie(env, clazz_objectInfo, "mStorageId", "I");
+        field_objectInfo_format = GetFieldIDOrDie(env, clazz_objectInfo, "mFormat", "I");
+        field_objectInfo_protectionStatus =
+            GetFieldIDOrDie(env, clazz_objectInfo, "mProtectionStatus", "I");
+        field_objectInfo_compressedSize =
+            GetFieldIDOrDie(env, clazz_objectInfo, "mCompressedSize", "I");
+        field_objectInfo_thumbFormat = GetFieldIDOrDie(env, clazz_objectInfo, "mThumbFormat", "I");
+        field_objectInfo_thumbCompressedSize =
+            GetFieldIDOrDie(env, clazz_objectInfo, "mThumbCompressedSize", "I");
+        field_objectInfo_thumbPixWidth =
+            GetFieldIDOrDie(env, clazz_objectInfo, "mThumbPixWidth", "I");
+        field_objectInfo_thumbPixHeight =
+            GetFieldIDOrDie(env, clazz_objectInfo, "mThumbPixHeight", "I");
+        field_objectInfo_imagePixWidth =
+            GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixWidth", "I");
+        field_objectInfo_imagePixHeight =
+            GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixHeight", "I");
+        field_objectInfo_imagePixDepth =
+                GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixDepth", "I");
+        field_objectInfo_parent = GetFieldIDOrDie(env, clazz_objectInfo, "mParent", "I");
+        field_objectInfo_associationType =
+                GetFieldIDOrDie(env, clazz_objectInfo, "mAssociationType", "I");
+        field_objectInfo_associationDesc =
+                GetFieldIDOrDie(env, clazz_objectInfo, "mAssociationDesc", "I");
+        field_objectInfo_sequenceNumber =
+                GetFieldIDOrDie(env, clazz_objectInfo, "mSequenceNumber", "I");
+        field_objectInfo_name =
+                GetFieldIDOrDie(env, clazz_objectInfo, "mName", "Ljava/lang/String;");
+        field_objectInfo_dateCreated = GetFieldIDOrDie(env, clazz_objectInfo, "mDateCreated", "J");
+        field_objectInfo_dateModified =
+                GetFieldIDOrDie(env, clazz_objectInfo, "mDateModified", "J");
+        field_objectInfo_keywords =
+                GetFieldIDOrDie(env, clazz_objectInfo, "mKeywords", "Ljava/lang/String;");
+
+        clazz_event = (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpEvent"));
+        constructor_event = GetMethodIDOrDie(env, clazz_event, "<init>", "()V");
+        field_event_eventCode = GetFieldIDOrDie(env, clazz_event, "mEventCode", "I");
+        field_event_parameter1 = GetFieldIDOrDie(env, clazz_event, "mParameter1", "I");
+        field_event_parameter2 = GetFieldIDOrDie(env, clazz_event, "mParameter2", "I");
+        field_event_parameter3 = GetFieldIDOrDie(env, clazz_event, "mParameter3", "I");
+
+        const jclass clazz = FindClassOrDie(env, "android/mtp/MtpDevice");
+        field_context = GetFieldIDOrDie(env, clazz, "mNativeContext", "J");
+
+        clazz_io_exception = (jclass)env->NewGlobalRef(FindClassOrDie(env, "java/io/IOException"));
+        clazz_operation_canceled_exception =
+            (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/os/OperationCanceledException"));
+    }, env);
+}
+
 class JavaArrayWriter {
 public:
     JavaArrayWriter(JNIEnv* env, jbyteArray array) :
@@ -132,6 +222,11 @@
 
 MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice)
 {
+    // get_device_from_object() is called by the majority of JNI methods in this file. Call
+    // `initializeJavaIDs()` here to ensure JNI methodID's, fieldIDs and classes are initialized
+    // before use.
+    initializeJavaIDs(env);
+
     return (MtpDevice*)env->GetLongField(javaDevice, field_context);
 }
 
@@ -200,6 +295,8 @@
     MtpDevice* device = MtpDevice::open(deviceNameStr, fd);
     env->ReleaseStringUTFChars(deviceName, deviceNameStr);
 
+    // The jfieldID `field_context` needs to be initialized before use below.
+    initializeJavaIDs(env);
     if (device)
         env->SetLongField(thiz, field_context,  (jlong)device);
     return (jboolean)(device != NULL);
@@ -781,256 +878,7 @@
 
 int register_android_mtp_MtpDevice(JNIEnv *env)
 {
-    jclass clazz;
-
     ALOGD("register_android_mtp_MtpDevice\n");
-
-    clazz = env->FindClass("android/mtp/MtpDeviceInfo");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/mtp/MtpDeviceInfo");
-        return -1;
-    }
-    constructor_deviceInfo = env->GetMethodID(clazz, "<init>", "()V");
-    if (constructor_deviceInfo == NULL) {
-        ALOGE("Can't find android/mtp/MtpDeviceInfo constructor");
-        return -1;
-    }
-    field_deviceInfo_manufacturer = env->GetFieldID(clazz, "mManufacturer", "Ljava/lang/String;");
-    if (field_deviceInfo_manufacturer == NULL) {
-        ALOGE("Can't find MtpDeviceInfo.mManufacturer");
-        return -1;
-    }
-    field_deviceInfo_model = env->GetFieldID(clazz, "mModel", "Ljava/lang/String;");
-    if (field_deviceInfo_model == NULL) {
-        ALOGE("Can't find MtpDeviceInfo.mModel");
-        return -1;
-    }
-    field_deviceInfo_version = env->GetFieldID(clazz, "mVersion", "Ljava/lang/String;");
-    if (field_deviceInfo_version == NULL) {
-        ALOGE("Can't find MtpDeviceInfo.mVersion");
-        return -1;
-    }
-    field_deviceInfo_serialNumber = env->GetFieldID(clazz, "mSerialNumber", "Ljava/lang/String;");
-    if (field_deviceInfo_serialNumber == NULL) {
-        ALOGE("Can't find MtpDeviceInfo.mSerialNumber");
-        return -1;
-    }
-    field_deviceInfo_operationsSupported = env->GetFieldID(clazz, "mOperationsSupported", "[I");
-    if (field_deviceInfo_operationsSupported == NULL) {
-        ALOGE("Can't find MtpDeviceInfo.mOperationsSupported");
-        return -1;
-    }
-    field_deviceInfo_eventsSupported = env->GetFieldID(clazz, "mEventsSupported", "[I");
-    if (field_deviceInfo_eventsSupported == NULL) {
-        ALOGE("Can't find MtpDeviceInfo.mEventsSupported");
-        return -1;
-    }
-    clazz_deviceInfo = (jclass)env->NewGlobalRef(clazz);
-
-    clazz = env->FindClass("android/mtp/MtpStorageInfo");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/mtp/MtpStorageInfo");
-        return -1;
-    }
-    constructor_storageInfo = env->GetMethodID(clazz, "<init>", "()V");
-    if (constructor_storageInfo == NULL) {
-        ALOGE("Can't find android/mtp/MtpStorageInfo constructor");
-        return -1;
-    }
-    field_storageInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I");
-    if (field_storageInfo_storageId == NULL) {
-        ALOGE("Can't find MtpStorageInfo.mStorageId");
-        return -1;
-    }
-    field_storageInfo_maxCapacity = env->GetFieldID(clazz, "mMaxCapacity", "J");
-    if (field_storageInfo_maxCapacity == NULL) {
-        ALOGE("Can't find MtpStorageInfo.mMaxCapacity");
-        return -1;
-    }
-    field_storageInfo_freeSpace = env->GetFieldID(clazz, "mFreeSpace", "J");
-    if (field_storageInfo_freeSpace == NULL) {
-        ALOGE("Can't find MtpStorageInfo.mFreeSpace");
-        return -1;
-    }
-    field_storageInfo_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
-    if (field_storageInfo_description == NULL) {
-        ALOGE("Can't find MtpStorageInfo.mDescription");
-        return -1;
-    }
-    field_storageInfo_volumeIdentifier = env->GetFieldID(clazz, "mVolumeIdentifier", "Ljava/lang/String;");
-    if (field_storageInfo_volumeIdentifier == NULL) {
-        ALOGE("Can't find MtpStorageInfo.mVolumeIdentifier");
-        return -1;
-    }
-    clazz_storageInfo = (jclass)env->NewGlobalRef(clazz);
-
-    clazz = env->FindClass("android/mtp/MtpObjectInfo");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/mtp/MtpObjectInfo");
-        return -1;
-    }
-    constructor_objectInfo = env->GetMethodID(clazz, "<init>", "()V");
-    if (constructor_objectInfo == NULL) {
-        ALOGE("Can't find android/mtp/MtpObjectInfo constructor");
-        return -1;
-    }
-    field_objectInfo_handle = env->GetFieldID(clazz, "mHandle", "I");
-    if (field_objectInfo_handle == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mHandle");
-        return -1;
-    }
-    field_objectInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I");
-    if (field_objectInfo_storageId == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mStorageId");
-        return -1;
-    }
-    field_objectInfo_format = env->GetFieldID(clazz, "mFormat", "I");
-    if (field_objectInfo_format == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mFormat");
-        return -1;
-    }
-    field_objectInfo_protectionStatus = env->GetFieldID(clazz, "mProtectionStatus", "I");
-    if (field_objectInfo_protectionStatus == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mProtectionStatus");
-        return -1;
-    }
-    field_objectInfo_compressedSize = env->GetFieldID(clazz, "mCompressedSize", "I");
-    if (field_objectInfo_compressedSize == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mCompressedSize");
-        return -1;
-    }
-    field_objectInfo_thumbFormat = env->GetFieldID(clazz, "mThumbFormat", "I");
-    if (field_objectInfo_thumbFormat == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mThumbFormat");
-        return -1;
-    }
-    field_objectInfo_thumbCompressedSize = env->GetFieldID(clazz, "mThumbCompressedSize", "I");
-    if (field_objectInfo_thumbCompressedSize == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mThumbCompressedSize");
-        return -1;
-    }
-    field_objectInfo_thumbPixWidth = env->GetFieldID(clazz, "mThumbPixWidth", "I");
-    if (field_objectInfo_thumbPixWidth == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mThumbPixWidth");
-        return -1;
-    }
-    field_objectInfo_thumbPixHeight = env->GetFieldID(clazz, "mThumbPixHeight", "I");
-    if (field_objectInfo_thumbPixHeight == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mThumbPixHeight");
-        return -1;
-    }
-    field_objectInfo_imagePixWidth = env->GetFieldID(clazz, "mImagePixWidth", "I");
-    if (field_objectInfo_imagePixWidth == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mImagePixWidth");
-        return -1;
-    }
-    field_objectInfo_imagePixHeight = env->GetFieldID(clazz, "mImagePixHeight", "I");
-    if (field_objectInfo_imagePixHeight == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mImagePixHeight");
-        return -1;
-    }
-    field_objectInfo_imagePixDepth = env->GetFieldID(clazz, "mImagePixDepth", "I");
-    if (field_objectInfo_imagePixDepth == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mImagePixDepth");
-        return -1;
-    }
-    field_objectInfo_parent = env->GetFieldID(clazz, "mParent", "I");
-    if (field_objectInfo_parent == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mParent");
-        return -1;
-    }
-    field_objectInfo_associationType = env->GetFieldID(clazz, "mAssociationType", "I");
-    if (field_objectInfo_associationType == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mAssociationType");
-        return -1;
-    }
-    field_objectInfo_associationDesc = env->GetFieldID(clazz, "mAssociationDesc", "I");
-    if (field_objectInfo_associationDesc == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mAssociationDesc");
-        return -1;
-    }
-    field_objectInfo_sequenceNumber = env->GetFieldID(clazz, "mSequenceNumber", "I");
-    if (field_objectInfo_sequenceNumber == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mSequenceNumber");
-        return -1;
-    }
-    field_objectInfo_name = env->GetFieldID(clazz, "mName", "Ljava/lang/String;");
-    if (field_objectInfo_name == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mName");
-        return -1;
-    }
-    field_objectInfo_dateCreated = env->GetFieldID(clazz, "mDateCreated", "J");
-    if (field_objectInfo_dateCreated == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mDateCreated");
-        return -1;
-    }
-    field_objectInfo_dateModified = env->GetFieldID(clazz, "mDateModified", "J");
-    if (field_objectInfo_dateModified == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mDateModified");
-        return -1;
-    }
-    field_objectInfo_keywords = env->GetFieldID(clazz, "mKeywords", "Ljava/lang/String;");
-    if (field_objectInfo_keywords == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mKeywords");
-        return -1;
-    }
-    clazz_objectInfo = (jclass)env->NewGlobalRef(clazz);
-
-    clazz = env->FindClass("android/mtp/MtpEvent");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/mtp/MtpEvent");
-        return -1;
-    }
-    constructor_event = env->GetMethodID(clazz, "<init>", "()V");
-    if (constructor_event == NULL) {
-        ALOGE("Can't find android/mtp/MtpEvent constructor");
-        return -1;
-    }
-    field_event_eventCode = env->GetFieldID(clazz, "mEventCode", "I");
-    if (field_event_eventCode == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mEventCode");
-        return -1;
-    }
-    field_event_parameter1 = env->GetFieldID(clazz, "mParameter1", "I");
-    if (field_event_parameter1 == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mParameter1");
-        return -1;
-    }
-    field_event_parameter2 = env->GetFieldID(clazz, "mParameter2", "I");
-    if (field_event_parameter2 == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mParameter2");
-        return -1;
-    }
-    field_event_parameter3 = env->GetFieldID(clazz, "mParameter3", "I");
-    if (field_event_parameter3 == NULL) {
-        ALOGE("Can't find MtpObjectInfo.mParameter3");
-        return -1;
-    }
-    clazz_event = (jclass)env->NewGlobalRef(clazz);
-
-    clazz = env->FindClass("android/mtp/MtpDevice");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/mtp/MtpDevice");
-        return -1;
-    }
-    field_context = env->GetFieldID(clazz, "mNativeContext", "J");
-    if (field_context == NULL) {
-        ALOGE("Can't find MtpDevice.mNativeContext");
-        return -1;
-    }
-    clazz = env->FindClass("java/io/IOException");
-    if (clazz == NULL) {
-        ALOGE("Can't find java.io.IOException");
-        return -1;
-    }
-    clazz_io_exception = (jclass)env->NewGlobalRef(clazz);
-    clazz = env->FindClass("android/os/OperationCanceledException");
-    if (clazz == NULL) {
-        ALOGE("Can't find android.os.OperationCanceledException");
-        return -1;
-    }
-    clazz_operation_canceled_exception = (jclass)env->NewGlobalRef(clazz);
-
     return AndroidRuntime::registerNativeMethods(env,
                 "android/mtp/MtpDevice", gMethods, NELEM(gMethods));
 }
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index 8a1ae92..b4844f7 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -24,6 +24,7 @@
 #include <fcntl.h>
 #include <utils/threads.h>
 
+#include "core_jni_helpers.h"
 #include "jni.h"
 #include <nativehelper/JNIPlatformHelp.h>
 #include "android_runtime/AndroidRuntime.h"
@@ -34,6 +35,8 @@
 
 using namespace android;
 
+static Mutex sMutex;
+
 // MtpServer fields
 static jfieldID field_MtpServer_nativeContext;
 
@@ -44,7 +47,25 @@
 static jfieldID field_MtpStorage_removable;
 static jfieldID field_MtpStorage_maxFileSize;
 
-static Mutex sMutex;
+// Initializer for the jfieldIDs above. This method must be invoked before accessing MtpServer and
+// MtpStorage fields.
+static void initializeJavaIDs(JNIEnv* env) {
+    static std::once_flag sJniInitialized;
+
+    std::call_once(sJniInitialized, [](JNIEnv *env) {
+        const jclass storage_clazz = FindClassOrDie(env, "android/mtp/MtpStorage");
+        field_MtpStorage_storageId = GetFieldIDOrDie(env, storage_clazz, "mStorageId", "I");
+        field_MtpStorage_path =
+                GetFieldIDOrDie(env, storage_clazz, "mPath", "Ljava/lang/String;");
+        field_MtpStorage_description =
+                GetFieldIDOrDie(env, storage_clazz, "mDescription", "Ljava/lang/String;");
+        field_MtpStorage_removable = GetFieldIDOrDie(env, storage_clazz, "mRemovable", "Z");
+        field_MtpStorage_maxFileSize = GetFieldIDOrDie(env, storage_clazz, "mMaxFileSize", "J");
+
+        const jclass server_clazz = FindClassOrDie(env, "android/mtp/MtpServer");
+        field_MtpServer_nativeContext = GetFieldIDOrDie(env, server_clazz, "mNativeContext", "J");
+    }, env);
+}
 
 // ----------------------------------------------------------------------------
 
@@ -52,6 +73,7 @@
 extern IMtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
 
 static inline MtpServer* getMtpServer(JNIEnv *env, jobject thiz) {
+    initializeJavaIDs(env);
     return (MtpServer*)env->GetLongField(thiz, field_MtpServer_nativeContext);
 }
 
@@ -60,6 +82,8 @@
         jboolean usePtp, jstring deviceInfoManufacturer, jstring deviceInfoModel,
         jstring deviceInfoDeviceVersion, jstring deviceInfoSerialNumber)
 {
+    initializeJavaIDs(env);
+
     const char *deviceInfoManufacturerStr = env->GetStringUTFChars(deviceInfoManufacturer, NULL);
     const char *deviceInfoModelStr = env->GetStringUTFChars(deviceInfoModel, NULL);
     const char *deviceInfoDeviceVersionStr = env->GetStringUTFChars(deviceInfoDeviceVersion, NULL);
@@ -224,50 +248,6 @@
 
 int register_android_mtp_MtpServer(JNIEnv *env)
 {
-    jclass clazz;
-
-    clazz = env->FindClass("android/mtp/MtpStorage");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/mtp/MtpStorage");
-        return -1;
-    }
-    field_MtpStorage_storageId = env->GetFieldID(clazz, "mStorageId", "I");
-    if (field_MtpStorage_storageId == NULL) {
-        ALOGE("Can't find MtpStorage.mStorageId");
-        return -1;
-    }
-    field_MtpStorage_path = env->GetFieldID(clazz, "mPath", "Ljava/lang/String;");
-    if (field_MtpStorage_path == NULL) {
-        ALOGE("Can't find MtpStorage.mPath");
-        return -1;
-    }
-    field_MtpStorage_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
-    if (field_MtpStorage_description == NULL) {
-        ALOGE("Can't find MtpStorage.mDescription");
-        return -1;
-    }
-    field_MtpStorage_removable = env->GetFieldID(clazz, "mRemovable", "Z");
-    if (field_MtpStorage_removable == NULL) {
-        ALOGE("Can't find MtpStorage.mRemovable");
-        return -1;
-    }
-    field_MtpStorage_maxFileSize = env->GetFieldID(clazz, "mMaxFileSize", "J");
-    if (field_MtpStorage_maxFileSize == NULL) {
-        ALOGE("Can't find MtpStorage.mMaxFileSize");
-        return -1;
-    }
-
-    clazz = env->FindClass("android/mtp/MtpServer");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/mtp/MtpServer");
-        return -1;
-    }
-    field_MtpServer_nativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
-    if (field_MtpServer_nativeContext == NULL) {
-        ALOGE("Can't find MtpServer.mNativeContext");
-        return -1;
-    }
-
     return AndroidRuntime::registerNativeMethods(env,
                 "android/mtp/MtpServer", gMethods, NELEM(gMethods));
 }
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index f400a71..f54e266 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -588,14 +588,15 @@
                 break;
             }
             case TunerFrontendStatus::codeRates: {
-                int size = s.get<TunerFrontendStatus::codeRates>().size();
-                status.codeRates().resize(size);
-                for (int i = 0; i < size; i++) {
-                    auto aidlCodeRate = s.get<TunerFrontendStatus::codeRates>()[i];
-                    status.codeRates()[i] =
-                            static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate);
+                vector<hardware::tv::tuner::V1_1::FrontendInnerFec> codeRates;
+                for (auto aidlCodeRate : s.get<TunerFrontendStatus::codeRates>()) {
+                    codeRates.push_back(
+                            static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate));
                 }
-                hidlStatus.push_back(status);
+                if (codeRates.size() > 0) {
+                    status.codeRates(codeRates);
+                    hidlStatus.push_back(status);
+                }
                 break;
             }
             case TunerFrontendStatus::bandwidth: {
diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp
index 1a51218..fdca3fa 100644
--- a/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp
+++ b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test_helper_app {
     name: "UidImportanceHelperApp",
     manifest: "HelperAppManifest.xml",
@@ -8,4 +17,3 @@
         "general-tests",
     ],
 }
-
diff --git a/native/android/tests/activitymanager/nativeTests/Android.bp b/native/android/tests/activitymanager/nativeTests/Android.bp
index d4b5015..528ac12 100644
--- a/native/android/tests/activitymanager/nativeTests/Android.bp
+++ b/native/android/tests/activitymanager/nativeTests/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 cc_test {
     name: "ActivityManagerNativeTestCases",
 
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 91bad7b..f9795c6 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -41,13 +41,13 @@
         android:supportsRtl="true">
 
         <service
-            android:name=".DeviceDiscoveryService"
+            android:name=".CompanionDeviceDiscoveryService"
             android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
             android:exported="true">
         </service>
 
         <activity
-            android:name=".DeviceChooserActivity"
+            android:name=".CompanionDeviceActivity"
             android:theme="@style/ChooserActivity"
             android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
             android:exported="true">
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
similarity index 62%
rename from packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
rename to packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index c30d4bf..f1a98ad 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -24,6 +24,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.companion.AssociationRequest;
@@ -31,41 +32,52 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.database.DataSetObserver;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.text.Html;
 import android.util.Log;
+import android.util.SparseArray;
+import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.View;
-import android.widget.AdapterView;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
 import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
-import com.android.companiondevicemanager.DeviceDiscoveryService.DeviceFilterPair;
+import com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DeviceFilterPair;
 import com.android.internal.util.Preconditions;
 
-public class DeviceChooserActivity extends Activity {
+public class CompanionDeviceActivity extends Activity {
 
     private static final boolean DEBUG = false;
-    private static final String LOG_TAG = "DeviceChooserActivity";
+    private static final String LOG_TAG = CompanionDeviceActivity.class.getSimpleName();
+
+    static CompanionDeviceActivity sInstance;
 
     View mLoadingIndicator = null;
     ListView mDeviceListView;
     private View mPairButton;
     private View mCancelButton;
 
+    DevicesAdapter mDevicesAdapter;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        if (DEBUG) Log.i(LOG_TAG, "Started with intent " + getIntent());
+        Log.i(LOG_TAG, "Starting UI for " + getService().mRequest);
 
         if (getService().mDevicesFound.isEmpty()) {
             Log.e(LOG_TAG, "About to show UI, but no devices to show");
         }
 
         getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+        sInstance = this;
 
         String deviceProfile = getRequest().getDeviceProfile();
         String profilePrivacyDisclaimer = emptyIfNull(getRequest()
@@ -96,17 +108,14 @@
                     profileName,
                     getCallingAppName()), 0));
             mDeviceListView = findViewById(R.id.device_list);
-            final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter;
-            mDeviceListView.setAdapter(adapter);
-            mDeviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
-                @Override
-                public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) {
-                    getService().mSelectedDevice =
-                            (DeviceFilterPair) adapterView.getItemAtPosition(pos);
-                    adapter.notifyDataSetChanged();
-                }
+            mDevicesAdapter = new DevicesAdapter();
+            mDeviceListView.setAdapter(mDevicesAdapter);
+            mDeviceListView.setOnItemClickListener((adapterView, view, pos, l) -> {
+                getService().mSelectedDevice =
+                        (DeviceFilterPair) adapterView.getItemAtPosition(pos);
+                mDevicesAdapter.notifyDataSetChanged();
             });
-            adapter.registerDataSetObserver(new DataSetObserver() {
+            mDevicesAdapter.registerDataSetObserver(new DataSetObserver() {
                 @Override
                 public void onChanged() {
                     onSelectionUpdate();
@@ -133,6 +142,12 @@
         mCancelButton.setOnClickListener(v -> cancel());
     }
 
+    static void notifyDevicesChanged() {
+        if (sInstance != null && sInstance.mDevicesAdapter != null && !sInstance.isFinishing()) {
+            sInstance.mDevicesAdapter.notifyDataSetChanged();
+        }
+    }
+
     private AssociationRequest getRequest() {
         return getService().mRequest;
     }
@@ -156,6 +171,7 @@
     }
 
     private void cancel() {
+        Log.i(LOG_TAG, "cancel()");
         getService().onCancel();
         setResult(RESULT_CANCELED);
         finish();
@@ -170,6 +186,14 @@
         }
     }
 
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (sInstance == this) {
+            sInstance = null;
+        }
+    }
+
     private CharSequence getCallingAppName() {
         try {
             final PackageManager packageManager = getPackageManager();
@@ -217,15 +241,91 @@
         }
     }
 
-    private DeviceDiscoveryService getService() {
-        return DeviceDiscoveryService.sInstance;
+    private CompanionDeviceDiscoveryService getService() {
+        return CompanionDeviceDiscoveryService.sInstance;
     }
 
-   protected void onDeviceConfirmed(DeviceFilterPair selectedDevice) {
+    protected void onDeviceConfirmed(DeviceFilterPair selectedDevice) {
+        Log.i(LOG_TAG, "onDeviceConfirmed(selectedDevice = " + selectedDevice + ")");
         getService().onDeviceSelected(
                 getCallingPackage(), getDeviceMacAddress(selectedDevice.device));
         setResult(RESULT_OK,
                 new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device));
         finish();
     }
+
+    class DevicesAdapter extends BaseAdapter {
+        private final Drawable mBluetoothIcon = icon(android.R.drawable.stat_sys_data_bluetooth);
+        private final Drawable mWifiIcon = icon(com.android.internal.R.drawable.ic_wifi_signal_3);
+
+        private SparseArray<Integer> mColors = new SparseArray();
+
+        private Drawable icon(int drawableRes) {
+            Drawable icon = getResources().getDrawable(drawableRes, null);
+            icon.setTint(Color.DKGRAY);
+            return icon;
+        }
+
+        @Override
+        public View getView(
+                int position,
+                @Nullable View convertView,
+                @NonNull ViewGroup parent) {
+            TextView view = convertView instanceof TextView
+                    ? (TextView) convertView
+                    : newView();
+            bind(view, getItem(position));
+            return view;
+        }
+
+        private void bind(TextView textView, DeviceFilterPair device) {
+            textView.setText(device.getDisplayName());
+            textView.setBackgroundColor(
+                    device.equals(getService().mSelectedDevice)
+                            ? getColor(android.R.attr.colorControlHighlight)
+                            : Color.TRANSPARENT);
+            textView.setCompoundDrawablesWithIntrinsicBounds(
+                    device.device instanceof android.net.wifi.ScanResult
+                            ? mWifiIcon
+                            : mBluetoothIcon,
+                    null, null, null);
+            textView.getCompoundDrawables()[0].setTint(getColor(android.R.attr.colorForeground));
+        }
+
+        private TextView newView() {
+            final TextView textView = new TextView(CompanionDeviceActivity.this);
+            textView.setTextColor(getColor(android.R.attr.colorForeground));
+            final int padding = CompanionDeviceActivity.getPadding(getResources());
+            textView.setPadding(padding, padding, padding, padding);
+            textView.setCompoundDrawablePadding(padding);
+            return textView;
+        }
+
+        private int getColor(int colorAttr) {
+            if (mColors.contains(colorAttr)) {
+                return mColors.get(colorAttr);
+            }
+            TypedValue typedValue = new TypedValue();
+            TypedArray a = obtainStyledAttributes(typedValue.data, new int[] { colorAttr });
+            int result = a.getColor(0, 0);
+            a.recycle();
+            mColors.put(colorAttr, result);
+            return result;
+        }
+
+        @Override
+        public int getCount() {
+            return getService().mDevicesFound.size();
+        }
+
+        @Override
+        public DeviceFilterPair getItem(int position) {
+            return getService().mDevicesFound.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+    }
 }
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
similarity index 80%
rename from packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
rename to packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index 67d4b41..e620dfb 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -50,9 +50,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
 import android.os.IBinder;
@@ -60,12 +57,6 @@
 import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.SparseArray;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
 
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.util.ArrayUtils;
@@ -76,14 +67,14 @@
 import java.util.List;
 import java.util.Objects;
 
-public class DeviceDiscoveryService extends Service {
+public class CompanionDeviceDiscoveryService extends Service {
 
     private static final boolean DEBUG = false;
-    private static final String LOG_TAG = "DeviceDiscoveryService";
+    private static final String LOG_TAG = CompanionDeviceDiscoveryService.class.getSimpleName();
 
     private static final long SCAN_TIMEOUT = 20000;
 
-    static DeviceDiscoveryService sInstance;
+    static CompanionDeviceDiscoveryService sInstance;
 
     private BluetoothManager mBluetoothManager;
     private BluetoothAdapter mBluetoothAdapter;
@@ -102,12 +93,12 @@
     AssociationRequest mRequest;
     List<DeviceFilterPair> mDevicesFound;
     DeviceFilterPair mSelectedDevice;
-    DevicesAdapter mDevicesAdapter;
     IFindDeviceCallback mFindCallback;
 
     AndroidFuture<Association> mServiceCallback;
     boolean mIsScanning = false;
-    @Nullable DeviceChooserActivity mActivity = null;
+    @Nullable
+    CompanionDeviceActivity mActivity = null;
 
     private final ICompanionDeviceDiscoveryService mBinder =
             new ICompanionDeviceDiscoveryService.Stub() {
@@ -125,7 +116,8 @@
             mFindCallback = findCallback;
             mServiceCallback = serviceCallback;
             Handler.getMain().sendMessage(obtainMessage(
-                    DeviceDiscoveryService::startDiscovery, DeviceDiscoveryService.this, request));
+                    CompanionDeviceDiscoveryService::startDiscovery,
+                    CompanionDeviceDiscoveryService.this, request));
         }
     };
 
@@ -151,7 +143,6 @@
         mWifiManager = getSystemService(WifiManager.class);
 
         mDevicesFound = new ArrayList<>();
-        mDevicesAdapter = new DevicesAdapter();
 
         sInstance = this;
     }
@@ -165,7 +156,8 @@
             mWifiFilters = CollectionUtils.filter(mFilters, WifiDeviceFilter.class);
             mBluetoothFilters = CollectionUtils.filter(mFilters, BluetoothDeviceFilter.class);
             mBLEFilters = CollectionUtils.filter(mFilters, BluetoothLeDeviceFilter.class);
-            mBLEScanFilters = CollectionUtils.map(mBLEFilters, BluetoothLeDeviceFilter::getScanFilter);
+            mBLEScanFilters
+                    = CollectionUtils.map(mBLEFilters, BluetoothLeDeviceFilter::getScanFilter);
 
             reset();
         } else if (DEBUG) Log.i(LOG_TAG, "startDiscovery: duplicate request: " + request);
@@ -223,7 +215,7 @@
         }
         mIsScanning = true;
         Handler.getMain().sendMessageDelayed(
-                obtainMessage(DeviceDiscoveryService::stopScan, this),
+                obtainMessage(CompanionDeviceDiscoveryService::stopScan, this),
                 SCAN_TIMEOUT);
     }
 
@@ -237,7 +229,7 @@
         stopScan();
         mDevicesFound.clear();
         mSelectedDevice = null;
-        mDevicesAdapter.notifyDataSetChanged();
+        CompanionDeviceActivity.notifyDevicesChanged();
     }
 
     @Override
@@ -252,7 +244,7 @@
         if (!mIsScanning) return;
         mIsScanning = false;
 
-        DeviceChooserActivity activity = mActivity;
+        CompanionDeviceActivity activity = mActivity;
         if (activity != null) {
             if (activity.mDeviceListView != null) {
                 activity.mDeviceListView.removeFooterView(activity.mLoadingIndicator);
@@ -276,7 +268,7 @@
         if (device == null) return;
 
         Handler.getMain().sendMessage(obtainMessage(
-                DeviceDiscoveryService::onDeviceFoundMainThread, this, device));
+                CompanionDeviceDiscoveryService::onDeviceFoundMainThread, this, device));
     }
 
     @MainThread
@@ -292,7 +284,7 @@
             onReadyToShowUI();
         }
         mDevicesFound.add(device);
-        mDevicesAdapter.notifyDataSetChanged();
+        CompanionDeviceActivity.notifyDevicesChanged();
     }
 
     //TODO also, on timeout -> call onFailure
@@ -300,7 +292,7 @@
         try {
             mFindCallback.onSuccess(PendingIntent.getActivity(
                     this, 0,
-                    new Intent(this, DeviceChooserActivity.class),
+                    new Intent(this, CompanionDeviceActivity.class),
                     PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT
                             | PendingIntent.FLAG_IMMUTABLE));
         } catch (RemoteException e) {
@@ -311,13 +303,13 @@
     private void onDeviceLost(@Nullable DeviceFilterPair device) {
         Log.i(LOG_TAG, "Lost device " + device.getDisplayName());
         Handler.getMain().sendMessage(obtainMessage(
-                DeviceDiscoveryService::onDeviceLostMainThread, this, device));
+                CompanionDeviceDiscoveryService::onDeviceLostMainThread, this, device));
     }
 
     @MainThread
     void onDeviceLostMainThread(@Nullable DeviceFilterPair device) {
         mDevicesFound.remove(device);
-        mDevicesAdapter.notifyDataSetChanged();
+        CompanionDeviceActivity.notifyDevicesChanged();
     }
 
     void onDeviceSelected(String callingPackage, String deviceAddress) {
@@ -331,81 +323,6 @@
         mServiceCallback.cancel(true);
     }
 
-    class DevicesAdapter extends BaseAdapter {
-        private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth);
-        private Drawable WIFI_ICON = icon(com.android.internal.R.drawable.ic_wifi_signal_3);
-
-        private SparseArray<Integer> mColors = new SparseArray();
-
-        private Drawable icon(int drawableRes) {
-            Drawable icon = getResources().getDrawable(drawableRes, null);
-            icon.setTint(Color.DKGRAY);
-            return icon;
-        }
-
-        @Override
-        public View getView(
-                int position,
-                @Nullable View convertView,
-                @NonNull ViewGroup parent) {
-            TextView view = convertView instanceof TextView
-                    ? (TextView) convertView
-                    : newView();
-            bind(view, getItem(position));
-            return view;
-        }
-
-        private void bind(TextView textView, DeviceFilterPair device) {
-            textView.setText(device.getDisplayName());
-            textView.setBackgroundColor(
-                    device.equals(mSelectedDevice)
-                            ? getColor(android.R.attr.colorControlHighlight)
-                            : Color.TRANSPARENT);
-            textView.setCompoundDrawablesWithIntrinsicBounds(
-                    device.device instanceof android.net.wifi.ScanResult
-                        ? WIFI_ICON
-                        : BLUETOOTH_ICON,
-                    null, null, null);
-            textView.getCompoundDrawables()[0].setTint(getColor(android.R.attr.colorForeground));
-        }
-
-        private TextView newView() {
-            final TextView textView = new TextView(DeviceDiscoveryService.this);
-            textView.setTextColor(getColor(android.R.attr.colorForeground));
-            final int padding = DeviceChooserActivity.getPadding(getResources());
-            textView.setPadding(padding, padding, padding, padding);
-            textView.setCompoundDrawablePadding(padding);
-            return textView;
-        }
-
-        private int getColor(int colorAttr) {
-            if (mColors.contains(colorAttr)) {
-                return mColors.get(colorAttr);
-            }
-            TypedValue typedValue = new TypedValue();
-            TypedArray a = obtainStyledAttributes(typedValue.data, new int[] { colorAttr });
-            int result = a.getColor(0, 0);
-            a.recycle();
-            mColors.put(colorAttr, result);
-            return result;
-        }
-
-        @Override
-        public int getCount() {
-            return mDevicesFound.size();
-        }
-
-        @Override
-        public DeviceFilterPair getItem(int position) {
-            return mDevicesFound.get(position);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-    }
-
     /**
      * A pair of device and a filter that matched this device if any.
      *
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index c7e261c..86b85e83 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -58,3 +58,28 @@
         "//packages/modules/Connectivity:__subpackages__",
     ],
 }
+
+java_sdk_library {
+    name: "framework-connectivity",
+    api_only: true,
+    defaults: ["framework-module-defaults"],
+    // TODO: build against module API
+    platform_apis: true,
+    srcs: [
+        ":framework-connectivity-sources",
+    ],
+    aidl: {
+        include_dirs: [
+            // Include directories for parcelables that are part of the stable API, and need a
+            // one-line "parcelable X" .aidl declaration to be used in AIDL interfaces.
+            // TODO(b/180293679): remove these dependencies as they should not be necessary once
+            // the module builds against API (the parcelable declarations exist in framework.aidl)
+            "frameworks/base/core/java", // For framework parcelables
+            "frameworks/native/aidl/binder", // For PersistableBundle.aidl
+        ],
+    },
+    libs: [
+        "unsupportedappusage",
+    ],
+    permitted_packages: ["android.net", "com.android.connectivity.aidl"],
+}
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
new file mode 100644
index 0000000..31b8fc8
--- /dev/null
+++ b/packages/Connectivity/framework/api/current.txt
@@ -0,0 +1,470 @@
+// Signature format: 2.0
+package android.net {
+
+  public class CaptivePortal implements android.os.Parcelable {
+    method public int describeContents();
+    method public void ignoreNetwork();
+    method public void reportCaptivePortalDismissed();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR;
+  }
+
+  public class ConnectivityDiagnosticsManager {
+    method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
+    method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
+  }
+
+  public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
+    ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback();
+    method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
+    method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport);
+    method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
+  }
+
+  public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable {
+    ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
+    method public int describeContents();
+    method @NonNull public android.os.PersistableBundle getAdditionalInfo();
+    method @NonNull public android.net.LinkProperties getLinkProperties();
+    method @NonNull public android.net.Network getNetwork();
+    method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+    method public long getReportTimestamp();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR;
+    field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted";
+    field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded";
+    field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
+    field public static final int NETWORK_PROBE_DNS = 4; // 0x4
+    field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20
+    field public static final int NETWORK_PROBE_HTTP = 8; // 0x8
+    field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10
+    field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40
+    field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0
+    field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2
+    field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3
+    field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1
+  }
+
+  public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
+    ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
+    method public int describeContents();
+    method public int getDetectionMethod();
+    method @NonNull public android.net.LinkProperties getLinkProperties();
+    method @NonNull public android.net.Network getNetwork();
+    method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+    method public long getReportTimestamp();
+    method @NonNull public android.os.PersistableBundle getStallDetails();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR;
+    field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
+    field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
+    field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
+    field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis";
+    field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
+  }
+
+  public class ConnectivityManager {
+    method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
+    method public boolean bindProcessToNetwork(@Nullable android.net.Network);
+    method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork();
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo();
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo();
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks();
+    method @Deprecated public boolean getBackgroundDataSetting();
+    method @Nullable public android.net.Network getBoundNetworkForProcess();
+    method public int getConnectionOwnerUid(int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress);
+    method @Nullable public android.net.ProxyInfo getDefaultProxy();
+    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.LinkProperties getLinkProperties(@Nullable android.net.Network);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getMultipathPreference(@Nullable android.net.Network);
+    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkCapabilities getNetworkCapabilities(@Nullable android.net.Network);
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(int);
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(@Nullable android.net.Network);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getNetworkPreference();
+    method @Nullable public byte[] getNetworkWatchlistConfigHash();
+    method @Deprecated @Nullable public static android.net.Network getProcessDefaultNetwork();
+    method public int getRestrictBackgroundStatus();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered();
+    method public boolean isDefaultNetworkActive();
+    method @Deprecated public static boolean isNetworkTypeValid(int);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent);
+    method public void releaseNetworkRequest(@NonNull android.app.PendingIntent);
+    method public void removeDefaultNetworkActiveListener(@NonNull android.net.ConnectivityManager.OnNetworkActiveListener);
+    method @Deprecated public void reportBadNetwork(@Nullable android.net.Network);
+    method public void reportNetworkConnectivity(@Nullable android.net.Network, boolean);
+    method public boolean requestBandwidthUpdate(@NonNull android.net.Network);
+    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int);
+    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler, int);
+    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent);
+    method @Deprecated public void setNetworkPreference(int);
+    method @Deprecated public static boolean setProcessDefaultNetwork(@Nullable android.net.Network);
+    method public void unregisterNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
+    method public void unregisterNetworkCallback(@NonNull android.app.PendingIntent);
+    field @Deprecated public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+    field public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
+    field public static final String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED";
+    field @Deprecated public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
+    field @Deprecated public static final int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
+    field public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
+    field public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
+    field @Deprecated public static final String EXTRA_EXTRA_INFO = "extraInfo";
+    field @Deprecated public static final String EXTRA_IS_FAILOVER = "isFailover";
+    field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
+    field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo";
+    field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
+    field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType";
+    field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity";
+    field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
+    field public static final String EXTRA_REASON = "reason";
+    field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1
+    field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4
+    field public static final int MULTIPATH_PREFERENCE_RELIABILITY = 2; // 0x2
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+    field @Deprecated public static final int TYPE_BLUETOOTH = 7; // 0x7
+    field @Deprecated public static final int TYPE_DUMMY = 8; // 0x8
+    field @Deprecated public static final int TYPE_ETHERNET = 9; // 0x9
+    field @Deprecated public static final int TYPE_MOBILE = 0; // 0x0
+    field @Deprecated public static final int TYPE_MOBILE_DUN = 4; // 0x4
+    field @Deprecated public static final int TYPE_MOBILE_HIPRI = 5; // 0x5
+    field @Deprecated public static final int TYPE_MOBILE_MMS = 2; // 0x2
+    field @Deprecated public static final int TYPE_MOBILE_SUPL = 3; // 0x3
+    field @Deprecated public static final int TYPE_VPN = 17; // 0x11
+    field @Deprecated public static final int TYPE_WIFI = 1; // 0x1
+    field @Deprecated public static final int TYPE_WIMAX = 6; // 0x6
+  }
+
+  public static class ConnectivityManager.NetworkCallback {
+    ctor public ConnectivityManager.NetworkCallback();
+    method public void onAvailable(@NonNull android.net.Network);
+    method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean);
+    method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities);
+    method public void onLinkPropertiesChanged(@NonNull android.net.Network, @NonNull android.net.LinkProperties);
+    method public void onLosing(@NonNull android.net.Network, int);
+    method public void onLost(@NonNull android.net.Network);
+    method public void onUnavailable();
+  }
+
+  public static interface ConnectivityManager.OnNetworkActiveListener {
+    method public void onNetworkActive();
+  }
+
+  public class DhcpInfo implements android.os.Parcelable {
+    ctor public DhcpInfo();
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpInfo> CREATOR;
+    field public int dns1;
+    field public int dns2;
+    field public int gateway;
+    field public int ipAddress;
+    field public int leaseDuration;
+    field public int netmask;
+    field public int serverAddress;
+  }
+
+  public final class DnsResolver {
+    method @NonNull public static android.net.DnsResolver getInstance();
+    method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>);
+    method public void query(@Nullable android.net.Network, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>);
+    method public void rawQuery(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>);
+    method public void rawQuery(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>);
+    field public static final int CLASS_IN = 1; // 0x1
+    field public static final int ERROR_PARSE = 0; // 0x0
+    field public static final int ERROR_SYSTEM = 1; // 0x1
+    field public static final int FLAG_EMPTY = 0; // 0x0
+    field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4
+    field public static final int FLAG_NO_CACHE_STORE = 2; // 0x2
+    field public static final int FLAG_NO_RETRY = 1; // 0x1
+    field public static final int TYPE_A = 1; // 0x1
+    field public static final int TYPE_AAAA = 28; // 0x1c
+  }
+
+  public static interface DnsResolver.Callback<T> {
+    method public void onAnswer(@NonNull T, int);
+    method public void onError(@NonNull android.net.DnsResolver.DnsException);
+  }
+
+  public static class DnsResolver.DnsException extends java.lang.Exception {
+    field public final int code;
+  }
+
+  public class InetAddresses {
+    method public static boolean isNumericAddress(@NonNull String);
+    method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String);
+  }
+
+  public final class IpPrefix implements android.os.Parcelable {
+    method public boolean contains(@NonNull java.net.InetAddress);
+    method public int describeContents();
+    method @NonNull public java.net.InetAddress getAddress();
+    method @IntRange(from=0, to=128) public int getPrefixLength();
+    method @NonNull public byte[] getRawAddress();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
+  }
+
+  public class LinkAddress implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.net.InetAddress getAddress();
+    method public int getFlags();
+    method @IntRange(from=0, to=128) public int getPrefixLength();
+    method public int getScope();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkAddress> CREATOR;
+  }
+
+  public final class LinkProperties implements android.os.Parcelable {
+    ctor public LinkProperties();
+    method public boolean addRoute(@NonNull android.net.RouteInfo);
+    method public void clear();
+    method public int describeContents();
+    method @Nullable public java.net.Inet4Address getDhcpServerAddress();
+    method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
+    method @Nullable public String getDomains();
+    method @Nullable public android.net.ProxyInfo getHttpProxy();
+    method @Nullable public String getInterfaceName();
+    method @NonNull public java.util.List<android.net.LinkAddress> getLinkAddresses();
+    method public int getMtu();
+    method @Nullable public android.net.IpPrefix getNat64Prefix();
+    method @Nullable public String getPrivateDnsServerName();
+    method @NonNull public java.util.List<android.net.RouteInfo> getRoutes();
+    method public boolean isPrivateDnsActive();
+    method public boolean isWakeOnLanSupported();
+    method public void setDhcpServerAddress(@Nullable java.net.Inet4Address);
+    method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
+    method public void setDomains(@Nullable String);
+    method public void setHttpProxy(@Nullable android.net.ProxyInfo);
+    method public void setInterfaceName(@Nullable String);
+    method public void setLinkAddresses(@NonNull java.util.Collection<android.net.LinkAddress>);
+    method public void setMtu(int);
+    method public void setNat64Prefix(@Nullable android.net.IpPrefix);
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
+  }
+
+  public final class MacAddress implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public static android.net.MacAddress fromBytes(@NonNull byte[]);
+    method @NonNull public static android.net.MacAddress fromString(@NonNull String);
+    method public int getAddressType();
+    method @Nullable public java.net.Inet6Address getLinkLocalIpv6FromEui48Mac();
+    method public boolean isLocallyAssigned();
+    method public boolean matches(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress);
+    method @NonNull public byte[] toByteArray();
+    method @NonNull public String toOuiString();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.net.MacAddress BROADCAST_ADDRESS;
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR;
+    field public static final int TYPE_BROADCAST = 3; // 0x3
+    field public static final int TYPE_MULTICAST = 2; // 0x2
+    field public static final int TYPE_UNICAST = 1; // 0x1
+  }
+
+  public class Network implements android.os.Parcelable {
+    method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException;
+    method public void bindSocket(java.net.Socket) throws java.io.IOException;
+    method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException;
+    method public int describeContents();
+    method public static android.net.Network fromNetworkHandle(long);
+    method public java.net.InetAddress[] getAllByName(String) throws java.net.UnknownHostException;
+    method public java.net.InetAddress getByName(String) throws java.net.UnknownHostException;
+    method public long getNetworkHandle();
+    method public javax.net.SocketFactory getSocketFactory();
+    method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
+    method public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException;
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.Network> CREATOR;
+  }
+
+  public final class NetworkCapabilities implements android.os.Parcelable {
+    ctor public NetworkCapabilities();
+    ctor public NetworkCapabilities(android.net.NetworkCapabilities);
+    method public int describeContents();
+    method public int getLinkDownstreamBandwidthKbps();
+    method public int getLinkUpstreamBandwidthKbps();
+    method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
+    method public int getOwnerUid();
+    method public int getSignalStrength();
+    method @Nullable public android.net.TransportInfo getTransportInfo();
+    method public boolean hasCapability(int);
+    method public boolean hasTransport(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR;
+    field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11
+    field public static final int NET_CAPABILITY_CBS = 5; // 0x5
+    field public static final int NET_CAPABILITY_DUN = 2; // 0x2
+    field public static final int NET_CAPABILITY_EIMS = 10; // 0xa
+    field public static final int NET_CAPABILITY_ENTERPRISE = 29; // 0x1d
+    field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
+    field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
+    field public static final int NET_CAPABILITY_IA = 7; // 0x7
+    field public static final int NET_CAPABILITY_IMS = 4; // 0x4
+    field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
+    field public static final int NET_CAPABILITY_MCX = 23; // 0x17
+    field public static final int NET_CAPABILITY_MMS = 0; // 0x0
+    field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14
+    field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
+    field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
+    field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
+    field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15
+    field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
+    field public static final int NET_CAPABILITY_RCS = 8; // 0x8
+    field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
+    field public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; // 0x19
+    field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe
+    field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10
+    field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6
+    field public static final int NET_CAPABILITY_XCAP = 9; // 0x9
+    field public static final int SIGNAL_STRENGTH_UNSPECIFIED = -2147483648; // 0x80000000
+    field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2
+    field public static final int TRANSPORT_CELLULAR = 0; // 0x0
+    field public static final int TRANSPORT_ETHERNET = 3; // 0x3
+    field public static final int TRANSPORT_LOWPAN = 6; // 0x6
+    field public static final int TRANSPORT_VPN = 4; // 0x4
+    field public static final int TRANSPORT_WIFI = 1; // 0x1
+    field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
+  }
+
+  @Deprecated public class NetworkInfo implements android.os.Parcelable {
+    ctor @Deprecated public NetworkInfo(int, int, @Nullable String, @Nullable String);
+    method @Deprecated public int describeContents();
+    method @Deprecated @NonNull public android.net.NetworkInfo.DetailedState getDetailedState();
+    method @Deprecated public String getExtraInfo();
+    method @Deprecated public String getReason();
+    method @Deprecated public android.net.NetworkInfo.State getState();
+    method @Deprecated public int getSubtype();
+    method @Deprecated public String getSubtypeName();
+    method @Deprecated public int getType();
+    method @Deprecated public String getTypeName();
+    method @Deprecated public boolean isAvailable();
+    method @Deprecated public boolean isConnected();
+    method @Deprecated public boolean isConnectedOrConnecting();
+    method @Deprecated public boolean isFailover();
+    method @Deprecated public boolean isRoaming();
+    method @Deprecated public void setDetailedState(@NonNull android.net.NetworkInfo.DetailedState, @Nullable String, @Nullable String);
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR;
+  }
+
+  @Deprecated public enum NetworkInfo.DetailedState {
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState AUTHENTICATING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState BLOCKED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CAPTIVE_PORTAL_CHECK;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState FAILED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState IDLE;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SCANNING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SUSPENDED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK;
+  }
+
+  @Deprecated public enum NetworkInfo.State {
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State SUSPENDED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State UNKNOWN;
+  }
+
+  public class NetworkRequest implements android.os.Parcelable {
+    method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkCapabilities);
+    method public int describeContents();
+    method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
+    method public boolean hasCapability(int);
+    method public boolean hasTransport(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR;
+  }
+
+  public static class NetworkRequest.Builder {
+    ctor public NetworkRequest.Builder();
+    method public android.net.NetworkRequest.Builder addCapability(int);
+    method public android.net.NetworkRequest.Builder addTransportType(int);
+    method public android.net.NetworkRequest build();
+    method @NonNull public android.net.NetworkRequest.Builder clearCapabilities();
+    method public android.net.NetworkRequest.Builder removeCapability(int);
+    method public android.net.NetworkRequest.Builder removeTransportType(int);
+    method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
+    method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+  }
+
+  public final class Proxy {
+    ctor public Proxy();
+    method @Deprecated public static String getDefaultHost();
+    method @Deprecated public static int getDefaultPort();
+    method @Deprecated public static String getHost(android.content.Context);
+    method @Deprecated public static int getPort(android.content.Context);
+    field @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
+    field public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
+  }
+
+  public class ProxyInfo implements android.os.Parcelable {
+    ctor public ProxyInfo(@Nullable android.net.ProxyInfo);
+    method public static android.net.ProxyInfo buildDirectProxy(String, int);
+    method public static android.net.ProxyInfo buildDirectProxy(String, int, java.util.List<java.lang.String>);
+    method public static android.net.ProxyInfo buildPacProxy(android.net.Uri);
+    method @NonNull public static android.net.ProxyInfo buildPacProxy(@NonNull android.net.Uri, int);
+    method public int describeContents();
+    method public String[] getExclusionList();
+    method public String getHost();
+    method public android.net.Uri getPacFileUrl();
+    method public int getPort();
+    method public boolean isValid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.ProxyInfo> CREATOR;
+  }
+
+  public final class RouteInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.net.IpPrefix getDestination();
+    method @Nullable public java.net.InetAddress getGateway();
+    method @Nullable public String getInterface();
+    method public boolean hasGateway();
+    method public boolean isDefaultRoute();
+    method public boolean matches(java.net.InetAddress);
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.RouteInfo> CREATOR;
+  }
+
+  public abstract class SocketKeepalive implements java.lang.AutoCloseable {
+    method public final void close();
+    method public final void start(@IntRange(from=0xa, to=0xe10) int);
+    method public final void stop();
+    field public static final int ERROR_HARDWARE_ERROR = -31; // 0xffffffe1
+    field public static final int ERROR_INSUFFICIENT_RESOURCES = -32; // 0xffffffe0
+    field public static final int ERROR_INVALID_INTERVAL = -24; // 0xffffffe8
+    field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
+    field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
+    field public static final int ERROR_INVALID_NETWORK = -20; // 0xffffffec
+    field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
+    field public static final int ERROR_INVALID_SOCKET = -25; // 0xffffffe7
+    field public static final int ERROR_SOCKET_NOT_IDLE = -26; // 0xffffffe6
+    field public static final int ERROR_UNSUPPORTED = -30; // 0xffffffe2
+  }
+
+  public static class SocketKeepalive.Callback {
+    ctor public SocketKeepalive.Callback();
+    method public void onDataReceived();
+    method public void onError(int);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public interface TransportInfo {
+  }
+
+}
+
diff --git a/packages/Connectivity/framework/api/lint-baseline.txt b/packages/Connectivity/framework/api/lint-baseline.txt
new file mode 100644
index 0000000..2f4004a
--- /dev/null
+++ b/packages/Connectivity/framework/api/lint-baseline.txt
@@ -0,0 +1,4 @@
+// Baseline format: 1.0
+VisiblySynchronized: android.net.NetworkInfo#toString():
+    Internal locks must not be exposed (synchronizing on this or class is still
+    externally observable): method android.net.NetworkInfo.toString()
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
new file mode 100644
index 0000000..3af855e
--- /dev/null
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -0,0 +1,66 @@
+// Signature format: 2.0
+package android.net {
+
+  public final class ConnectivityFrameworkInitializer {
+    method public static void registerServiceWrappers();
+  }
+
+  public class ConnectivityManager {
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
+  }
+
+  public final class NetworkAgentConfig implements android.os.Parcelable {
+    method @Nullable public String getSubscriberId();
+  }
+
+  public static final class NetworkAgentConfig.Builder {
+    method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
+  }
+
+  public final class NetworkCapabilities implements android.os.Parcelable {
+    field public static final int TRANSPORT_TEST = 7; // 0x7
+  }
+
+  public final class Proxy {
+    method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
+  }
+
+  public final class TcpRepairWindow {
+    ctor public TcpRepairWindow(int, int, int, int, int, int);
+    field public final int maxWindow;
+    field public final int rcvWnd;
+    field public final int rcvWndScale;
+    field public final int rcvWup;
+    field public final int sndWl1;
+    field public final int sndWnd;
+  }
+
+  public final class TestNetworkInterface implements android.os.Parcelable {
+    ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String);
+    method public int describeContents();
+    method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor();
+    method @NonNull public String getInterfaceName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR;
+  }
+
+  public class TestNetworkManager {
+    method @NonNull public android.net.TestNetworkInterface createTapInterface();
+    method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>);
+    method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
+    method public void teardownTestNetwork(@NonNull android.net.Network);
+    field public static final String TEST_TAP_PREFIX = "testtap";
+  }
+
+  public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
+    ctor public VpnTransportInfo(int);
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
+    field public final int type;
+  }
+
+}
+
diff --git a/packages/Connectivity/framework/api/module-lib-removed.txt b/packages/Connectivity/framework/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Connectivity/framework/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Connectivity/framework/api/removed.txt b/packages/Connectivity/framework/api/removed.txt
new file mode 100644
index 0000000..303a1e61
--- /dev/null
+++ b/packages/Connectivity/framework/api/removed.txt
@@ -0,0 +1,11 @@
+// Signature format: 2.0
+package android.net {
+
+  public class ConnectivityManager {
+    method @Deprecated public boolean requestRouteToHost(int, int);
+    method @Deprecated public int startUsingNetworkFeature(int, String);
+    method @Deprecated public int stopUsingNetworkFeature(int, String);
+  }
+
+}
+
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
new file mode 100644
index 0000000..41ebc57
--- /dev/null
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -0,0 +1,407 @@
+// Signature format: 2.0
+package android.net {
+
+  public class CaptivePortal implements android.os.Parcelable {
+    method public void logEvent(int, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork();
+    method public void useNetwork();
+    field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
+    field public static final int APP_RETURN_DISMISSED = 0; // 0x0
+    field public static final int APP_RETURN_UNWANTED = 1; // 0x1
+    field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
+  }
+
+  public final class CaptivePortalData implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getByteLimit();
+    method public long getExpiryTimeMillis();
+    method public long getRefreshTimeMillis();
+    method @Nullable public android.net.Uri getUserPortalUrl();
+    method public int getUserPortalUrlSource();
+    method @Nullable public String getVenueFriendlyName();
+    method @Nullable public android.net.Uri getVenueInfoUrl();
+    method public int getVenueInfoUrlSource();
+    method public boolean isCaptive();
+    method public boolean isSessionExtendable();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0
+    field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR;
+  }
+
+  public static class CaptivePortalData.Builder {
+    ctor public CaptivePortalData.Builder();
+    ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData);
+    method @NonNull public android.net.CaptivePortalData build();
+    method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean);
+    method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
+    method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
+    method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int);
+    method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String);
+    method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
+    method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int);
+  }
+
+  public class ConnectivityManager {
+    method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+    method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
+    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
+    method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
+    method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
+    method public void unregisterQosCallback(@NonNull android.net.QosCallback);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
+    field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
+    field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
+    field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+    field public static final int TETHERING_USB = 1; // 0x1
+    field public static final int TETHERING_WIFI = 0; // 0x0
+    field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd
+    field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+    field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+    field public static final int TYPE_NONE = -1; // 0xffffffff
+    field @Deprecated public static final int TYPE_PROXY = 16; // 0x10
+    field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
+  }
+
+  public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener {
+    method public void onComplete();
+  }
+
+  @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback {
+    ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback();
+    method @Deprecated public void onTetheringFailed();
+    method @Deprecated public void onTetheringStarted();
+  }
+
+  @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener {
+    method @Deprecated public void onTetheringEntitlementResult(int);
+  }
+
+  @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback {
+    ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback();
+    method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network);
+  }
+
+  public final class InvalidPacketException extends java.lang.Exception {
+    ctor public InvalidPacketException(int);
+    method public int getError();
+    field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
+    field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
+    field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
+  }
+
+  public final class IpConfiguration implements android.os.Parcelable {
+    ctor public IpConfiguration();
+    ctor public IpConfiguration(@NonNull android.net.IpConfiguration);
+    method public int describeContents();
+    method @Nullable public android.net.ProxyInfo getHttpProxy();
+    method @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment();
+    method @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings();
+    method @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration();
+    method public void setHttpProxy(@Nullable android.net.ProxyInfo);
+    method public void setIpAssignment(@NonNull android.net.IpConfiguration.IpAssignment);
+    method public void setProxySettings(@NonNull android.net.IpConfiguration.ProxySettings);
+    method public void setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.IpConfiguration> CREATOR;
+  }
+
+  public enum IpConfiguration.IpAssignment {
+    enum_constant public static final android.net.IpConfiguration.IpAssignment DHCP;
+    enum_constant public static final android.net.IpConfiguration.IpAssignment STATIC;
+    enum_constant public static final android.net.IpConfiguration.IpAssignment UNASSIGNED;
+  }
+
+  public enum IpConfiguration.ProxySettings {
+    enum_constant public static final android.net.IpConfiguration.ProxySettings NONE;
+    enum_constant public static final android.net.IpConfiguration.ProxySettings PAC;
+    enum_constant public static final android.net.IpConfiguration.ProxySettings STATIC;
+    enum_constant public static final android.net.IpConfiguration.ProxySettings UNASSIGNED;
+  }
+
+  public final class IpPrefix implements android.os.Parcelable {
+    ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
+    ctor public IpPrefix(@NonNull String);
+  }
+
+  public class KeepalivePacketData {
+    ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull byte[]) throws android.net.InvalidPacketException;
+    method @NonNull public java.net.InetAddress getDstAddress();
+    method public int getDstPort();
+    method @NonNull public byte[] getPacket();
+    method @NonNull public java.net.InetAddress getSrcAddress();
+    method public int getSrcPort();
+  }
+
+  public class LinkAddress implements android.os.Parcelable {
+    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
+    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long);
+    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
+    ctor public LinkAddress(@NonNull String);
+    ctor public LinkAddress(@NonNull String, int, int);
+    method public long getDeprecationTime();
+    method public long getExpirationTime();
+    method public boolean isGlobalPreferred();
+    method public boolean isIpv4();
+    method public boolean isIpv6();
+    method public boolean isSameAddressAs(@Nullable android.net.LinkAddress);
+    field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL
+    field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL
+  }
+
+  public final class LinkProperties implements android.os.Parcelable {
+    ctor public LinkProperties(@Nullable android.net.LinkProperties);
+    ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean);
+    method public boolean addDnsServer(@NonNull java.net.InetAddress);
+    method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
+    method public boolean addPcscfServer(@NonNull java.net.InetAddress);
+    method @NonNull public java.util.List<java.net.InetAddress> getAddresses();
+    method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames();
+    method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses();
+    method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes();
+    method @Nullable public android.net.Uri getCaptivePortalApiUrl();
+    method @Nullable public android.net.CaptivePortalData getCaptivePortalData();
+    method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
+    method @Nullable public String getTcpBufferSizes();
+    method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
+    method public boolean hasGlobalIpv6Address();
+    method public boolean hasIpv4Address();
+    method public boolean hasIpv4DefaultRoute();
+    method public boolean hasIpv4DnsServer();
+    method public boolean hasIpv6DefaultRoute();
+    method public boolean hasIpv6DnsServer();
+    method public boolean isIpv4Provisioned();
+    method public boolean isIpv6Provisioned();
+    method public boolean isProvisioned();
+    method public boolean isReachable(@NonNull java.net.InetAddress);
+    method public boolean removeDnsServer(@NonNull java.net.InetAddress);
+    method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
+    method public boolean removeRoute(@NonNull android.net.RouteInfo);
+    method public void setCaptivePortalApiUrl(@Nullable android.net.Uri);
+    method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData);
+    method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
+    method public void setPrivateDnsServerName(@Nullable String);
+    method public void setTcpBufferSizes(@Nullable String);
+    method public void setUsePrivateDns(boolean);
+    method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
+  }
+
+  public final class NattKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
+    ctor public NattKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException;
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NattKeepalivePacketData> CREATOR;
+  }
+
+  public class Network implements android.os.Parcelable {
+    ctor public Network(@NonNull android.net.Network);
+    method public int getNetId();
+    method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
+  }
+
+  public abstract class NetworkAgent {
+    ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
+    method @Nullable public android.net.Network getNetwork();
+    method public void markConnected();
+    method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
+    method public void onAutomaticReconnectDisabled();
+    method public void onNetworkUnwanted();
+    method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
+    method public void onQosCallbackUnregistered(int);
+    method public void onRemoveKeepalivePacketFilter(int);
+    method public void onSaveAcceptUnvalidated(boolean);
+    method public void onSignalStrengthThresholdsUpdated(@NonNull int[]);
+    method public void onStartSocketKeepalive(int, @NonNull java.time.Duration, @NonNull android.net.KeepalivePacketData);
+    method public void onStopSocketKeepalive(int);
+    method public void onValidationStatus(int, @Nullable android.net.Uri);
+    method @NonNull public android.net.Network register();
+    method public final void sendLinkProperties(@NonNull android.net.LinkProperties);
+    method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
+    method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
+    method public final void sendQosCallbackError(int, int);
+    method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes);
+    method public final void sendQosSessionLost(int, int);
+    method public final void sendSocketKeepaliveEvent(int, int);
+    method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
+    method public void unregister();
+    field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
+    field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
+  }
+
+  public final class NetworkAgentConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getLegacyType();
+    method @NonNull public String getLegacyTypeName();
+    method public boolean isExplicitlySelected();
+    method public boolean isPartialConnectivityAcceptable();
+    method public boolean isUnvalidatedConnectivityAcceptable();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
+  }
+
+  public static final class NetworkAgentConfig.Builder {
+    ctor public NetworkAgentConfig.Builder();
+    method @NonNull public android.net.NetworkAgentConfig build();
+    method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean);
+    method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int);
+    method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String);
+    method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean);
+    method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean);
+  }
+
+  public final class NetworkCapabilities implements android.os.Parcelable {
+    ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean);
+    method @NonNull public int[] getAdministratorUids();
+    method @Nullable public String getSsid();
+    method @NonNull public int[] getTransportTypes();
+    method public boolean isPrivateDnsBroken();
+    method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+    field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c
+    field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
+    field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
+    field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
+    field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b
+  }
+
+  public static final class NetworkCapabilities.Builder {
+    ctor public NetworkCapabilities.Builder();
+    ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
+    method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
+    method @NonNull public android.net.NetworkCapabilities build();
+    method @NonNull public android.net.NetworkCapabilities.Builder clearAll();
+    method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
+    method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+    method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
+  }
+
+  public class NetworkProvider {
+    ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
+    method public int getProviderId();
+    method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest);
+    method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int);
+    field public static final int ID_NONE = -1; // 0xffffffff
+  }
+
+  public class NetworkRequest implements android.os.Parcelable {
+    method @Nullable public String getRequestorPackageName();
+    method public int getRequestorUid();
+  }
+
+  public static class NetworkRequest.Builder {
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
+  }
+
+  public final class RouteInfo implements android.os.Parcelable {
+    ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int);
+    ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int);
+    method public int getMtu();
+    method public int getType();
+    field public static final int RTN_THROW = 9; // 0x9
+    field public static final int RTN_UNICAST = 1; // 0x1
+    field public static final int RTN_UNREACHABLE = 7; // 0x7
+  }
+
+  public abstract class SocketKeepalive implements java.lang.AutoCloseable {
+    field public static final int SUCCESS = 0; // 0x0
+  }
+
+  public final class StaticIpConfiguration implements android.os.Parcelable {
+    ctor public StaticIpConfiguration();
+    ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
+    method public void addDnsServer(@NonNull java.net.InetAddress);
+    method public void clear();
+    method public int describeContents();
+    method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
+    method @Nullable public String getDomains();
+    method @Nullable public java.net.InetAddress getGateway();
+    method @Nullable public android.net.LinkAddress getIpAddress();
+    method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR;
+  }
+
+  public static final class StaticIpConfiguration.Builder {
+    ctor public StaticIpConfiguration.Builder();
+    method @NonNull public android.net.StaticIpConfiguration build();
+    method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>);
+    method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String);
+    method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress);
+    method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
+  }
+
+  public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
+    ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException;
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR;
+    field public final int ipTos;
+    field public final int ipTtl;
+    field public final int tcpAck;
+    field public final int tcpSeq;
+    field public final int tcpWindow;
+    field public final int tcpWindowScale;
+  }
+
+  public interface TransportInfo {
+    method public default boolean hasLocationSensitiveFields();
+    method @NonNull public default android.net.TransportInfo makeCopy(boolean);
+  }
+
+}
+
+package android.net.apf {
+
+  public final class ApfCapabilities implements android.os.Parcelable {
+    ctor public ApfCapabilities(int, int, int);
+    method public int describeContents();
+    method public static boolean getApfDrop8023Frames();
+    method @NonNull public static int[] getApfEtherTypeBlackList();
+    method public boolean hasDataAccess();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR;
+    field public final int apfPacketFormat;
+    field public final int apfVersionSupported;
+    field public final int maximumApfProgramSize;
+  }
+
+}
+
+package android.net.util {
+
+  public final class SocketUtils {
+    method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException;
+    method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException;
+    method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int);
+    method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int);
+    method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]);
+    method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]);
+  }
+
+}
+
diff --git a/packages/Connectivity/framework/api/system-lint-baseline.txt b/packages/Connectivity/framework/api/system-lint-baseline.txt
new file mode 100644
index 0000000..9a97707
--- /dev/null
+++ b/packages/Connectivity/framework/api/system-lint-baseline.txt
@@ -0,0 +1 @@
+// Baseline format: 1.0
diff --git a/packages/Connectivity/framework/api/system-removed.txt b/packages/Connectivity/framework/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Connectivity/framework/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 26d14cb..cd76f40 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -205,6 +205,7 @@
             NET_CAPABILITY_OEM_PRIVATE,
             NET_CAPABILITY_VEHICLE_INTERNAL,
             NET_CAPABILITY_NOT_VCN_MANAGED,
+            NET_CAPABILITY_ENTERPRISE,
     })
     public @interface NetCapability { }
 
@@ -415,8 +416,17 @@
     @SystemApi
     public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
 
+    /**
+     * Indicates that this network is intended for enterprise use.
+     * <p>
+     * 5G URSP rules may indicate that all data should use a connection dedicated for enterprise
+     * use. If the enterprise capability is requested, all enterprise traffic will be routed over
+     * the connection with this capability.
+     */
+    public static final int NET_CAPABILITY_ENTERPRISE = 29;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_ENTERPRISE;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -474,7 +484,8 @@
             | (1 << NET_CAPABILITY_MCX)
             | (1 << NET_CAPABILITY_RCS)
             | (1 << NET_CAPABILITY_VEHICLE_INTERNAL)
-            | (1 << NET_CAPABILITY_XCAP);
+            | (1 << NET_CAPABILITY_XCAP)
+            | (1 << NET_CAPABILITY_ENTERPRISE);
 
     /**
      * Capabilities that force network to be restricted.
@@ -2028,8 +2039,9 @@
             case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY";
             case NET_CAPABILITY_TEMPORARILY_NOT_METERED:    return "TEMPORARILY_NOT_METERED";
             case NET_CAPABILITY_OEM_PRIVATE:          return "OEM_PRIVATE";
-            case NET_CAPABILITY_VEHICLE_INTERNAL:     return "NET_CAPABILITY_VEHICLE_INTERNAL";
+            case NET_CAPABILITY_VEHICLE_INTERNAL:     return "VEHICLE_INTERNAL";
             case NET_CAPABILITY_NOT_VCN_MANAGED:      return "NOT_VCN_MANAGED";
+            case NET_CAPABILITY_ENTERPRISE:           return "ENTERPRISE";
             default:                                  return Integer.toString(capability);
         }
     }
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
index 8b4db92..2946db3 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
@@ -27,9 +27,11 @@
     private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS;
     private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag";
     private static final String KEY_NON_INCREMENTAL_ONLY = "non_incremental_only";
+    private static final String KEY_IS_DEVICE_TRANSFER = "is_device_transfer";
 
     private boolean mFakeEncryptionFlag;
     private boolean mIsNonIncrementalOnly;
+    private boolean mIsDeviceTransfer;
 
     public LocalTransportParameters(Handler handler, ContentResolver resolver) {
         super(handler, resolver, Settings.Secure.getUriFor(SETTING));
@@ -43,6 +45,10 @@
         return mIsNonIncrementalOnly;
     }
 
+    boolean isDeviceTransfer() {
+        return mIsDeviceTransfer;
+    }
+
     public String getSettingValue(ContentResolver resolver) {
         return Settings.Secure.getString(resolver, SETTING);
     }
@@ -50,5 +56,6 @@
     public void update(KeyValueListParser parser) {
         mFakeEncryptionFlag = parser.getBoolean(KEY_FAKE_ENCRYPTION_FLAG, false);
         mIsNonIncrementalOnly = parser.getBoolean(KEY_NON_INCREMENTAL_ONLY, false);
+        mIsDeviceTransfer = parser.getBoolean(KEY_IS_DEVICE_TRANSFER, false);
     }
 }
diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp
index 095975a..82e837b 100644
--- a/packages/SettingsLib/BannerMessagePreference/Android.bp
+++ b/packages/SettingsLib/BannerMessagePreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_library {
     name: "SettingsLibBannerMessagePreference",
 
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
index c23ff05..ed49bf4 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_library {
     name: "SettingsLibCollapsingToolbarBaseActivity",
 
diff --git a/packages/SettingsLib/EmergencyNumber/Android.bp b/packages/SettingsLib/EmergencyNumber/Android.bp
index 3c41f78..25b4905 100644
--- a/packages/SettingsLib/EmergencyNumber/Android.bp
+++ b/packages/SettingsLib/EmergencyNumber/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_library {
     name: "SettingsLibEmergencyNumber",
 
diff --git a/packages/SettingsLib/FooterPreference/Android.bp b/packages/SettingsLib/FooterPreference/Android.bp
index 1af967f..11f39e7 100644
--- a/packages/SettingsLib/FooterPreference/Android.bp
+++ b/packages/SettingsLib/FooterPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_library {
     name: "SettingsLibFooterPreference",
 
diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp
index 1dc18f5..1feec21 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_library {
     name: "SettingsLibMainSwitchPreference",
 
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
index 52779bc..85c01c5 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml
@@ -19,6 +19,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
+    android:background="?android:attr/colorBackground"
     android:orientation="vertical">
 
     <LinearLayout
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 74b6578..1c9298e 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -17,6 +17,7 @@
 package com.android.settingslib.widget;
 
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
@@ -29,6 +30,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.VisibleForTesting;
+import androidx.core.content.res.TypedArrayUtils;
 
 import com.android.settingslib.RestrictedLockUtils;
 
@@ -88,6 +90,17 @@
         });
 
         setChecked(mSwitch.isChecked());
+
+        if (attrs != null) {
+            final TypedArray a = context.obtainStyledAttributes(attrs,
+                    androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/,
+                    0 /*defStyleRes*/);
+            final CharSequence title = TypedArrayUtils.getText(a,
+                    androidx.preference.R.styleable.Preference_title,
+                    androidx.preference.R.styleable.Preference_android_title);
+            setTitle(title);
+            a.recycle();
+        }
     }
 
     @Override
@@ -126,7 +139,7 @@
     /**
      * Set the title text
      */
-    public void setTitle(String text) {
+    public void setTitle(CharSequence text) {
         if (mTextView != null) {
             mTextView.setText(text);
         }
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
index 274bf8d..35afec3 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.text.TextUtils;
 import android.util.AttributeSet;
 
 import androidx.core.content.res.TypedArrayUtils;
@@ -40,7 +39,7 @@
     private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>();
 
     private MainSwitchBar mMainSwitchBar;
-    private String mTitle;
+    private CharSequence mTitle;
 
     private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin;
 
@@ -81,24 +80,28 @@
         setLayoutResource(R.layout.main_switch_layout);
 
         if (attrs != null) {
-            TypedArray a = context.obtainStyledAttributes(attrs,
+            final TypedArray a = context.obtainStyledAttributes(attrs,
                     androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/,
                     0 /*defStyleRes*/);
             final CharSequence title = TypedArrayUtils.getText(a,
                     androidx.preference.R.styleable.Preference_title,
                     androidx.preference.R.styleable.Preference_android_title);
-            if (!TextUtils.isEmpty(title)) {
-                setTitle(title.toString());
-            }
+            setTitle(title);
             a.recycle();
         }
     }
 
-    /**
-     * Set the preference title text
-     */
-    public void setTitle(String text) {
-        mTitle = text;
+    @Override
+    public void setChecked(boolean checked) {
+        super.setChecked(checked);
+        if (mMainSwitchBar != null) {
+            mMainSwitchBar.setChecked(checked);
+        }
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        mTitle = title;
         if (mMainSwitchBar != null) {
             mMainSwitchBar.setTitle(mTitle);
         }
diff --git a/packages/SettingsLib/TopIntroPreference/Android.bp b/packages/SettingsLib/TopIntroPreference/Android.bp
index 03becbd..9577281 100644
--- a/packages/SettingsLib/TopIntroPreference/Android.bp
+++ b/packages/SettingsLib/TopIntroPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_library {
     name: "SettingsLibTopIntroPreference",
 
diff --git a/packages/SettingsLib/UsageProgressBarPreference/Android.bp b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
index 3331550..ad6e7ab 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/Android.bp
+++ b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_library {
     name: "SettingsLibUsageProgressBarPreference",
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 32419f4..1f3e0e9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -205,7 +205,6 @@
 
     void dispatchDeviceListUpdate() {
         final List<MediaDevice> mediaDevices = new ArrayList<>(mMediaDevices);
-        Collections.sort(mediaDevices, COMPARATOR);
         for (DeviceCallback callback : getCallbacks()) {
             callback.onDeviceListUpdate(mediaDevices);
         }
@@ -472,6 +471,7 @@
             synchronized (mMediaDevicesLock) {
                 mMediaDevices.clear();
                 mMediaDevices.addAll(devices);
+                Collections.sort(devices, COMPARATOR);
                 // Add disconnected bluetooth devices only when phone output device is available.
                 for (MediaDevice device : devices) {
                     final int type = device.getDeviceType();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
index 647fd2a..552fa11 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
@@ -39,8 +39,7 @@
     /**
      * A string extra specifying a media package name.
      */
-    public static final String EXTRA_PACKAGE_NAME =
-            "com.android.settings.panel.extra.PACKAGE_NAME";
+    public static final String EXTRA_PACKAGE_NAME = "package_name";
 
     /**
      * An intent action to launch media output dialog.
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 2594840..71e0910 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -84,8 +84,6 @@
     <uses-permission android:name="android.permission.READ_INPUT_STATE" />
     <uses-permission android:name="android.permission.SET_ORIENTATION" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
-    <!--  TODO(b/152310230): remove once APIs are confirmed to be sufficient -->
-    <uses-permission android:name="com.android.permission.USE_INSTALLER_V2" />
     <uses-permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS" />
     <uses-permission android:name="android.permission.MOVE_PACKAGE" />
     <uses-permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" />
@@ -125,6 +123,8 @@
     <uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" />
     <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
     <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
+    <uses-permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" />
+    <uses-permission android:name="android.permission.CLEAR_FREEZE_PERIOD" />
     <uses-permission android:name="android.permission.QUERY_USERS" />
     <uses-permission android:name="android.permission.MODIFY_QUIET_MODE" />
     <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/>
@@ -398,6 +398,7 @@
 
     <!-- Permission required for CTS to test sensor privacy behavior -->
     <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
+    <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" />
 
     <!-- Permission needed for CTS test - CallLogTest -->
     <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index fbe58c5..044216e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -87,6 +87,7 @@
     <uses-permission android:name="android.permission.MASTER_CLEAR" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
+    <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" />
 
     <!-- ActivityManager -->
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
index 71cdaf5..7986809 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -24,7 +24,7 @@
     <include
         style="@style/BouncerSecurityContainer"
         layout="@layout/keyguard_host_view"
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
 </FrameLayout>
 
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index c75ee51..04e645b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -41,14 +41,13 @@
         android:layout_gravity="center">
         <com.android.keyguard.KeyguardSecurityViewFlipper
             android:id="@+id/view_flipper"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
             android:clipChildren="false"
             android:clipToPadding="false"
             android:paddingTop="@dimen/keyguard_security_view_top_margin"
             android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
             android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
-            android:layout_gravity="center"
             android:gravity="center">
         </com.android.keyguard.KeyguardSecurityViewFlipper>
     </com.android.keyguard.KeyguardSecurityContainer>
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index 6176f7c..8d9d6ee 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,5 +22,4 @@
 
     <!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
     <bool name="config_disableMenuKeyInLockScreen">false</bool>
-    <bool name="can_use_one_handed_bouncer">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/drawable/horizontal_ellipsis.xml b/packages/SystemUI/res/drawable/horizontal_ellipsis.xml
new file mode 100644
index 0000000..1800857
--- /dev/null
+++ b/packages/SystemUI/res/drawable/horizontal_ellipsis.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="?android:attr/colorBackgroundFloating" >
+
+    <path
+        android:pathData="M6 10c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm12 0c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm-6 0c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2z"
+        android:fillColor="@android:color/white" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
index 9f66581..96e0193 100644
--- a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
+++ b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
@@ -36,8 +36,4 @@
         </vector>
     </item>
 
-    <item>
-        <ripple android:color="@color/GM2_grey_600"></ripple>
-    </item>
-
 </layer-list>
diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
index 659b020..368c8df 100644
--- a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
+++ b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
@@ -50,28 +50,7 @@
                     android:strokeWidth="1"
                     android:pathData="M 12.5 13.92 L 15.59 17 L 17 15.59 L 13.91 12.5 L 16.5 12.5 L 16.5 10.5 L 10.5 10.5 L 10.5 16.5 L 12.5 16.5 Z" />
             </group>
-            <group
-                android:translateX="22.000000"
-                android:translateY="2.000000">
-                <group
-                    android:translateX="3.000000"
-                    android:translateY="3.000000">
-                    <group
-                        android:translateX="-3.000000"
-                        android:translateY="-3.000000">
-                        <path
-                            android:fillColor="#80868B"
-                            android:fillType="evenOdd"
-                            android:strokeWidth="1"
-                            android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
-                    </group>
-                </group>
-            </group>
         </vector>
     </item>
 
-    <item>
-        <ripple android:color="@color/GM2_grey_600"></ripple>
-    </item>
-
 </layer-list>
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/packages/SystemUI/res/drawable/volume_drawer_bg.xml
similarity index 66%
rename from packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
rename to packages/SystemUI/res/drawable/volume_drawer_bg.xml
index e09bf7e..f0e2292 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
+++ b/packages/SystemUI/res/drawable/volume_drawer_bg.xml
@@ -14,7 +14,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<resources>
-    <bool name="can_use_one_handed_bouncer">true</bool>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+            android:paddingMode="stack" >
+    <size android:width="@dimen/volume_ringer_drawer_item_size" />
+    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
similarity index 63%
copy from packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
copy to packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
index e09bf7e..5e7cb12 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
+++ b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
@@ -14,7 +14,11 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<resources>
-    <bool name="can_use_one_handed_bouncer">true</bool>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+            android:paddingMode="stack" >
+    <size
+        android:height="@dimen/volume_ringer_drawer_item_size"
+        android:width="@dimen/volume_ringer_drawer_item_size" />
+    <solid android:color="?android:attr/colorAccent" />
+    <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar.xml b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
new file mode 100644
index 0000000..b0e0ed5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
@@ -0,0 +1,56 @@
+<?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.
+  -->
+
+<!-- SeekBar drawable for volume rows. This contains a background layer (with a solid round rect,
+     and a bottom-aligned icon) and a progress layer (with an accent-colored round rect and icon)
+     that moves up and down with the progress value. -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+            android:paddingMode="stack" >
+    <item android:id="@android:id/background"
+        android:gravity="center_vertical|fill_horizontal">
+        <layer-list>
+            <item android:id="@+id/volume_seekbar_background_solid">
+                <shape>
+                    <size android:height="@dimen/volume_dialog_panel_width" />
+                    <solid android:color="?android:attr/colorBackgroundFloating" />
+                    <corners android:radius="@dimen/volume_dialog_panel_width_half" />
+                </shape>
+            </item>
+            <item
+                android:id="@+id/volume_seekbar_background_icon"
+                android:gravity="center_vertical|left"
+                android:height="@dimen/rounded_slider_icon_size"
+                android:width="@dimen/rounded_slider_icon_size"
+                android:left="@dimen/rounded_slider_icon_inset">
+                <rotate
+                    android:fromDegrees="-270"
+                    android:toDegrees="-270">
+                    <!-- A placeholder drawable is required here - it'll be replaced in code. -->
+                    <com.android.systemui.util.AlphaTintDrawableWrapper
+                        android:drawable="@drawable/ic_volume_media"
+                        android:tint="?android:attr/colorAccent" />
+                </rotate>
+            </item>
+        </layer-list>
+    </item>
+    <item android:id="@android:id/progress"
+          android:gravity="center_vertical|fill_horizontal">
+            <com.android.systemui.util.RoundedCornerProgressDrawable
+                android:drawable="@drawable/volume_row_seekbar_progress"
+            />
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml b/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml
new file mode 100644
index 0000000..ef20236
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml
@@ -0,0 +1,44 @@
+<?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.
+  -->
+
+<!-- Progress drawable for volume row SeekBars. This is the accent-colored round rect that moves up
+     and down as the progress value changes. -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="true">
+    <item android:id="@+id/volume_seekbar_progress_solid">
+        <shape>
+            <size android:height="@dimen/volume_dialog_panel_width" />
+            <solid android:color="?android:attr/colorAccent" />
+            <corners android:radius="@dimen/volume_dialog_panel_width_half"/>
+        </shape>
+    </item>
+    <item
+        android:id="@+id/volume_seekbar_progress_icon"
+        android:gravity="center_vertical|right"
+        android:height="@dimen/rounded_slider_icon_size"
+        android:width="@dimen/rounded_slider_icon_size"
+        android:right="@dimen/rounded_slider_icon_inset">
+        <rotate
+            android:fromDegrees="-270"
+            android:toDegrees="-270">
+            <!-- A placeholder drawable is required here - it'll be replaced in code. -->
+            <com.android.systemui.util.AlphaTintDrawableWrapper
+                android:drawable="@drawable/ic_volume_media"
+                android:tint="?android:attr/colorBackgroundFloating" />
+        </rotate>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index c420117..237dc02 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -32,105 +32,124 @@
         android:gravity="right"
         android:layout_gravity="right"
         android:background="@android:color/transparent"
-        android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_right"
-        android:paddingTop="@dimen/volume_dialog_panel_transparent_padding"
-        android:paddingBottom="@dimen/volume_dialog_panel_transparent_padding"
+        android:paddingRight="@dimen/volume_dialog_stream_padding"
         android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding"
         android:clipToPadding="false">
 
-        <FrameLayout
-            android:id="@+id/ringer"
-            android:layout_width="@dimen/volume_dialog_ringer_size"
-            android:layout_height="@dimen/volume_dialog_ringer_size"
-            android:layout_marginBottom="@dimen/volume_dialog_spacer"
-            android:gravity="right"
-            android:layout_gravity="right"
-            android:translationZ="@dimen/volume_dialog_elevation"
-            android:clipToPadding="false"
-            android:background="@drawable/rounded_bg_full">
-            <com.android.keyguard.AlphaOptimizedImageButton
-                android:id="@+id/ringer_icon"
-                style="@style/VolumeButtons"
-                android:background="@drawable/rounded_ripple"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:scaleType="fitCenter"
-                android:padding="@dimen/volume_dialog_ringer_icon_padding"
-                android:tint="@color/accent_tint_color_selector"
-                android:layout_gravity="center"
-                android:soundEffectsEnabled="false" />
-
-            <include layout="@layout/volume_dnd_icon"
-                     android:layout_width="match_parent"
-                     android:layout_height="wrap_content"
-                     android:layout_marginRight="@dimen/volume_dialog_stream_padding"
-                     android:layout_marginTop="6dp"/>
-        </FrameLayout>
-
+        <!--
+            Container for a) the ringer drawer and the caption button next to b) the volume rows.
+        -->
         <LinearLayout
-            android:id="@+id/main"
             android:layout_width="wrap_content"
-            android:minWidth="@dimen/volume_dialog_panel_width"
             android:layout_height="wrap_content"
-            android:layout_marginTop="68dp"
-            android:gravity="right"
-            android:layout_gravity="right"
-            android:orientation="vertical"
-            android:translationZ="@dimen/volume_dialog_elevation"
+            android:orientation="horizontal"
             android:clipChildren="false"
-            android:clipToPadding="false"
-            android:background="@drawable/rounded_bg_full" >
-            <LinearLayout
-                android:id="@+id/volume_dialog_rows"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:minWidth="@dimen/volume_dialog_panel_width"
-                android:gravity="center"
-                android:orientation="horizontal"
-                android:paddingRight="@dimen/volume_dialog_stream_padding"
-                android:paddingLeft="@dimen/volume_dialog_stream_padding">
-                <!-- volume rows added and removed here! :-) -->
-            </LinearLayout>
-            <FrameLayout
-                android:id="@+id/settings_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:background="@drawable/rounded_bg_bottom_background">
-                <com.android.keyguard.AlphaOptimizedImageButton
-                    android:id="@+id/settings"
-                    android:src="@drawable/ic_tune_black_16dp"
-                    android:layout_width="@dimen/volume_dialog_tap_target_size"
-                    android:layout_height="@dimen/volume_dialog_tap_target_size"
-                    android:layout_gravity="center"
-                    android:contentDescription="@string/accessibility_volume_settings"
-                    android:background="@drawable/ripple_drawable_20dp"
-                    android:tint="?android:attr/textColorSecondary"
-                    android:soundEffectsEnabled="false" />
-            </FrameLayout>
-        </LinearLayout>
+            android:clipToPadding="false">
 
-        <FrameLayout
-            android:id="@+id/odi_captions"
-            android:layout_width="@dimen/volume_dialog_caption_size"
-            android:layout_height="@dimen/volume_dialog_caption_size"
-            android:layout_marginRight="68dp"
-            android:gravity="right"
-            android:layout_gravity="right"
-            android:clipToPadding="false"
-            android:translationZ="@dimen/volume_dialog_elevation"
-            android:background="@drawable/rounded_bg_full">
-            <com.android.systemui.volume.CaptionsToggleImageButton
-                android:id="@+id/odi_captions_icon"
-                android:src="@drawable/ic_volume_odi_captions_disabled"
-                style="@style/VolumeButtons"
-                android:background="@drawable/rounded_ripple"
-                android:layout_width="match_parent"
+            <!-- The ringer drawer and the caption button. -->
+            <FrameLayout
+                android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:tint="@color/caption_tint_color_selector"
-                android:layout_gravity="center"
-                android:soundEffectsEnabled="false"
-                sysui:optedOut="false"/>
-        </FrameLayout>
+                android:paddingRight="@dimen/volume_dialog_stream_padding"
+                android:clipChildren="false"
+                android:clipToPadding="false"
+                android:orientation="vertical">
+
+                <include layout="@layout/volume_ringer_drawer"
+                    android:layout_gravity="top|right"/>
+
+                <FrameLayout
+                    android:id="@+id/odi_captions"
+                    android:layout_width="@dimen/volume_dialog_caption_size"
+                    android:layout_height="@dimen/volume_dialog_caption_size"
+                    android:gravity="center"
+                    android:layout_gravity="bottom|right"
+                    android:layout_marginBottom="@dimen/volume_dialog_tap_target_size"
+                    android:clipToPadding="false">
+
+                    <com.android.systemui.volume.CaptionsToggleImageButton
+                        android:id="@+id/odi_captions_icon"
+                        android:src="@drawable/ic_volume_odi_captions_disabled"
+                        style="@style/VolumeButtons"
+                        android:background="@drawable/rounded_ripple"
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent"
+                        android:tint="@color/caption_tint_color_selector"
+                        android:layout_gravity="center"
+                        android:soundEffectsEnabled="false"
+                        sysui:optedOut="false"/>
+
+                </FrameLayout>
+
+            </FrameLayout>
+
+            <FrameLayout
+                android:visibility="gone"
+                android:id="@+id/ringer"
+                android:layout_width="@dimen/volume_dialog_ringer_size"
+                android:layout_height="@dimen/volume_dialog_ringer_size"
+                android:layout_marginBottom="@dimen/volume_dialog_spacer"
+                android:gravity="right"
+                android:layout_gravity="right"
+                android:translationZ="@dimen/volume_dialog_elevation"
+                android:clipToPadding="false"
+                android:background="@drawable/rounded_bg_full">
+                <com.android.keyguard.AlphaOptimizedImageButton
+                    android:id="@+id/ringer_icon"
+                    style="@style/VolumeButtons"
+                    android:background="@drawable/rounded_ripple"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:scaleType="fitCenter"
+                    android:padding="@dimen/volume_dialog_ringer_icon_padding"
+                    android:tint="@color/accent_tint_color_selector"
+                    android:layout_gravity="center"
+                    android:soundEffectsEnabled="false" />
+
+                <include layout="@layout/volume_dnd_icon"
+                         android:layout_width="match_parent"
+                         android:layout_height="wrap_content"
+                         android:layout_marginRight="@dimen/volume_dialog_stream_padding"
+                         android:layout_marginTop="6dp"/>
+            </FrameLayout>
+
+            <LinearLayout
+                android:id="@+id/main"
+                android:layout_width="wrap_content"
+                android:minWidth="@dimen/volume_dialog_panel_width"
+                android:layout_height="wrap_content"
+                android:gravity="right"
+                android:layout_gravity="right"
+                android:orientation="vertical"
+                android:clipChildren="false"
+                android:clipToPadding="false">
+                <LinearLayout
+                    android:id="@+id/volume_dialog_rows"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:minWidth="@dimen/volume_dialog_panel_width"
+                    android:gravity="center"
+                    android:orientation="horizontal">
+                    <!-- volume rows added and removed here! :-) -->
+                </LinearLayout>
+                <FrameLayout
+                    android:id="@+id/settings_container"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content">
+                    <com.android.keyguard.AlphaOptimizedImageButton
+                        android:id="@+id/settings"
+                        android:src="@drawable/horizontal_ellipsis"
+                        android:layout_width="@dimen/volume_dialog_tap_target_size"
+                        android:layout_height="@dimen/volume_dialog_tap_target_size"
+                        android:layout_gravity="center"
+                        android:contentDescription="@string/accessibility_volume_settings"
+                        android:background="@drawable/ripple_drawable_20dp"
+                        android:tint="?android:attr/colorBackgroundFloating"
+                        android:soundEffectsEnabled="false" />
+                </FrameLayout>
+            </LinearLayout>
+
+        </LinearLayout>
 
         <ViewStub
             android:id="@+id/odi_captions_tooltip_stub"
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 73beefc..d996cee 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -32,7 +32,8 @@
             android:id="@+id/header_icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:paddingEnd="@dimen/media_output_dialog_header_icon_padding"/>
+            android:paddingEnd="@dimen/media_output_dialog_header_icon_padding"
+            android:importantForAccessibility="no"/>
 
         <LinearLayout
             android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 0822947..93dd1a1 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -81,6 +81,22 @@
             android:layout_height="match_parent"
             android:layout_weight="@integer/qs_footer_actions_weight"
             android:gravity="center_vertical|end" >
+
+            <com.android.systemui.statusbar.AlphaOptimizedImageView
+                android:id="@+id/pm_lite"
+                android:layout_width="@dimen/qs_footer_action_button_size"
+                android:layout_height="@dimen/qs_footer_action_button_size"
+                android:background="?android:attr/selectableItemBackgroundBorderless"
+                android:clickable="true"
+                android:clipToPadding="false"
+                android:contentDescription="@string/accessibility_quick_settings_edit"
+                android:focusable="true"
+                android:padding="@dimen/qs_footer_icon_padding"
+                android:src="@*android:drawable/ic_lock_power_off"
+                android:tint="?android:attr/colorForeground"
+                android:visibility="gone"
+                />
+
             <com.android.systemui.statusbar.phone.MultiUserSwitch
                 android:id="@+id/multi_user_switch"
                 android:layout_width="@dimen/qs_footer_action_button_size"
diff --git a/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml b/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml
index ee54f1d..c830773 100644
--- a/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml
+++ b/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml
@@ -14,4 +14,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<include layout="@layout/qs_paged_page" />
+<com.android.systemui.qs.SideLabelTileLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/tile_page"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false"
+    android:clipToPadding="false" />
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 571cbbc..1ce87c1 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -20,7 +20,6 @@
     android:layout_height="wrap_content"
     android:clipChildren="false"
     android:clipToPadding="false"
-    android:minHeight="48dp"
     android:paddingTop="12dp">
     <LinearLayout
         android:id="@+id/label_group"
diff --git a/packages/SystemUI/res/layout/qs_tile_label_divider.xml b/packages/SystemUI/res/layout/qs_tile_label_divider.xml
deleted file mode 100644
index 150a5b8..0000000
--- a/packages/SystemUI/res/layout/qs_tile_label_divider.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<View />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 23f3425..bbb6107 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -37,12 +37,6 @@
         android:layout_height="match_parent"
         android:layout_width="match_parent" />
 
-    <ViewStub
-        android:id="@+id/keyguard_user_switcher_stub"
-        android:layout="@layout/keyguard_user_switcher"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent" />
-
     <include
         layout="@layout/keyguard_status_view"
         android:visibility="gone" />
@@ -61,12 +55,14 @@
             android:id="@+id/qs_frame"
             android:layout="@layout/qs_panel"
             android:layout_width="@dimen/qs_panel_width"
-            android:layout_height="match_parent"
+            android:layout_height="0dp"
             android:clipToPadding="false"
             android:clipChildren="false"
             systemui:viewType="com.android.systemui.plugins.qs.QS"
             systemui:layout_constraintStart_toStartOf="parent"
             systemui:layout_constraintEnd_toEndOf="parent"
+            systemui:layout_constraintTop_toTopOf="parent"
+            systemui:layout_constraintBottom_toBottomOf="parent"
         />
 
         <androidx.constraintlayout.widget.Guideline
@@ -109,5 +105,11 @@
         layout="@layout/keyguard_bottom_area"
         android:visibility="gone" />
 
+    <ViewStub
+        android:id="@+id/keyguard_user_switcher_stub"
+        android:layout="@layout/keyguard_user_switcher"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent" />
+
     <include layout="@layout/status_bar_expanded_plugin_frame"/>
 </com.android.systemui.statusbar.phone.NotificationPanelView>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 1810c19..6aac5a3 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -21,6 +21,8 @@
     android:layout_height="wrap_content"
     android:gravity="right"
     android:layout_gravity="right"
+    android:paddingRight="@dimen/volume_dialog_stream_padding"
+    android:clipToPadding="false"
     android:background="@android:color/transparent"
     android:theme="@style/volume_dialog_theme">
 
@@ -34,13 +36,15 @@
         android:layout_gravity="right"
         android:background="@android:color/transparent"
         android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_right"
-        android:paddingTop="@dimen/volume_dialog_panel_transparent_padding"
-        android:paddingBottom="@dimen/volume_dialog_panel_transparent_padding"
         android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding"
         android:orientation="vertical"
-        android:clipToPadding="false">
+        android:clipToPadding="false"
+        android:clipChildren="false">
+
+        <include layout="@layout/volume_ringer_drawer" />
 
         <FrameLayout
+            android:visibility="gone"
             android:id="@+id/ringer"
             android:layout_width="@dimen/volume_dialog_ringer_size"
             android:layout_height="@dimen/volume_dialog_ringer_size"
@@ -77,10 +81,8 @@
             android:gravity="right"
             android:layout_gravity="right"
             android:orientation="vertical"
-            android:translationZ="@dimen/volume_dialog_elevation"
             android:clipChildren="false"
-            android:clipToPadding="false"
-            android:background="@drawable/rounded_bg_full" >
+            android:clipToPadding="false" >
             <LinearLayout
                 android:id="@+id/volume_dialog_rows"
                 android:layout_width="wrap_content"
@@ -88,24 +90,22 @@
                 android:minWidth="@dimen/volume_dialog_panel_width"
                 android:gravity="center"
                 android:orientation="horizontal"
-                android:paddingRight="@dimen/volume_dialog_stream_padding"
-                android:paddingLeft="@dimen/volume_dialog_stream_padding">
+                android:layout_marginTop="@dimen/volume_row_slider_padding_start">
                     <!-- volume rows added and removed here! :-) -->
             </LinearLayout>
             <FrameLayout
                 android:id="@+id/settings_container"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:background="@drawable/rounded_bg_bottom_background">
+                android:layout_height="wrap_content">
                 <com.android.keyguard.AlphaOptimizedImageButton
                     android:id="@+id/settings"
-                    android:src="@drawable/ic_tune_black_16dp"
+                    android:src="@drawable/horizontal_ellipsis"
                     android:layout_width="@dimen/volume_dialog_tap_target_size"
                     android:layout_height="@dimen/volume_dialog_tap_target_size"
                     android:layout_gravity="center"
                     android:contentDescription="@string/accessibility_volume_settings"
                     android:background="@drawable/ripple_drawable_20dp"
-                    android:tint="?android:attr/textColorPrimary"
+                    android:tint="?android:attr/colorBackgroundFloating"
                     android:soundEffectsEnabled="false" />
             </FrameLayout>
         </LinearLayout>
@@ -118,7 +118,6 @@
             android:gravity="right"
             android:layout_gravity="right"
             android:clipToPadding="false"
-            android:translationZ="@dimen/volume_dialog_elevation"
             android:background="@drawable/rounded_bg_full">
             <com.android.systemui.volume.CaptionsToggleImageButton
                 android:id="@+id/odi_captions_icon"
@@ -127,7 +126,7 @@
                 android:background="@drawable/rounded_ripple"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:tint="?android:attr/textColorPrimary"
+                android:tint="?android:attr/colorAccent"
                 android:layout_gravity="center"
                 android:soundEffectsEnabled="false"
                 sysui:optedOut="false"/>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index b9efc5b..fda59b5 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -20,11 +20,12 @@
     android:layout_width="@dimen/volume_dialog_panel_width"
     android:clipChildren="false"
     android:clipToPadding="false"
+    android:translationZ="@dimen/volume_dialog_elevation"
     android:theme="@style/volume_dialog_theme">
 
     <LinearLayout
         android:layout_height="wrap_content"
-        android:layout_width="match_parent"
+        android:layout_width="@dimen/volume_dialog_panel_width"
         android:gravity="center"
         android:layout_gravity="center"
         android:orientation="vertical" >
@@ -41,21 +42,23 @@
         <FrameLayout
             android:id="@+id/volume_row_slider_frame"
             android:layout_width="match_parent"
-            android:layout_marginTop="@dimen/volume_dialog_slider_margin_top"
-            android:layout_marginBottom="@dimen/volume_dialog_slider_margin_bottom"
-            android:layoutDirection="rtl"
-            android:layout_height="@dimen/volume_dialog_slider_height">
+            android:layout_height="@dimen/volume_row_slider_height">
+            <include layout="@layout/volume_dnd_icon"/>
             <SeekBar
                 android:id="@+id/volume_row_slider"
+                android:paddingLeft="0dp"
+                android:paddingRight="0dp"
+                android:paddingStart="0dp"
+                android:paddingEnd="0dp"
                 android:clickable="true"
-                android:layout_width="@dimen/volume_dialog_slider_height"
+                android:layout_width="@dimen/volume_row_slider_height"
                 android:layout_height="match_parent"
-                android:layoutDirection="rtl"
                 android:layout_gravity="center"
-                android:rotation="90" />
+                android:rotation="270" />
         </FrameLayout>
 
         <com.android.keyguard.AlphaOptimizedImageButton
+            android:visibility="gone"
             android:id="@+id/volume_row_icon"
             style="@style/VolumeButtons"
             android:layout_width="@dimen/volume_dialog_tap_target_size"
@@ -66,6 +69,4 @@
             android:soundEffectsEnabled="false" />
     </LinearLayout>
 
-    <include layout="@layout/volume_dnd_icon"/>
-
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
new file mode 100644
index 0000000..d6e1782
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -0,0 +1,126 @@
+<?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.
+-->
+
+<!-- Contains the active ringer icon and a hidden drawer containing all three ringer options. -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:clipToPadding="false"
+    android:clipChildren="false">
+
+    <!-- Drawer view, invisible by default. -->
+    <FrameLayout
+        android:id="@+id/volume_drawer_container"
+        android:alpha="0.0"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/volume_drawer_bg"
+        android:orientation="vertical">
+
+        <!-- View that is animated to a tapped ringer selection, so it appears selected. -->
+        <FrameLayout
+            android:id="@+id/volume_drawer_selection_background"
+            android:alpha="0.0"
+            android:layout_width="@dimen/volume_ringer_drawer_item_size"
+            android:layout_height="@dimen/volume_ringer_drawer_item_size"
+            android:layout_gravity="bottom|right"
+            android:background="@drawable/volume_drawer_selection_bg" />
+
+        <LinearLayout
+            android:id="@+id/volume_drawer_options"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <FrameLayout
+                android:id="@+id/volume_drawer_vibrate"
+                android:layout_width="@dimen/volume_ringer_drawer_item_size"
+                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:description="@string/volume_ringer_hint_vibrate"
+                android:gravity="center">
+
+                <ImageView
+                    android:id="@+id/volume_drawer_vibrate_icon"
+                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_gravity="center"
+                    android:tint="?android:attr/colorAccent"
+                    android:src="@drawable/ic_volume_ringer_vibrate" />
+
+            </FrameLayout>
+
+            <FrameLayout
+                android:id="@+id/volume_drawer_mute"
+                android:layout_width="@dimen/volume_ringer_drawer_item_size"
+                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:description="@string/volume_ringer_hint_mute"
+                android:gravity="center">
+
+                <ImageView
+                    android:id="@+id/volume_drawer_mute_icon"
+                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_gravity="center"
+                    android:tint="?android:attr/colorAccent"
+                    android:src="@drawable/ic_volume_ringer_mute" />
+
+            </FrameLayout>
+
+            <FrameLayout
+                android:id="@+id/volume_drawer_normal"
+                android:layout_width="@dimen/volume_ringer_drawer_item_size"
+                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:description="@string/volume_ringer_hint_unmute"
+                android:gravity="center">
+
+                <ImageView
+                    android:id="@+id/volume_drawer_normal_icon"
+                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_gravity="center"
+                    android:tint="?android:attr/colorAccent"
+                    android:src="@drawable/ic_volume_ringer" />
+
+            </FrameLayout>
+
+        </LinearLayout>
+
+    </FrameLayout>
+
+    <!-- The current ringer selection. When the drawer is opened, this animates to the corresponding
+         position in the drawer. When the drawer is closed, it animates back. -->
+    <FrameLayout
+        android:id="@+id/volume_new_ringer_active_icon_container"
+        android:layout_width="@dimen/volume_ringer_drawer_item_size"
+        android:layout_height="@dimen/volume_ringer_drawer_item_size"
+        android:layout_gravity="bottom|right"
+        android:description="@string/volume_ringer_change"
+        android:background="@drawable/volume_drawer_selection_bg">
+
+        <ImageView
+            android:id="@+id/volume_new_ringer_active_icon"
+            android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+            android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+            android:layout_gravity="center"
+            android:tint="?android:attr/colorBackgroundFloating"
+            android:src="@drawable/ic_volume_media" />
+
+    </FrameLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 13c0110..0893c14 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -91,6 +91,9 @@
     <!-- The number of columns in the QuickSettings -->
     <integer name="quick_settings_num_columns">3</integer>
 
+    <!-- The number of columns in the Quick Settings customizer -->
+    <integer name="quick_settings_edit_num_columns">@integer/quick_settings_num_columns</integer>
+
     <!-- The number of rows in the QuickSettings -->
     <integer name="quick_settings_max_rows">3</integer>
 
@@ -421,6 +424,9 @@
      vibrator is capable of subtle vibrations -->
     <bool name="config_vibrateOnIconAnimation">false</bool>
 
+    <!-- Adjust the theme on fully custom and decorated custom view notifications -->
+    <bool name="config_adjustThemeOnNotificationCustomViews">false</bool>
+
     <!-- If true, enable the advance anti-falsing classifier on the lockscreen. On some devices it
          does not work well, particularly with noisy touchscreens. Note that disabling it may
          increase the rate of unintentional unlocks. -->
@@ -574,4 +580,7 @@
 
     <!-- Whether to use the split 2-column notification shade -->
     <bool name="config_use_split_notification_shade">false</bool>
+
+    <!-- Determines whether the shell features all run on another thread. -->
+    <bool name="config_enableShellMainThread">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4bc5a30..ea0ea5e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -456,10 +456,12 @@
 
     <dimen name="volume_dialog_panel_transparent_padding">20dp</dimen>
 
-    <dimen name="volume_dialog_stream_padding">8dp</dimen>
+    <dimen name="volume_dialog_stream_padding">12dp</dimen>
 
     <dimen name="volume_dialog_panel_width">64dp</dimen>
 
+    <dimen name="volume_dialog_panel_width_half">32dp</dimen>
+
     <dimen name="volume_dialog_slider_height">116dp</dimen>
 
     <dimen name="volume_dialog_ringer_size">64dp</dimen>
@@ -486,6 +488,13 @@
 
     <dimen name="volume_tool_tip_arrow_corner_radius">2dp</dimen>
 
+    <!-- Size of each item in the ringer selector drawer. -->
+    <dimen name="volume_ringer_drawer_item_size">64dp</dimen>
+    <dimen name="volume_ringer_drawer_item_size_half">32dp</dimen>
+
+    <!-- Size of the icon inside each item in the ringer selector drawer. -->
+    <dimen name="volume_ringer_drawer_icon_size">24dp</dimen>
+
     <!-- Gravity for the notification panel -->
     <integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
 
@@ -651,7 +660,7 @@
     </dimen>
 
     <!-- The height of a notification header -->
-    <dimen name="notification_header_height">53dp</dimen>
+    <dimen name="notification_header_height">@*android:dimen/notification_header_height</dimen>
 
     <!-- The height of the gap between adjacent notification sections. -->
     <dimen name="notification_section_divider_height">@dimen/notification_side_paddings</dimen>
@@ -966,7 +975,7 @@
     <dimen name="volume_row_padding_start">4dp</dimen>
     <dimen name="volume_row_header_padding_start">16dp</dimen>
     <dimen name="volume_row_height">64dp</dimen>
-    <dimen name="volume_row_slider_height">48dp</dimen>
+    <dimen name="volume_row_slider_height">192dp</dimen>
     <dimen name="volume_row_slider_padding_start">12dp</dimen>
 
     <dimen name="volume_expander_margin_end">2dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index d4bb128..e551892 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -42,4 +42,8 @@
     <bool name="flag_lockscreen_animations">false</bool>
 
     <bool name="flag_toast_style">false</bool>
+
+    <bool name="flag_navigation_bar_overlay">false</bool>
+
+    <bool name="flag_pm_lite">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4b95c16..74f59e3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1558,6 +1558,8 @@
     <string name="volume_stream_content_description_vibrate_a11y">%1$s. Tap to set to vibrate.</string>
     <string name="volume_stream_content_description_mute_a11y">%1$s. Tap to mute.</string>
 
+    <string name="volume_ringer_change">Tap to change ringer mode</string>
+
     <!-- Hint for accessibility. For example: double tap to mute [CHAR_LIMIT=NONE] -->
     <string name="volume_ringer_hint_mute">mute</string>
     <!-- Hint for accessibility. For example: double tap to unmute [CHAR_LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index d2698ee..b3a29a3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -190,7 +190,7 @@
         }
 
         @Override
-        public void onActivityDismissingDockedStack() {
+        public void onActivityDismissingDockedTask() {
             mHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK);
         }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index f511ed1..5f6fd30 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -29,17 +29,12 @@
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Rect;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.MathUtils;
 import android.util.TypedValue;
-import android.view.Gravity;
 import android.view.MotionEvent;
-import android.view.OrientationEventListener;
 import android.view.VelocityTracker;
-import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewPropertyAnimator;
 import android.view.WindowInsets;
 import android.view.WindowInsetsAnimation;
 import android.view.WindowInsetsAnimationControlListener;
@@ -60,7 +55,6 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
 import java.util.List;
 
@@ -105,12 +99,6 @@
     private boolean mDisappearAnimRunning;
     private SwipeListener mSwipeListener;
 
-    private boolean mIsSecurityViewLeftAligned = true;
-    private boolean mOneHandedMode = false;
-    private SecurityMode mSecurityMode = SecurityMode.Invalid;
-    private ViewPropertyAnimator mRunningOneHandedAnimator;
-    private final OrientationEventListener mOrientationEventListener;
-
     private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
             new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
 
@@ -169,20 +157,16 @@
     // Used to notify the container when something interesting happens.
     public interface SecurityCallback {
         boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
-
         void userActivity();
-
         void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
 
         /**
-         * @param strongAuth   wheher the user has authenticated with strong authentication like
-         *                     pattern, password or PIN but not by trust agents or fingerprint
+         * @param strongAuth wheher the user has authenticated with strong authentication like
+         *                   pattern, password or PIN but not by trust agents or fingerprint
          * @param targetUserId a user that needs to be the foreground user at the finish completion.
          */
         void finish(boolean strongAuth, int targetUserId);
-
         void reset();
-
         void onCancelClicked();
     }
 
@@ -240,136 +224,12 @@
         super(context, attrs, defStyle);
         mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
         mViewConfiguration = ViewConfiguration.get(context);
-
-        mOrientationEventListener = new OrientationEventListener(context) {
-            @Override
-            public void onOrientationChanged(int orientation) {
-                updateLayoutForSecurityMode(mSecurityMode);
-            }
-        };
     }
 
     void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
-        mSecurityMode = securityMode;
         mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
         updateBiometricRetry(securityMode, faceAuthEnabled);
 
-        updateLayoutForSecurityMode(securityMode);
-        mOrientationEventListener.enable();
-    }
-
-    void updateLayoutForSecurityMode(SecurityMode securityMode) {
-        mSecurityMode = securityMode;
-        mOneHandedMode = canUseOneHandedBouncer();
-
-        if (mOneHandedMode) {
-            mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
-        }
-
-        updateSecurityViewGravity();
-        updateSecurityViewLocation(false);
-    }
-
-    /** Return whether the one-handed keyguard should be enabled. */
-    private boolean canUseOneHandedBouncer() {
-        // Is it enabled?
-        if (!getResources().getBoolean(
-                com.android.internal.R.bool.config_enableOneHandedKeyguard)) {
-            return false;
-        }
-
-        if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
-            return false;
-        }
-
-        return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
-    }
-
-    /** Read whether the one-handed keyguard should be on the left/right from settings. */
-    private boolean isOneHandedKeyguardLeftAligned(Context context) {
-        try {
-            return Settings.Global.getInt(context.getContentResolver(),
-                    Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
-                    == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
-        } catch (Settings.SettingNotFoundException ex) {
-            return true;
-        }
-    }
-
-    private void updateSecurityViewGravity() {
-        View securityView = findKeyguardSecurityView();
-
-        if (securityView == null) {
-            return;
-        }
-
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
-
-        if (mOneHandedMode) {
-            lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
-        } else {
-            lp.gravity = Gravity.CENTER_HORIZONTAL;
-        }
-
-        securityView.setLayoutParams(lp);
-    }
-
-    /**
-     * Moves the inner security view to the correct location (in one handed mode) with animation.
-     * This is triggered when the user taps on the side of the screen that is not currently occupied
-     * by the security view .
-     */
-    private void updateSecurityViewLocation(boolean animate) {
-        View securityView = findKeyguardSecurityView();
-
-        if (securityView == null) {
-            return;
-        }
-
-        if (!mOneHandedMode) {
-            securityView.setTranslationX(0);
-            return;
-        }
-
-        if (mRunningOneHandedAnimator != null) {
-            mRunningOneHandedAnimator.cancel();
-            mRunningOneHandedAnimator = null;
-        }
-
-        int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
-
-        if (animate) {
-            mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
-            mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-            mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mRunningOneHandedAnimator = null;
-                }
-            });
-
-            mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            mRunningOneHandedAnimator.start();
-        } else {
-            securityView.setTranslationX(targetTranslation);
-        }
-    }
-
-    @Nullable
-    private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-
-            if (isKeyguardSecurityView(child)) {
-                return (KeyguardSecurityViewFlipper) child;
-            }
-        }
-
-        return null;
-    }
-
-    private boolean isKeyguardSecurityView(View view) {
-        return view instanceof KeyguardSecurityViewFlipper;
     }
 
     public void onPause() {
@@ -378,7 +238,6 @@
             mAlertDialog = null;
         }
         mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
-        mOrientationEventListener.disable();
     }
 
     @Override
@@ -460,44 +319,19 @@
                 if (mSwipeListener != null) {
                     mSwipeListener.onSwipeUp();
                 }
-            } else {
-                if (!mIsDragging) {
-                    handleTap(event);
-                }
             }
         }
         return true;
     }
 
-    private void handleTap(MotionEvent event) {
-        // If we're using a fullscreen security mode, skip
-        if (!mOneHandedMode) {
-            return;
-        }
-
-        // Did the tap hit the "other" side of the bouncer?
-        if ((mIsSecurityViewLeftAligned && (event.getX() > getWidth() / 2f))
-                || (!mIsSecurityViewLeftAligned && (event.getX() < getWidth() / 2f))) {
-            mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned;
-
-            Settings.Global.putInt(
-                    mContext.getContentResolver(),
-                    Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
-                    mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
-                            : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
-
-            updateSecurityViewLocation(true);
-        }
-    }
-
     void setSwipeListener(SwipeListener swipeListener) {
         mSwipeListener = swipeListener;
     }
 
     private void startSpringAnimation(float startVelocity) {
         mSpringAnimation
-                .setStartVelocity(startVelocity)
-                .animateToFinalPosition(0);
+            .setStartVelocity(startVelocity)
+            .animateToFinalPosition(0);
     }
 
     public void startDisappearAnimation(SecurityMode securitySelection) {
@@ -607,17 +441,18 @@
         return insets.inset(0, 0, 0, inset);
     }
 
+
     private void showDialog(String title, String message) {
         if (mAlertDialog != null) {
             mAlertDialog.dismiss();
         }
 
         mAlertDialog = new AlertDialog.Builder(mContext)
-                .setTitle(title)
-                .setMessage(message)
-                .setCancelable(false)
-                .setNeutralButton(R.string.ok, null)
-                .create();
+            .setTitle(title)
+            .setMessage(message)
+            .setCancelable(false)
+            .setNeutralButton(R.string.ok, null)
+            .create();
         if (!(mContext instanceof Activity)) {
             mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
         }
@@ -655,44 +490,6 @@
         }
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int maxHeight = 0;
-        int maxWidth = 0;
-        int childState = 0;
-
-        int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                MeasureSpec.getSize(widthMeasureSpec) / 2,
-                MeasureSpec.getMode(widthMeasureSpec));
-
-        for (int i = 0; i < getChildCount(); i++) {
-            final View view = getChildAt(i);
-            if (view.getVisibility() != GONE) {
-                if (mOneHandedMode && isKeyguardSecurityView(view)) {
-                    measureChildWithMargins(view, halfWidthMeasureSpec, 0,
-                            heightMeasureSpec, 0);
-                } else {
-                    measureChildWithMargins(view, widthMeasureSpec, 0,
-                            heightMeasureSpec, 0);
-                }
-                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-                maxWidth = Math.max(maxWidth,
-                        view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
-                maxHeight = Math.max(maxHeight,
-                        view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
-                childState = combineMeasuredStates(childState, view.getMeasuredState());
-            }
-        }
-
-        // Check against our minimum height and width
-        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
-        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
-
-        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
-                resolveSizeAndState(maxHeight, heightMeasureSpec,
-                        childState << MEASURED_HEIGHT_STATE_SHIFT));
-    }
-
     void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
         String message = null;
         switch (userType) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index fdab8db..1a8d420 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -404,7 +404,6 @@
         if (newView != null) {
             newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
             mSecurityViewFlipperController.show(newView);
-            mView.updateLayoutForSecurityMode(securityMode);
         }
 
         mSecurityCallback.onSecurityModeChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index 631c248..c77c867 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -92,13 +92,4 @@
                 throw new IllegalStateException("Unknown security quality:" + security);
         }
     }
-
-    /**
-     * Returns whether the given security view should be used in a "one handed" way. This can be
-     * used to change how the security view is drawn (e.g. take up less of the screen, and align to
-     * one side).
-     */
-    public static boolean isSecurityViewOneHanded(SecurityMode securityMode) {
-        return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d9a1eb6..a4054be 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -305,6 +305,7 @@
     private boolean mLogoutEnabled;
     // cached value to avoid IPCs
     private boolean mIsUdfpsEnrolled;
+    private boolean mKeyguardQsUserSwitchEnabled;
     // If the user long pressed the lock icon, disabling face auth for the current session.
     private boolean mLockIconPressed;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -1916,7 +1917,7 @@
             return isFaceAuthEnabledForUser(KeyguardUpdateMonitor.getCurrentUser())
                     && !isUdfpsEnrolled();
         }
-        return true;
+        return !isKeyguardQsUserSwitchEnabled();
     }
 
     /**
@@ -1926,6 +1927,17 @@
         return mIsUdfpsEnrolled;
     }
 
+    /**
+     * @return true if the keyguard qs user switcher shortcut is enabled
+     */
+    public boolean isKeyguardQsUserSwitchEnabled() {
+        return mKeyguardQsUserSwitchEnabled;
+    }
+
+    public void setKeyguardQsUserSwitchEnabled(boolean enabled) {
+        mKeyguardQsUserSwitchEnabled = enabled;
+    }
+
     private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() {
         @Override
         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 8cb1bc4..5db3349 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -69,7 +69,9 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        if (mAnimator != null) mAnimator.start();
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            if (mAnimator != null) mAnimator.start();
+        }
         return super.onTouchEvent(event);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index a4a781d..e6a9d4f 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -160,10 +160,9 @@
     public boolean onTouchEvent(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
             doHapticKeyClick();
+            if (mAnimator != null) mAnimator.start();
         }
 
-        if (mAnimator != null) mAnimator.start();
-
         return super.onTouchEvent(event);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 9686c91f..79f0688 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.appops;
 
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
 
 import android.Manifest;
@@ -137,8 +137,8 @@
         mAudioManager = audioManager;
         mSensorPrivacyController = sensorPrivacyController;
         mMicMuted = audioManager.isMicrophoneMute()
-                || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
-        mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA);
+                || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
+        mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
         mLocationManager = context.getSystemService(LocationManager.class);
         mPackageManager = context.getPackageManager();
         dumpManager.registerDumpable(TAG, this);
@@ -159,8 +159,8 @@
             mSensorPrivacyController.addCallback(this);
 
             mMicMuted = mAudioManager.isMicrophoneMute()
-                    || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
-            mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA);
+                    || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
+            mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
 
             mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
                     mAudioManager.getActiveRecordingConfigurations()));
@@ -583,16 +583,16 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         mMicMuted = mAudioManager.isMicrophoneMute()
-                || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
+                || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
         updateSensorDisabledStatus();
     }
 
     @Override
     public void onSensorBlockedChanged(int sensor, boolean blocked) {
         mBGHandler.post(() -> {
-            if (sensor == INDIVIDUAL_SENSOR_CAMERA) {
+            if (sensor == CAMERA) {
                 mCameraDisabled = blocked;
-            } else if (sensor == INDIVIDUAL_SENSOR_MICROPHONE) {
+            } else if (sensor == MICROPHONE) {
                 mMicMuted = mAudioManager.isMicrophoneMute() || blocked;
             }
             updateSensorDisabledStatus();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index d4678f3..127128d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -103,6 +103,11 @@
      */
     public void updateIndication(@IndicationType int type, KeyguardIndication newIndication,
             boolean showImmediately) {
+        if (type == INDICATION_TYPE_NOW_PLAYING
+                || type == INDICATION_TYPE_REVERSE_CHARGING) {
+            // temporarily don't show here, instead use AmbientContainer b/181049781
+            return;
+        }
         final boolean hasPreviousIndication = mIndicationMessages.get(type) != null;
         final boolean hasNewIndication = newIndication != null;
         if (!hasNewIndication) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
index c526c5d..62b9458 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
@@ -21,6 +21,7 @@
 import android.view.View;
 
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.FeatureFlags;
 
 import java.util.function.Consumer;
 
@@ -31,16 +32,22 @@
 public class NavigationBarOverlayController {
 
     protected final Context mContext;
+    protected final FeatureFlags mFeatureFlags;
 
     @Inject
-    public NavigationBarOverlayController(Context context) {
+    public NavigationBarOverlayController(Context context, FeatureFlags featureFlags) {
         mContext = context;
+        mFeatureFlags = featureFlags;
     }
 
     public Context getContext() {
         return mContext;
     }
 
+    public boolean isNavigationBarOverlayEnabled() {
+        return mFeatureFlags.isNavigationBarOverlayEnabled();
+    }
+
     /**
      * Initialize the controller with visibility change callback and light/dark icon color.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index b55fa4d..61e1d61 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -192,6 +192,7 @@
             buttonDispatchers.valueAt(i).setDarkIntensity(darkIntensity);
         }
         mView.getRotationButtonController().setDarkIntensity(darkIntensity);
+
         Dependency.get(NavigationBarOverlayController.class).setDarkIntensity(darkIntensity);
         for (DarkIntensityListener listener : mDarkIntensityListeners) {
             listener.onDarkIntensity(darkIntensity);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 8e75bec..19e3278 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -171,6 +171,7 @@
     private NotificationPanelViewController mPanelView;
     private FloatingRotationButton mFloatingRotationButton;
     private RotationButtonController mRotationButtonController;
+    private NavigationBarOverlayController mNavBarOverlayController;
 
     /**
      * Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -339,8 +340,11 @@
                 isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton,
                 mRotationButtonListener);
 
-        Dependency.get(NavigationBarOverlayController.class).init(
-                mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor);
+        mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class);
+        if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+            mNavBarOverlayController.init(
+                    mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor);
+        }
 
         mConfiguration = new Configuration();
         mTmpLastConfiguration = new Configuration();
@@ -431,8 +435,9 @@
 
         // The visibility of the navigation bar buttons is dependent on the transient state of
         // the navigation bar.
-        Dependency.get(NavigationBarOverlayController.class).setButtonState(
-                isTransient, /* force */ false);
+        if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+            mNavBarOverlayController.setButtonState(isTransient, /* force */ false);
+        }
     }
 
     void onBarTransition(int newMode) {
@@ -666,7 +671,9 @@
         }
         mImeVisible = visible;
         mRotationButtonController.getRotationButton().setCanShowRotationButton(!mImeVisible);
-        Dependency.get(NavigationBarOverlayController.class).setCanShow(!mImeVisible);
+        if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+            mNavBarOverlayController.setCanShow(!mImeVisible);
+        }
     }
 
     public void setDisabledFlags(int disabledFlags) {
@@ -999,10 +1006,9 @@
         } else {
             updateButtonLocation(getRotateSuggestionButton(), inScreenSpace);
         }
-        final NavigationBarOverlayController navBarButtonsController =
-                Dependency.get(NavigationBarOverlayController.class);
-        if (navBarButtonsController.isVisible()) {
-            updateButtonLocation(navBarButtonsController.getCurrentView(), inScreenSpace);
+        if (mNavBarOverlayController.isNavigationBarOverlayEnabled()
+                && mNavBarOverlayController.isVisible()) {
+            updateButtonLocation(mNavBarOverlayController.getCurrentView(), inScreenSpace);
         }
         return mTmpRegion;
     }
@@ -1230,7 +1236,9 @@
         if (mRotationButtonController != null) {
             mRotationButtonController.registerListeners();
         }
-        Dependency.get(NavigationBarOverlayController.class).registerListeners();
+        if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+            mNavBarOverlayController.registerListeners();
+        }
 
         getViewTreeObserver().addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
         updateNavButtonIcons();
@@ -1247,7 +1255,10 @@
         if (mRotationButtonController != null) {
             mRotationButtonController.unregisterListeners();
         }
-        Dependency.get(NavigationBarOverlayController.class).unregisterListeners();
+
+        if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+            mNavBarOverlayController.unregisterListeners();
+        }
 
         mEdgeBackGestureHandler.onNavBarDetached();
         getViewTreeObserver().removeOnComputeInternalInsetsListener(
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
index 65d4e23..870e3be 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
@@ -132,7 +132,7 @@
             Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
                     Secure.NAVIGATION_MODE, String.valueOf(mode)));
         if (DEBUG) {
-            Log.e(TAG, "updateCurrentInteractionMode: mode=" + mode);
+            Log.d(TAG, "updateCurrentInteractionMode: mode=" + mode);
             dumpAssetPaths(mCurrentUserContext);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 2f9b17a..2ea8657 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -19,8 +19,6 @@
 import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
 import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
 
-import static com.android.systemui.people.PeopleSpaceUtils.getUserHandle;
-
 import android.app.Activity;
 import android.app.INotificationManager;
 import android.app.people.IPeopleManager;
@@ -155,7 +153,7 @@
                 if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
                 mLauncherApps.cacheShortcuts(tile.getPackageName(),
                         Collections.singletonList(tile.getId()),
-                        getUserHandle(tile), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
+                        tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
             } catch (Exception e) {
                 Log.w(TAG, "Exception caching shortcut:" + e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
index 9ae7847..6f89332 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
@@ -21,7 +21,6 @@
 import android.content.pm.LauncherApps;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
-import android.os.UserHandle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -82,7 +81,7 @@
     public void setOnClickListener(LauncherApps launcherApps, PeopleSpaceTile tile) {
         mTileView.setOnClickListener(v ->
                 launcherApps.startShortcut(tile.getPackageName(), tile.getId(), null, null,
-                        UserHandle.getUserHandleForUid(tile.getUid())));
+                        tile.getUserHandle()));
     }
 
     /** Sets the click listener of the tile directly. */
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 5dda23e..41080af 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -55,7 +55,6 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.provider.ContactsContract;
 import android.provider.Settings;
 import android.service.notification.ConversationChannelWrapper;
@@ -160,7 +159,6 @@
         List<ConversationChannelWrapper> conversations =
                 notificationManager.getConversations(
                         false).getList();
-
         // Add priority conversations to tiles list.
         Stream<ShortcutInfo> priorityConversations = conversations.stream()
                 .filter(c -> c.getNotificationChannel() != null
@@ -252,7 +250,11 @@
             }
 
             // If tile is null, we need to retrieve from persisted storage.
-            if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots");
+            if (DEBUG) {
+                Log.d(TAG,
+                        "Retrieving from storage after reboots: " + shortcutId + " user: " + userId
+                                + " pkg: " + pkg);
+            }
             LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
             ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
             if (channel == null) {
@@ -384,7 +386,7 @@
             PeopleSpaceTile tile, Map<String, NotificationEntry> visibleNotifications) {
         String shortcutId = tile.getId();
         String packageName = tile.getPackageName();
-        int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+        int userId = getUserId(tile);
         String key = getKey(shortcutId, packageName, userId);
         if (!visibleNotifications.containsKey(key)) {
             if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key);
@@ -641,7 +643,8 @@
             activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId());
             activityIntent.putExtra(
                     PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName());
-            activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid());
+            activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE,
+                    tile.getUserHandle());
             views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity(
                     context,
                     appWidgetId,
@@ -788,7 +791,6 @@
             Log.i(TAG, "ConversationChannel is null");
             return null;
         }
-
         PeopleSpaceTile tile = new PeopleSpaceTile.Builder(channel, launcherApps).build();
         if (!PeopleSpaceUtils.shouldKeepConversation(tile)) {
             Log.i(TAG, "PeopleSpaceTile is not valid");
@@ -1069,11 +1071,6 @@
 
     /** Returns the userId associated with a {@link PeopleSpaceTile} */
     public static int getUserId(PeopleSpaceTile tile) {
-        return getUserHandle(tile).getIdentifier();
-    }
-
-    /** Returns the {@link UserHandle} associated with a {@link PeopleSpaceTile} */
-    public static UserHandle getUserHandle(PeopleSpaceTile tile) {
-        return UserHandle.getUserHandleForUid(tile.getUid());
+        return tile.getUserHandle().getIdentifier();
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
index 358f311..13e30f9 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
@@ -41,7 +41,8 @@
         Intent intent = getIntent();
         String tileId = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID);
         String packageName = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME);
-        int uid = intent.getIntExtra(PeopleSpaceWidgetProvider.EXTRA_UID, 0);
+        UserHandle userHandle = intent.getParcelableExtra(
+                PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE);
 
         if (tileId != null && !tileId.isEmpty()) {
             if (DEBUG) {
@@ -52,7 +53,7 @@
                 LauncherApps launcherApps =
                         getApplicationContext().getSystemService(LauncherApps.class);
                 launcherApps.startShortcut(
-                        packageName, tileId, null, null, UserHandle.getUserHandleForUid(uid));
+                        packageName, tileId, null, null, userHandle);
             } catch (Exception e) {
                 Log.e(TAG, "Exception starting shortcut:" + e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index 3d1055f..90baf56 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -48,7 +48,7 @@
 
     public static final String EXTRA_TILE_ID = "extra_tile_id";
     public static final String EXTRA_PACKAGE_NAME = "extra_package_name";
-    public static final String EXTRA_UID = "extra_uid";
+    public static final String EXTRA_USER_HANDLE = "extra_user_handle";
 
     public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
 
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
index 80794cb..0503522 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -123,7 +123,8 @@
             fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId());
             fillInIntent.putExtra(
                     PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName());
-            fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid());
+            fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE,
+                    tile.getUserHandle());
             personView.setOnClickFillInIntent(R.id.item, fillInIntent);
         } catch (Exception e) {
             Log.e(TAG, "Couldn't retrieve shortcut information", e);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index c8edaec..fe76668 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -29,7 +29,6 @@
 import com.android.systemui.qs.TouchAnimator.Builder;
 import com.android.systemui.qs.TouchAnimator.Listener;
 import com.android.systemui.qs.dagger.QSScope;
-import com.android.systemui.qs.tileimpl.QSTileBaseView;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
@@ -56,7 +55,8 @@
     private final ArrayList<View> mAllViews = new ArrayList<>();
     /**
      * List of {@link View}s representing Quick Settings that are being animated from the quick QS
-     * position to the normal QS panel.
+     * position to the normal QS panel. These views will only show once the animation is complete,
+     * to prevent overlapping of semi transparent views
      */
     private final ArrayList<View> mQuickQsViews = new ArrayList<>();
     private final QuickQSPanel mQuickQsPanel;
@@ -233,7 +233,6 @@
                 // Quick tiles.
                 QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
                 if (quickTileView == null) continue;
-                View qqsBgCircle = ((QSTileBaseView) quickTileView).getBgCircle();
 
                 getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
                 getRelativePosition(loc2, tileIcon, view);
@@ -255,11 +254,6 @@
                     translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
                     translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
 
-                    if (mFeatureFlags.isQSLabelsEnabled()) {
-                        firstPageBuilder.addFloat(qqsBgCircle, "alpha", 1, 1, 0);
-                        mAllViews.add(qqsBgCircle);
-                    }
-
                 } else { // These tiles disappear when expanding
                     firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
                     translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
@@ -271,7 +265,11 @@
                             translationX);
                 }
 
-                mQuickQsViews.add(tileView.getIconWithBackground());
+                if (mFeatureFlags.isQSLabelsEnabled()) {
+                    mQuickQsViews.add(tileView);
+                } else {
+                    mQuickQsViews.add(tileView.getIconWithBackground());
+                }
                 mAllViews.add(tileView.getIcon());
                 mAllViews.add(quickTileView);
             } else if (mFullRows && isIconInAnimatedRow(count)) {
@@ -362,7 +360,7 @@
         if(view == parent || view == null) return;
         // Ignore tile pages as they can have some offset we don't want to take into account in
         // RTL.
-        if (!(view instanceof PagedTileLayout.TilePage || view instanceof SideLabelTileLayout)) {
+        if (!isAPage(view)) {
             loc1[0] += view.getLeft();
             loc1[1] += view.getTop();
         }
@@ -374,6 +372,16 @@
         getRelativePositionInt(loc1, (View) view.getParent(), parent);
     }
 
+    // Returns true if the view is a possible page in PagedTileLayout
+    private boolean isAPage(View view) {
+        if (view instanceof PagedTileLayout.TilePage) {
+            return true;
+        } else if (view instanceof SideLabelTileLayout) {
+            return !(view instanceof QuickQSPanel.QQSSideLabelTileLayout);
+        }
+        return false;
+    }
+
     public void setPosition(float position) {
         if (mNeedsAnimatorUpdate) {
             updateAnimators();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 16e5196..2bea72c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs;
 
+import static com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED;
+
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.Intent;
@@ -41,6 +43,7 @@
 import com.android.systemui.util.ViewController;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 /**
  * Controller for {@link QSFooterView}.
@@ -63,6 +66,8 @@
     private final View mEdit;
     private final MultiUserSwitch mMultiUserSwitch;
     private final PageIndicator mPageIndicator;
+    private final View mPowerMenuLite;
+    private final boolean mShowPMLiteButton;
 
     private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
             new UserInfoController.OnUserInfoChangedListener() {
@@ -123,7 +128,8 @@
             DeviceProvisionedController deviceProvisionedController, UserTracker userTracker,
             QSPanelController qsPanelController, QSDetailDisplayer qsDetailDisplayer,
             QuickQSPanelController quickQSPanelController,
-            TunerService tunerService, MetricsLogger metricsLogger) {
+            TunerService tunerService, MetricsLogger metricsLogger,
+            @Named(PM_LITE_ENABLED) boolean showPMLiteButton) {
         super(view);
         mUserManager = userManager;
         mUserInfoController = userInfoController;
@@ -141,10 +147,15 @@
         mEdit = mView.findViewById(android.R.id.edit);
         mMultiUserSwitch = mView.findViewById(R.id.multi_user_switch);
         mPageIndicator = mView.findViewById(R.id.footer_page_indicator);
+        mPowerMenuLite = mView.findViewById(R.id.pm_lite);
+        mShowPMLiteButton = showPMLiteButton;
     }
 
     @Override
     protected void onViewAttached() {
+        if (mShowPMLiteButton) {
+            mPowerMenuLite.setVisibility(View.VISIBLE);
+        }
         mView.addOnLayoutChangeListener(
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
                         mView.updateAnimator(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 91ae571..0563307 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -112,7 +112,7 @@
     private int mMediaTotalBottomMargin;
     private int mFooterMarginStartHorizontal;
     private Consumer<Boolean> mMediaVisibilityChangedListener;
-    private boolean mSideLabels;
+    protected boolean mSideLabels;
 
     public QSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -201,16 +201,20 @@
                 mFooterPageIndicator.setNumPages(((PagedTileLayout) mTileLayout).getNumPages());
             }
 
-            // Allow the UI to be as big as it want's to, we're in a scroll view
-            int newHeight = 10000;
-            int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
-            int excessHeight = newHeight - availableHeight;
-            // Measure with EXACTLY. That way, The content will only use excess height and will
-            // be measured last, after other views and padding is accounted for. This only
-            // works because our Layouts in here remeasure themselves with the exact content
-            // height.
-            heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
-            ((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight);
+            // In landscape, mTileLayout's parent is not the panel but a view that contains the
+            // tile layout and the media controls.
+            if (((View) mTileLayout).getParent() == this) {
+                // Allow the UI to be as big as it want's to, we're in a scroll view
+                int newHeight = 10000;
+                int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
+                int excessHeight = newHeight - availableHeight;
+                // Measure with EXACTLY. That way, The content will only use excess height and will
+                // be measured last, after other views and padding is accounted for. This only
+                // works because our Layouts in here remeasure themselves with the exact content
+                // height.
+                heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
+                ((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight);
+            }
         }
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
@@ -758,7 +762,7 @@
                 // Let's use 3 columns to match the current layout
                 int columns;
                 if (mSideLabels) {
-                    columns = horizontal ? 1 : 2;
+                    columns = horizontal ? 2 : 4;
                 } else {
                     columns = horizontal ? 3 : TileLayout.NO_MAX_COLUMNS;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index fcb35e2..d60801e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -18,7 +18,6 @@
 
 import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
 import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
-import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
 import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
 
 import android.annotation.NonNull;
@@ -66,7 +65,6 @@
 
     private BrightnessMirrorController mBrightnessMirrorController;
     private boolean mGridContentVisible = true;
-    private boolean mQsLabelsFlag;
 
     private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
             new QSPanel.OnConfigurationChangedListener() {
@@ -93,7 +91,6 @@
             DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
             QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
             BrightnessSlider.Factory brightnessSliderFactory,
-            @Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
             FeatureFlags featureFlags) {
         super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
                 metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags);
@@ -108,9 +105,6 @@
         mView.setBrightnessView(mBrightnessSlider.getRootView());
 
         mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider);
-
-        mQsLabelsFlag = qsLabelsFlag;
-        mSideLabels = qsLabelsFlag;
     }
 
     @Override
@@ -329,7 +323,7 @@
         @Override
         public void onTuningChanged(String key, String newValue) {
             if (QS_REMOVE_LABELS.equals(key)) {
-                if (!mQsLabelsFlag) return;
+                if (!mQSLabelFlag) return;
                 boolean newShowLabels = newValue == null || "0".equals(newValue);
                 if (mShowLabels == newShowLabels) return;
                 mShowLabels = newShowLabels;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 9426e71..f1174fb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -75,7 +75,7 @@
 
     private final QSHost.Callback mQSHostCallback = this::setTiles;
     protected boolean mShowLabels = true;
-    protected boolean mSideLabels;
+    protected boolean mQSLabelFlag;
 
     private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
             new QSPanel.OnConfigurationChangedListener() {
@@ -118,11 +118,12 @@
         mQSLogger = qsLogger;
         mDumpManager = dumpManager;
         mFeatureFlags = featureFlags;
+        mQSLabelFlag = featureFlags.isQSLabelsEnabled();
     }
 
     @Override
     protected void onInit() {
-        mView.initialize(mSideLabels);
+        mView.initialize(mQSLabelFlag);
         mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), "");
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index a29ac3b..9b66b59 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -69,12 +69,22 @@
 
     @Override
     public TileLayout createRegularTileLayout() {
-        return new QuickQSPanel.HeaderTileLayout(mContext);
+        if (mSideLabels) {
+            return new QQSSideLabelTileLayout(mContext);
+        } else {
+            return new QuickQSPanel.HeaderTileLayout(mContext);
+        }
     }
 
     @Override
     protected QSTileLayout createHorizontalTileLayout() {
-        return new DoubleLineTileLayout(mContext);
+        if (mSideLabels) {
+            TileLayout t = createRegularTileLayout();
+            t.setMaxColumns(2);
+            return t;
+        } else {
+            return new DoubleLineTileLayout(mContext);
+        }
     }
 
     @Override
@@ -331,4 +341,38 @@
             }
         }
     }
+
+    static class QQSSideLabelTileLayout extends SideLabelTileLayout {
+        QQSSideLabelTileLayout(Context context) {
+            super(context, null);
+            setClipChildren(false);
+            setClipToPadding(false);
+            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+                    LayoutParams.WRAP_CONTENT);
+            lp.gravity = Gravity.CENTER_HORIZONTAL;
+            setLayoutParams(lp);
+            setMaxColumns(4);
+        }
+
+        @Override
+        public boolean updateResources() {
+            boolean b = super.updateResources();
+            mMaxAllowedRows = 2;
+            return b;
+        }
+
+        @Override
+        protected void onConfigurationChanged(Configuration newConfig) {
+            super.onConfigurationChanged(newConfig);
+            updateResources();
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            // Make sure to always use the correct number of rows. As it's determined by the
+            // columns, just use as many as needed.
+            updateMaxRows(10000, mRecords.size());
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 383e932..671f8f7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,7 +17,6 @@
 package com.android.systemui.qs;
 
 import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
-import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
 import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
 
 import com.android.internal.logging.MetricsLogger;
@@ -42,8 +41,6 @@
 @QSScope
 public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> {
 
-    private boolean mUseSideLabels;
-
     private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
             newConfig -> {
                 int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
@@ -58,12 +55,10 @@
             @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
             @Named(QUICK_QS_PANEL) MediaHost mediaHost,
             MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
-            DumpManager dumpManager, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
-            FeatureFlags featureFlags
+            DumpManager dumpManager, FeatureFlags featureFlags
     ) {
         super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
                 uiEventLogger, qsLogger, dumpManager, featureFlags);
-        mUseSideLabels = qsLabelsFlag;
     }
 
     @Override
@@ -104,19 +99,7 @@
                 break;
             }
         }
-        if (mUseSideLabels) {
-            List<QSTile> newTiles = new ArrayList<>();
-            for (int i = 0; i < tiles.size(); i += 2) {
-                newTiles.add(tiles.get(i));
-            }
-            for (int i = 1; i < tiles.size(); i += 2) {
-                newTiles.add(tiles.get(i));
-            }
-            super.setTiles(newTiles, true);
-
-        } else {
-            super.setTiles(tiles, true);
-        }
+        super.setTiles(tiles, !mQSLabelFlag);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 74a7ac1..4de4a78 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -20,11 +20,13 @@
 import android.util.AttributeSet
 import com.android.systemui.R
 
-open class SideLabelTileLayout(context: Context, attrs: AttributeSet) : TileLayout(context, attrs) {
+open class SideLabelTileLayout(
+    context: Context,
+    attrs: AttributeSet?
+) : TileLayout(context, attrs) {
 
     override fun updateResources(): Boolean {
         return super.updateResources().also {
-            mResourceColumns = 2
             mMaxAllowedRows = 4
             mCellMarginHorizontal = (mCellMarginHorizontal * 1.2).toInt()
             mCellMarginVertical = mCellMarginHorizontal
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index d559e07..14cbf98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -124,6 +124,7 @@
     public boolean updateResources() {
         final Resources res = mContext.getResources();
         mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
+        updateColumns();
         mMaxCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
         mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
         mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 048fdc3..7a91421 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -75,6 +75,8 @@
     private static final int ACTION_ADD = 1;
     private static final int ACTION_MOVE = 2;
 
+    private static final int NUM_COLUMNS_ID = R.integer.quick_settings_edit_num_columns;
+
     private final Context mContext;
 
     private final Handler mHandler = new Handler();
@@ -87,6 +89,7 @@
     private int mEditIndex;
     private int mTileDividerIndex;
     private int mFocusIndex;
+
     private boolean mNeedsFocus;
     private List<String> mCurrentSpecs;
     private List<TileInfo> mOtherTiles;
@@ -109,7 +112,7 @@
         mDecoration = new TileItemDecoration(context);
         mMarginDecoration = new MarginTileDecoration();
         mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
-        mNumColumns = context.getResources().getInteger(R.integer.quick_settings_num_columns);
+        mNumColumns = context.getResources().getInteger(NUM_COLUMNS_ID);
         mAccessibilityDelegate = new TileAdapterDelegate();
     }
 
@@ -129,7 +132,7 @@
      * @return {@code true} if the number of columns changed, {@code false} otherwise
      */
     public boolean updateNumColumns() {
-        int numColumns = mContext.getResources().getInteger(R.integer.quick_settings_num_columns);
+        int numColumns = mContext.getResources().getInteger(NUM_COLUMNS_ID);
         if (numColumns != mNumColumns) {
             mNumColumns = numColumns;
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
index 35a8257..10192bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
@@ -21,6 +21,7 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.util.settings.GlobalSettings;
 
 import javax.inject.Named;
 
@@ -31,6 +32,8 @@
 public interface QSFlagsModule {
     String QS_LABELS_FLAG = "qs_labels_flag";
     String RBC_AVAILABLE = "rbc_available";
+    String PM_LITE_ENABLED = "pm_lite";
+    String PM_LITE_SETTING = "sysui_pm_lite";
 
     @Provides
     @SysUISingleton
@@ -46,4 +49,11 @@
     static boolean isReduceBrightColorsAvailable(Context context) {
         return ColorDisplayManager.isReduceBrightColorsAvailable(context);
     }
+
+    @Provides
+    @SysUISingleton
+    @Named(PM_LITE_ENABLED)
+    static boolean isPMLiteEnabled(FeatureFlags featureFlags, GlobalSettings globalSettings) {
+        return featureFlags.isPMLiteEnabled() && globalSettings.getInt(PM_LITE_SETTING, 0) != 0;
+    }
 }
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 a699e2e..424aafa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -105,24 +105,27 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mLabel.setSingleLine(false);
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
-        // Remeasure view if the primary label requires more then 2 lines or the secondary label
-        // text will be cut off.
-        if (mLabel.getLineCount() > mMaxLabelLines || !TextUtils.isEmpty(mSecondLine.getText())
-                        && mSecondLine.getLineHeight() > mSecondLine.getHeight()) {
-            if (!mLabel.isSingleLine()) {
-                mLabel.setSingleLine();
-                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            }
-        } else {
-            if (mLabel.isSingleLine()) {
-                mLabel.setSingleLine(false);
-                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            }
+        // Remeasure view if the primary label requires more than mMaxLabelLines lines or the
+        // secondary label text will be cut off.
+        if (shouldLabelBeSingleLine()) {
+            mLabel.setSingleLine();
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
     }
 
+    private boolean shouldLabelBeSingleLine() {
+        if (mLabel.getLineCount() > mMaxLabelLines) {
+            return true;
+        } else if (!TextUtils.isEmpty(mSecondLine.getText())
+                && mLabel.getLineCount() > mMaxLabelLines - 1) {
+            return true;
+        }
+        return false;
+    }
+
     @Override
     protected void handleStateChanged(QSTile.State state) {
         super.handleStateChanged(state);
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 dc81b702..c98de8c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -24,8 +24,8 @@
 import android.graphics.drawable.RippleDrawable
 import android.service.quicksettings.Tile.STATE_ACTIVE
 import android.view.Gravity
-import android.view.View
 import android.widget.LinearLayout
+import android.widget.RelativeLayout
 import com.android.systemui.R
 import com.android.systemui.plugins.qs.QSIconView
 import com.android.systemui.plugins.qs.QSTile
@@ -37,7 +37,6 @@
 ) : QSTileView(context, icon, false) {
 
     private var paintDrawable: PaintDrawable? = null
-    private var divider: View? = null
 
     init {
         orientation = HORIZONTAL
@@ -49,7 +48,12 @@
 
     override fun createLabel() {
         super.createLabel()
-        findViewById<LinearLayout>(R.id.label_group)?.gravity = Gravity.START
+        findViewById<LinearLayout>(R.id.label_group)?.apply {
+            gravity = Gravity.START
+            (layoutParams as? RelativeLayout.LayoutParams)?.apply {
+                removeRule(RelativeLayout.ALIGN_PARENT_TOP)
+            }
+        }
         mLabel.gravity = Gravity.START
         mLabel.textDirection = TEXT_DIRECTION_LOCALE
         mSecondLine.gravity = Gravity.START
@@ -57,7 +61,7 @@
         val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
         mLabelContainer.setPaddingRelative(0, padding, padding, padding)
         (mLabelContainer.layoutParams as LayoutParams).gravity =
-                Gravity.CENTER_VERTICAL or Gravity.START
+            Gravity.CENTER_VERTICAL or Gravity.START
     }
 
     override fun updateRippleSize() {
@@ -93,7 +97,6 @@
         paintDrawable?.setTint(getCircleColor(state.state))
         mSecondLine.setTextColor(mLabel.textColors)
         mLabelContainer.background = null
-        divider?.backgroundTintList = mLabel.textColors
     }
 
     override fun handleExpand(dualTarget: Boolean) {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index 3841dac..70287cd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -16,10 +16,13 @@
 
 package com.android.systemui.qs.tiles;
 
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
+import static android.content.pm.PackageManager.FEATURE_CAMERA_TOGGLE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
+import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.DeviceConfig;
@@ -58,8 +61,8 @@
 
     @Override
     public boolean isAvailable() {
-        return /*getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE)
-                && */whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+        return getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE)
+                && whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
                 "camera_toggle_enabled",
                 false));
     }
@@ -75,7 +78,7 @@
     }
 
     @Override
-    public int getSensorId() {
+    public @Sensor int getSensorId() {
         return CAMERA;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index 2f0071a..e9b712d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -16,10 +16,13 @@
 
 package com.android.systemui.qs.tiles;
 
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
+import static android.content.pm.PackageManager.FEATURE_MICROPHONE_TOGGLE;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
+import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.DeviceConfig;
@@ -58,7 +61,9 @@
 
     @Override
     public boolean isAvailable() {
-        return whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+        return getHost().getContext().getPackageManager()
+                .hasSystemFeature(FEATURE_MICROPHONE_TOGGLE)
+                && whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
                 "mic_toggle_enabled",
                 false));
     }
@@ -74,7 +79,7 @@
     }
 
     @Override
-    public int getSensorId() {
+    public @Sensor int getSensorId() {
         return MICROPHONE;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 00703e7..0c582bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -17,7 +17,7 @@
 package com.android.systemui.qs.tiles;
 
 import android.content.Intent;
-import android.hardware.SensorPrivacyManager.IndividualSensor;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.os.Handler;
 import android.os.Looper;
 import android.service.quicksettings.Tile;
@@ -49,7 +49,7 @@
     /**
      * @return Id of the sensor that will be toggled
      */
-    public abstract @IndividualSensor int getSensorId();
+    public abstract @Sensor int getSensorId();
 
     /**
      * @return icon for the QS tile
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS b/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
new file mode 100644
index 0000000..9b3e386
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
@@ -0,0 +1,12 @@
+# Scroll Capture (Long Screenshots)
+# Bug component: 801322
+#
+# Referenced by:
+#
+# core/java/src/android/view/OWNERS
+# core/java/src/com/android/internal/view/OWNERS
+# core/tests/coretests/src/android/view/OWNERS
+# core/tests/coretests/src/com/android/internal/view/OWNERS
+
+mrcasey@google.com
+mrenouf@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 131fde6..805ac7c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -325,6 +325,7 @@
 
         attachWindow();
         mWindow.setContentView(mScreenshotView);
+        mScreenshotView.requestApplyInsets();
 
         mScreenshotView.takePartialScreenshot(
                 rect -> takeScreenshotInternal(finisher, rect));
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index dc639dc..54b99bb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -21,24 +21,25 @@
 import static java.lang.Math.min;
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.BinderThread;
 import android.annotation.UiContext;
 import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.graphics.PixelFormat;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.IBinder;
+import android.os.ICancellationSignal;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IScrollCaptureCallbacks;
 import android.view.IScrollCaptureConnection;
 import android.view.IWindowManager;
+import android.view.ScrollCaptureResponse;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.view.ScrollCaptureViewSupport;
 
 import java.util.function.Consumer;
 
@@ -62,16 +63,19 @@
      */
     public interface Connection {
         /**
-         * Session start should be deferred until UI is active because of resource allocation and
-         * potential visible side effects in the target window.
-         *
+         * Start a session.
+
          * @param sessionConsumer listener to receive the session once active
          * @param maxPages the capture buffer size expressed as a multiple of the content height
          */
+        // TODO ListenableFuture
         void start(Consumer<Session> sessionConsumer, float maxPages);
 
         /**
-         * Close the connection.
+         * Close the connection. Must end capture if started to avoid potential unwanted visual
+         * artifacts.
+         *
+         * @see Session#end(Runnable)
          */
         void close();
     }
@@ -119,6 +123,7 @@
          * @param top the top (y) position of the tile to capture, in content rect space
          * @param consumer listener to be informed of the result
          */
+        // TODO ListenableFuture
         void requestTile(int top, Consumer<CaptureResult> consumer);
 
         /**
@@ -129,16 +134,31 @@
          */
         int getMaxTiles();
 
+        /**
+         * @return the height of each image tile
+         */
         int getTileHeight();
 
+        /**
+         * @return the height of scrollable content being captured
+         */
         int getPageHeight();
 
+        /**
+         * @return the width of the scrollable page
+         */
         int getPageWidth();
 
         /**
+         * @return the bounds on screen of the window being captured.
+         */
+        Rect getWindowBounds();
+
+        /**
          * End the capture session, return the target app to original state. The listener
          * will be called when the target app is ready to before visible and interactive.
          */
+        // TODO ListenableFuture
         void end(Runnable listener);
     }
 
@@ -185,13 +205,13 @@
                         + ", taskId=" + taskId + ", consumer=" + consumer + ")");
             }
             mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId,
-                    new ControllerCallbacks(consumer));
+                    new ClientCallbacks(consumer));
         } catch (RemoteException e) {
             Log.e(TAG, "Ignored remote exception", e);
         }
     }
 
-    private static class ControllerCallbacks extends IScrollCaptureCallbacks.Stub implements
+    private static class ClientCallbacks extends IScrollCaptureCallbacks.Stub implements
             Connection, Session, IBinder.DeathRecipient {
 
         private IScrollCaptureConnection mConnection;
@@ -206,46 +226,63 @@
         private int mTileWidth;
         private Rect mRequestRect;
         private boolean mStarted;
+
+        private ICancellationSignal mCancellationSignal;
+        private Rect mWindowBounds;
+        private Rect mBoundsInWindow;
         private int mMaxTiles;
 
-        private ControllerCallbacks(Consumer<Connection> connectionConsumer) {
+        private ClientCallbacks(Consumer<Connection> connectionConsumer) {
             mConnectionConsumer = connectionConsumer;
         }
 
-        // IScrollCaptureCallbacks
-
+        @BinderThread
         @Override
-        public void onConnected(IScrollCaptureConnection connection, Rect scrollBounds,
-                Point positionInWindow) throws RemoteException {
+        public void onScrollCaptureResponse(ScrollCaptureResponse response) throws RemoteException {
             if (DEBUG_SCROLL) {
-                Log.d(TAG, "onConnected(connection=" + connection + ", scrollBounds=" + scrollBounds
-                        + ", positionInWindow=" + positionInWindow + ")");
+                Log.d(TAG, "onScrollCaptureResponse(response=" + response + ")");
             }
-            mConnection = connection;
-            mConnection.asBinder().linkToDeath(this, 0);
-            mScrollBounds = scrollBounds;
-            mConnectionConsumer.accept(this);
+            if (response.isConnected()) {
+                mConnection = response.getConnection();
+                mConnection.asBinder().linkToDeath(this, 0);
+                mWindowBounds = response.getWindowBounds();
+                mBoundsInWindow = response.getBoundsInWindow();
+
+                int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height();
+                int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE));
+                mTileWidth = mBoundsInWindow.width();
+                mTileHeight = pxPerTile  / mBoundsInWindow.width();
+                if (DEBUG_SCROLL) {
+                    Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
+                    Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight);
+                    Log.d(TAG, "maxHeight: " + (mMaxTiles * mTileHeight) + "px");
+                }
+                mConnectionConsumer.accept(this);
+            }
             mConnectionConsumer = null;
-
-            int pxPerPage = mScrollBounds.width() * mScrollBounds.height();
-            int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE));
-            mTileWidth = mScrollBounds.width();
-            mTileHeight = pxPerTile  / mScrollBounds.width();
-            if (DEBUG_SCROLL) {
-                Log.d(TAG, "scrollBounds: " + mScrollBounds);
-                Log.d(TAG, "tile dimen: " + mTileWidth + "x" + mTileHeight);
-            }
         }
 
         @Override
-        public void onUnavailable() throws RemoteException {
+        public void start(Consumer<Session> sessionConsumer, float maxPages) {
             if (DEBUG_SCROLL) {
-                Log.d(TAG, "onUnavailable");
+                Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + ","
+                        + " maxPages=" + maxPages + ")");
             }
-            // The targeted app does not support scroll capture
-            // or the window could not be found... etc etc.
+            mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
+            mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
+                    mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
+            mSessionConsumer = sessionConsumer;
+
+            try {
+                mCancellationSignal = mConnection.startCapture(mReader.getSurface());
+                mStarted = true;
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to start", e);
+                mReader.close();
+            }
         }
 
+        @BinderThread
         @Override
         public void onCaptureStarted() {
             if (DEBUG_SCROLL) {
@@ -256,13 +293,25 @@
         }
 
         @Override
-        public void onCaptureBufferSent(long frameNumber, Rect contentArea) {
-            Image image = null;
-            if (frameNumber != ScrollCaptureViewSupport.NO_FRAME_PRODUCED) {
-                image = mReader.acquireNextImage();
-            }
+        public void requestTile(int top, Consumer<CaptureResult> consumer) {
             if (DEBUG_SCROLL) {
-                Log.d(TAG, "onCaptureBufferSent(frameNumber=" + frameNumber
+                Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")");
+            }
+            cancelPendingRequest();
+            mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight);
+            mResultConsumer = consumer;
+            try {
+                mCancellationSignal = mConnection.requestImage(mRequestRect);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Caught remote exception from requestImage", e);
+            }
+        }
+
+        @Override
+        public void onImageRequestCompleted(int flags, Rect contentArea) {
+            Image image = mReader.acquireLatestImage();
+            if (DEBUG_SCROLL) {
+                Log.d(TAG, "onCaptureBufferSent(flags=" + flags
                         + ", contentArea=" + contentArea + ") image=" + image);
             }
             // Save and clear first, since the consumer will likely request the next
@@ -273,61 +322,13 @@
         }
 
         @Override
-        public void onConnectionClosed() {
-            if (DEBUG_SCROLL) {
-                Log.d(TAG, "onConnectionClosed()");
-            }
-            disconnect();
-            if (mShutdownListener != null) {
-                mShutdownListener.run();
-                mShutdownListener = null;
-            }
-        }
-
-        // Misc
-
-        private void disconnect() {
-            if (mConnection != null) {
-                mConnection.asBinder().unlinkToDeath(this, 0);
-            }
-            mConnection = null;
-        }
-
-        // ScrollCaptureController.Connection
-
-        @Override
-        public void start(Consumer<Session> sessionConsumer, float maxPages) {
-            if (DEBUG_SCROLL) {
-                Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + ","
-                        + " maxPages=" + maxPages + ")"
-                        + " [maxHeight: " + (mMaxTiles * mTileHeight) + "px]");
-            }
-            mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
-            mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
-                    mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
-            mSessionConsumer = sessionConsumer;
-            try {
-                mConnection.startCapture(mReader.getSurface());
-                mStarted = true;
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to start", e);
-            }
-        }
-
-        @Override
-        public void close() {
-            end(null);
-        }
-
-        // ScrollCaptureController.Session
-
-        @Override
         public void end(Runnable listener) {
             if (DEBUG_SCROLL) {
                 Log.d(TAG, "end(listener=" + listener + ")");
             }
             if (mStarted) {
                 mShutdownListener = listener;
+                mReader.close();
                 try {
                     // listener called from onConnectionClosed callback
                     mConnection.endCapture();
@@ -342,40 +343,37 @@
             }
         }
 
+        @BinderThread
         @Override
-        public int getPageHeight() {
-            return mScrollBounds.height();
-        }
-
-        @Override
-        public int getPageWidth() {
-            return mScrollBounds.width();
-        }
-
-        @Override
-        public int getTileHeight() {
-            return mTileHeight;
-        }
-
-        @Override
-        public int getMaxTiles() {
-            return mMaxTiles;
-        }
-
-        @Override
-        public void requestTile(int top, Consumer<CaptureResult> consumer) {
-            if (DEBUG_SCROLL) {
-                Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")");
+        public void onCaptureEnded() {
+            close();
+            if (mShutdownListener != null) {
+                mShutdownListener.run();
+                mShutdownListener = null;
             }
-            mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight);
-            mResultConsumer = consumer;
-            try {
-                mConnection.requestImage(mRequestRect);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Caught remote exception from requestImage", e);
+        }
+
+        @Override
+        public void close() {
+            if (mConnection != null) {
+                try {
+                    mConnection.close();
+                } catch (RemoteException e) {
+                    /* ignore */
+                }
+                disconnect();
             }
         }
 
+        // Misc
+
+        private void disconnect() {
+            if (mConnection != null) {
+                mConnection.asBinder().unlinkToDeath(this, 0);
+            }
+            mConnection = null;
+        }
+
         /**
          * The process hosting the window went away abruptly!
          */
@@ -386,5 +384,40 @@
             }
             disconnect();
         }
+
+        @Override
+        public int getPageHeight() {
+            return mBoundsInWindow.height();
+        }
+
+        @Override
+        public int getPageWidth() {
+            return mBoundsInWindow.width();
+        }
+
+        @Override
+        public int getTileHeight() {
+            return mTileHeight;
+        }
+
+        public Rect getWindowBounds() {
+            return new Rect(mWindowBounds);
+        }
+
+        @Override
+        public int getMaxTiles() {
+            return mMaxTiles;
+        }
+
+        private void cancelPendingRequest() {
+            if (mCancellationSignal != null) {
+                try {
+                    mCancellationSignal.cancel();
+                } catch (RemoteException e) {
+                    /* ignore */
+                }
+                mCancellationSignal = null;
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 9f182e1..6586137 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -25,8 +25,8 @@
 import android.content.res.Resources
 import android.hardware.SensorPrivacyManager
 import android.hardware.SensorPrivacyManager.EXTRA_SENSOR
-import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA
-import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE
+import android.hardware.SensorPrivacyManager.Sensors.CAMERA
+import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE
 import android.os.Bundle
 import android.os.Handler
 import android.text.Html
@@ -81,7 +81,7 @@
                 dismiss()
             }
         }
-        if (!sensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor)) {
+        if (!sensorPrivacyManager.isSensorPrivacyEnabled(sensor)) {
             finish()
             return
         }
@@ -89,9 +89,9 @@
         mAlertParams.apply {
             try {
                 mMessage = Html.fromHtml(getString(when (sensor) {
-                    INDIVIDUAL_SENSOR_MICROPHONE ->
+                    MICROPHONE ->
                         R.string.sensor_privacy_start_use_mic_dialog_content
-                    INDIVIDUAL_SENSOR_CAMERA ->
+                    CAMERA ->
                         R.string.sensor_privacy_start_use_camera_dialog_content
                     else -> Resources.ID_NULL
                 }, packageManager.getApplicationInfo(sensorUsePackageName, 0)
@@ -102,9 +102,9 @@
             }
 
             mIconId = when (sensor) {
-                INDIVIDUAL_SENSOR_MICROPHONE ->
+                MICROPHONE ->
                     com.android.internal.R.drawable.perm_group_microphone
-                INDIVIDUAL_SENSOR_CAMERA -> com.android.internal.R.drawable.perm_group_camera
+                CAMERA -> com.android.internal.R.drawable.perm_group_camera
                 else -> Resources.ID_NULL
             }
 
@@ -121,7 +121,7 @@
     override fun onStart() {
         super.onStart()
 
-        sensorPrivacyManager.suppressIndividualSensorPrivacyReminders(sensorUsePackageName, true)
+        sensorPrivacyManager.suppressSensorPrivacyReminders(sensorUsePackageName, true)
         unsuppressImmediately = false
     }
 
@@ -156,11 +156,11 @@
 
         if (unsuppressImmediately) {
             sensorPrivacyManager
-                    .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
+                    .suppressSensorPrivacyReminders(sensorUsePackageName, false)
         } else {
             Handler(mainLooper).postDelayed({
                 sensorPrivacyManager
-                        .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
+                        .suppressSensorPrivacyReminders(sensorUsePackageName, false)
             }, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS)
         }
     }
@@ -170,7 +170,7 @@
     }
 
     private fun disableSensorPrivacy() {
-        sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false)
+        sensorPrivacyManager.setSensorPrivacyForProfileGroup(sensor, false)
         unsuppressImmediately = true
         setResult(RESULT_OK)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 0bfc8e5..fea521f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -72,8 +72,6 @@
     private static final Uri BRIGHTNESS_FOR_VR_FLOAT_URI =
             Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT);
 
-    private final float mMinimumBacklight;
-    private final float mMaximumBacklight;
     private final float mDefaultBacklight;
     private final float mMinimumBacklightForVr;
     private final float mMaximumBacklightForVr;
@@ -314,10 +312,6 @@
 
         mDisplayId = mContext.getDisplayId();
         PowerManager pm = context.getSystemService(PowerManager.class);
-        mMinimumBacklight = pm.getBrightnessConstraint(
-                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
-        mMaximumBacklight = pm.getBrightnessConstraint(
-                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
         mDefaultBacklight = mContext.getDisplay().getBrightnessDefault();
         mMinimumBacklightForVr = pm.getBrightnessConstraint(
                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR);
@@ -375,8 +369,8 @@
             metric = mAutomatic
                     ? MetricsEvent.ACTION_BRIGHTNESS_AUTO
                     : MetricsEvent.ACTION_BRIGHTNESS;
-            minBacklight = mMinimumBacklight;
-            maxBacklight = mMaximumBacklight;
+            minBacklight = PowerManager.BRIGHTNESS_MIN;
+            maxBacklight = PowerManager.BRIGHTNESS_MAX;
             settingToChange = Settings.System.SCREEN_BRIGHTNESS_FLOAT;
         }
         final float valFloat = MathUtils.min(convertGammaToLinearFloat(value,
@@ -439,8 +433,8 @@
             min = mMinimumBacklightForVr;
             max = mMaximumBacklightForVr;
         } else {
-            min = mMinimumBacklight;
-            max = mMaximumBacklight;
+            min = PowerManager.BRIGHTNESS_MIN;
+            max = PowerManager.BRIGHTNESS_MAX;
         }
         // convertGammaToLinearFloat returns 0-1
         if (BrightnessSynchronizer.floatEquals(brightnessValue,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 862c279..1d59257 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -82,4 +82,12 @@
     public boolean isMonetEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_monet);
     }
+
+    public boolean isNavigationBarOverlayEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_navigation_bar_overlay);
+    }
+
+    public boolean isPMLiteEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_pm_lite);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
index f2adaf0..9ed9659 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
@@ -207,7 +207,7 @@
             sb.append(g.toJson());
             count++;
         }
-        mLastSaveLen = count;
+        mLastSaveLen += count;
         sb.append("]");
         return sb.toString();
     }
@@ -249,7 +249,9 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         save();
         if (mLastSaveLen >= 0) {
-            pw.println(String.valueOf(mLastSaveLen) + " gestures written to " + mLogfile);
+            pw.println(String.valueOf(mLastSaveLen)
+                    + " gestures since last dump written to " + mLogfile);
+            mLastSaveLen = 0;
         } else {
             pw.println("error writing gestures");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 7c3b791..c8c0755 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -548,8 +548,6 @@
         try {
             mStatusBarService.onNotificationClear(
                     notification.getPackageName(),
-                    notification.getTag(),
-                    notification.getId(),
                     notification.getUser().getIdentifier(),
                     notification.getKey(),
                     dismissedByUserStats.dismissalSurface,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index d617dff..6b96ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -254,8 +254,6 @@
                 try {
                     mStatusBarService.onNotificationClear(
                             entry.getSbn().getPackageName(),
-                            entry.getSbn().getTag(),
-                            entry.getSbn().getId(),
                             entry.getSbn().getUser().getIdentifier(),
                             entry.getSbn().getKey(),
                             stats.dismissalSurface,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
index 414d620..222735a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
@@ -49,7 +49,7 @@
 
         // Custom views will most likely use just white or black as their text color.
         // We need to scan through and replace these colors by Material NEXT colors.
-        ensureThemeOnChildren();
+        ensureThemeOnChildren(mView);
 
         // Let's invert the notification colors when we're in night mode and
         // the notification background isn't colorized.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
index 301c372..d21ae13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
@@ -64,7 +64,7 @@
 
         // Custom views will most likely use just white or black as their text color.
         // We need to scan through and replace these colors by Material NEXT colors.
-        ensureThemeOnChildren();
+        ensureThemeOnChildren(mWrappedView);
 
         if (needsInversion(resolveBackgroundColor(), mWrappedView)) {
             invertViewLuminosity(mWrappedView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 5fff8c8..89babf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -36,12 +36,12 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.internal.widget.CachingIconView;
 import com.android.settingslib.Utils;
+import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.notification.TransformState;
@@ -58,9 +58,11 @@
     private final Rect mTmpRect = new Rect();
 
     protected int mBackgroundColor = 0;
-    private int mLightTextColor;
-    private int mDarkTextColor;
-    private int mDefaultTextColor;
+    private int mMaterialTextColorPrimary;
+    private int mMaterialTextColorSecondary;
+    private int mThemedTextColorPrimary;
+    private int mThemedTextColorSecondary;
+    private boolean mAdjustTheme;
 
     public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) {
         if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
@@ -97,6 +99,8 @@
         mView = view;
         mRow = row;
         onReinflated();
+        mAdjustTheme = ctx.getResources().getBoolean(
+                R.bool.config_adjustThemeOnNotificationCustomViews);
     }
 
     /**
@@ -121,15 +125,22 @@
             mBackgroundColor = backgroundColor;
             mView.setBackground(new ColorDrawable(Color.TRANSPARENT));
         }
-        mLightTextColor = mView.getContext().getColor(
-                com.android.internal.R.color.notification_primary_text_color_light);
-        mDarkTextColor = mView.getContext().getColor(
-                R.color.notification_primary_text_color_dark);
+
+        Context materialTitleContext = new ContextThemeWrapper(mView.getContext(),
+                com.android.internal.R.style.TextAppearance_Material_Notification_Title);
+        mMaterialTextColorPrimary = Utils.getColorAttr(materialTitleContext,
+                com.android.internal.R.attr.textColor).getDefaultColor();
+        Context materialContext = new ContextThemeWrapper(mView.getContext(),
+                com.android.internal.R.style.TextAppearance_Material_Notification);
+        mMaterialTextColorSecondary = Utils.getColorAttr(materialContext,
+                com.android.internal.R.attr.textColor).getDefaultColor();
 
         Context themedContext = new ContextThemeWrapper(mView.getContext(),
-                R.style.Theme_DeviceDefault_DayNight);
-        mDefaultTextColor = Utils.getColorAttr(themedContext, R.attr.textColorPrimary)
-                .getDefaultColor();
+                com.android.internal.R.style.Theme_DeviceDefault_DayNight);
+        mThemedTextColorPrimary = Utils.getColorAttr(themedContext,
+                com.android.internal.R.attr.textColorPrimary).getDefaultColor();
+        mThemedTextColorSecondary = Utils.getColorAttr(themedContext,
+                com.android.internal.R.attr.textColorSecondary).getDefaultColor();
     }
 
     protected boolean needsInversion(int defaultBackgroundColor, View view) {
@@ -207,38 +218,35 @@
         return false;
     }
 
-    protected void ensureThemeOnChildren() {
-        if (mView == null) {
+    protected void ensureThemeOnChildren(View rootView) {
+        if (!mAdjustTheme || mView == null || rootView == null) {
             return;
         }
 
         // Notifications with custom backgrounds should not be adjusted
         if (mBackgroundColor != Color.TRANSPARENT
-                || getBackgroundColor(mView) != Color.TRANSPARENT) {
+                || getBackgroundColor(mView) != Color.TRANSPARENT
+                || getBackgroundColor(rootView) != Color.TRANSPARENT) {
             return;
         }
 
         // Now let's check if there's unprotected text somewhere, and apply the theme if we find it.
-        if (!(mView instanceof ViewGroup)) {
-            return;
-        }
-        processChildrenTextColor((ViewGroup) mView);
+        processTextColorRecursive(rootView);
     }
 
-    private void processChildrenTextColor(ViewGroup viewGroup) {
-        if (viewGroup == null) {
-            return;
-        }
-
-        for (int i = 0; i < viewGroup.getChildCount(); i++) {
-            View child = viewGroup.getChildAt(i);
-            if (child instanceof TextView) {
-                int foreground = ((TextView) child).getCurrentTextColor();
-                if (foreground == mLightTextColor || foreground == mDarkTextColor) {
-                    ((TextView) child).setTextColor(mDefaultTextColor);
-                }
-            } else if (child instanceof ViewGroup) {
-                processChildrenTextColor((ViewGroup) child);
+    private void processTextColorRecursive(View view) {
+        if (view instanceof TextView) {
+            TextView textView = (TextView) view;
+            int foreground = textView.getCurrentTextColor();
+            if (foreground == mMaterialTextColorPrimary) {
+                textView.setTextColor(mThemedTextColorPrimary);
+            } else if (foreground == mMaterialTextColorSecondary) {
+                textView.setTextColor(mThemedTextColorSecondary);
+            }
+        } else if (view instanceof ViewGroup) {
+            ViewGroup viewGroup = (ViewGroup) view;
+            for (int i = 0; i < viewGroup.getChildCount(); i++) {
+                processTextColorRecursive(viewGroup.getChildAt(i));
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 2ce4037..d638019 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -89,7 +89,8 @@
     private int mNotificationStackHeight;
 
     /**
-     * Minimum top margin to avoid overlap with status bar.
+     * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
+     * avatar.
      */
     private int mMinTopMargin;
 
@@ -186,15 +187,15 @@
     /**
      * Sets up algorithm values.
      */
-    public void setup(int statusBarMinHeight, int maxShadeBottom, int notificationStackHeight,
-            float panelExpansion, int parentHeight, int keyguardStatusHeight,
-            int userSwitchHeight, int clockPreferredY, int userSwitchPreferredY,
-            boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount,
-            boolean bypassEnabled, int unlockedStackScrollerPadding, boolean showLockIcon,
-            float qsExpansion, int cutoutTopInset) {
-        mMinTopMargin = statusBarMinHeight + (showLockIcon
-                ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon)
-                + userSwitchHeight;
+    public void setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom,
+            int notificationStackHeight, float panelExpansion, int parentHeight,
+            int keyguardStatusHeight, int userSwitchHeight, int clockPreferredY,
+            int userSwitchPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark,
+            float emptyDragAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
+            boolean showLockIcon, float qsExpansion, int cutoutTopInset) {
+        mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(showLockIcon
+                        ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon,
+                userSwitchHeight);
         mMaxShadeBottom = maxShadeBottom;
         mNotificationStackHeight = notificationStackHeight;
         mPanelExpansion = panelExpansion;
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 6940050..83c347b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -287,21 +287,6 @@
                 }
     };
 
-    final KeyguardUserSwitcherController.KeyguardUserSwitcherListener
-            mKeyguardUserSwitcherListener =
-            new KeyguardUserSwitcherController.KeyguardUserSwitcherListener() {
-                @Override
-                public void onKeyguardUserSwitcherChanged(boolean open) {
-                    if (mKeyguardUserSwitcherController == null) {
-                        updateUserSwitcherVisibility(false);
-                    } else if (!mKeyguardUserSwitcherController.isSimpleUserSwitcher()) {
-                        updateUserSwitcherVisibility(open
-                                && mKeyguardStateController.isShowing()
-                                && !mKeyguardStateController.isKeyguardFadingAway());
-                    }
-                }
-            };
-
     private final LayoutInflater mLayoutInflater;
     private final PowerManager mPowerManager;
     private final AccessibilityManager mAccessibilityManager;
@@ -329,7 +314,6 @@
 
     private KeyguardAffordanceHelper mAffordanceHelper;
     private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
-    private boolean mKeyguardUserSwitcherIsShowing;
     private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
     private KeyguardStatusBarView mKeyguardStatusBar;
     private ViewGroup mBigClockContainer;
@@ -374,6 +358,7 @@
     private ValueAnimator mQsExpansionAnimator;
     private FlingAnimationUtils mFlingAnimationUtils;
     private int mStatusBarMinHeight;
+    private int mStatusBarHeaderHeightKeyguard;
     private int mNotificationsHeaderCollideDistance;
     private float mEmptyDragAmount;
     private float mDownX;
@@ -620,6 +605,7 @@
         mKeyguardQsUserSwitchEnabled =
                 mKeyguardUserSwitcherEnabled && mResources.getBoolean(
                         R.bool.config_keyguard_user_switch_opens_qs_details);
+        keyguardUpdateMonitor.setKeyguardQsUserSwitchEnabled(mKeyguardQsUserSwitchEnabled);
         mView.setWillNotDraw(!DEBUG);
         mLayoutInflater = layoutInflater;
         mFalsingManager = falsingManager;
@@ -772,6 +758,8 @@
                 .setMaxLengthSeconds(0.4f).build();
         mStatusBarMinHeight = mResources.getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
+        mStatusBarHeaderHeightKeyguard = mResources.getDimensionPixelSize(
+                R.dimen.status_bar_header_height_keyguard);
         mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
         mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize(
                 R.dimen.header_notifications_collide_distance);
@@ -808,7 +796,6 @@
             // Try to close the switcher so that callbacks are triggered if necessary.
             // Otherwise, NPV can get into a state where some of the views are still hidden
             mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false);
-            mKeyguardUserSwitcherController.removeCallback();
         }
 
         mKeyguardQsUserSwitchController = null;
@@ -828,7 +815,6 @@
                     mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView);
             mKeyguardUserSwitcherController =
                     userSwitcherComponent.getKeyguardUserSwitcherController();
-            mKeyguardUserSwitcherController.setCallback(mKeyguardUserSwitcherListener);
             mKeyguardUserSwitcherController.init();
             mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true);
         } else {
@@ -1069,7 +1055,7 @@
             int totalHeight = mView.getHeight();
             int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
             int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight);
-            int userSwitcherPreferredY = mStatusBarMinHeight;
+            int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
             boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
             final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
                     .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
@@ -1078,7 +1064,8 @@
                     ? mKeyguardQsUserSwitchController.getUserIconHeight()
                     : (mKeyguardUserSwitcherController != null
                             ? mKeyguardUserSwitcherController.getUserIconHeight() : 0);
-            mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding,
+            mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
+                    totalHeight - bottomPadding,
                     mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
                     getExpandedFraction(),
                     totalHeight,
@@ -3523,34 +3510,6 @@
         return false;
     }
 
-    private void updateUserSwitcherVisibility(boolean open) {
-        // Do not update if previously called with the same state.
-        if (mKeyguardUserSwitcherIsShowing == open) {
-            return;
-        }
-        mKeyguardUserSwitcherIsShowing = open;
-
-        if (open) {
-            animateKeyguardStatusBarOut();
-            mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
-                    mBarState,
-                    true /* keyguardFadingAway */,
-                    true /* goingToFullShade */,
-                    mBarState);
-            setKeyguardBottomAreaVisibility(mBarState, true);
-            mNotificationContainerParent.setVisibility(View.GONE);
-        } else {
-            animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
-                    StatusBarState.KEYGUARD,
-                    false,
-                    false,
-                    StatusBarState.SHADE_LOCKED);
-            setKeyguardBottomAreaVisibility(mBarState, false);
-            mNotificationContainerParent.setVisibility(View.VISIBLE);
-        }
-    }
-
     private void updateDisabledUdfpsController() {
         final boolean udfpsEnrolled = mAuthController.getUdfpsRegion() != null
                 && mAuthController.isUdfpsEnrolled(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 041a97e..b25fced 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -78,6 +78,7 @@
 import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -276,7 +277,8 @@
     public static final boolean DEBUG = false;
     public static final boolean SPEW = false;
     public static final boolean DUMPTRUCK = true; // extra dumpsys info
-    public static final boolean DEBUG_GESTURES = false;
+    public static final boolean DEBUG_GESTURES = Build.IS_DEBUGGABLE; // TODO(b/178277858)
+    public static final boolean DEBUG_GESTURES_VERBOSE = true;
     public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
     public static final boolean DEBUG_CAMERA_LIFT = false;
 
@@ -456,9 +458,7 @@
     private final DisplayMetrics mDisplayMetrics;
 
     // XXX: gesture research
-    private final GestureRecorder mGestureRec = DEBUG_GESTURES
-        ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
-        : null;
+    private GestureRecorder mGestureRec = null;
 
     private final ScreenPinningRequest mScreenPinningRequest;
 
@@ -856,6 +856,10 @@
 
         mActivityIntentHelper = new ActivityIntentHelper(mContext);
         DateTimeView.setReceiverHandler(timeTickHandler);
+
+        if (DEBUG_GESTURES) {
+            mGestureRec = new GestureRecorder(mContext.getCacheDir() + "/statusbar_gestures.dat");
+        }
     }
 
     @Override
@@ -2267,7 +2271,7 @@
 
     public boolean interceptTouchEvent(MotionEvent event) {
         if (DEBUG_GESTURES) {
-            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
+            if (DEBUG_GESTURES_VERBOSE || event.getActionMasked() != MotionEvent.ACTION_MOVE) {
                 EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
                         event.getActionMasked(), (int) event.getX(), (int) event.getY(),
                         mDisabled1, mDisabled2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
index a76d08a..7f935d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
@@ -16,17 +16,17 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.hardware.SensorPrivacyManager.IndividualSensor;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 
 public interface IndividualSensorPrivacyController extends
         CallbackController<IndividualSensorPrivacyController.Callback> {
     void init();
 
-    boolean isSensorBlocked(@IndividualSensor int sensor);
+    boolean isSensorBlocked(@Sensor int sensor);
 
-    void setSensorBlocked(@IndividualSensor int sensor, boolean blocked);
+    void setSensorBlocked(@Sensor int sensor, boolean blocked);
 
     interface Callback {
-        void onSensorBlockedChanged(@IndividualSensor int sensor, boolean blocked);
+        void onSensorBlockedChanged(@Sensor int sensor, boolean blocked);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index 32d15ed..295df05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 
 import android.hardware.SensorPrivacyManager;
-import android.hardware.SensorPrivacyManager.IndividualSensor;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.util.ArraySet;
 import android.util.SparseBooleanArray;
 
@@ -30,8 +30,7 @@
 
 public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController {
 
-    private static final int[] SENSORS = new int[] {INDIVIDUAL_SENSOR_CAMERA,
-            INDIVIDUAL_SENSOR_MICROPHONE};
+    private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE};
 
     private final @NonNull SensorPrivacyManager mSensorPrivacyManager;
     private final SparseBooleanArray mState = new SparseBooleanArray();
@@ -48,18 +47,18 @@
             mSensorPrivacyManager.addSensorPrivacyListener(sensor,
                     (enabled) -> onSensorPrivacyChanged(sensor, enabled));
 
-            mState.put(sensor, mSensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor));
+            mState.put(sensor, mSensorPrivacyManager.isSensorPrivacyEnabled(sensor));
         }
     }
 
     @Override
-    public boolean isSensorBlocked(@IndividualSensor int sensor) {
+    public boolean isSensorBlocked(@Sensor int sensor) {
         return mState.get(sensor, false);
     }
 
     @Override
-    public void setSensorBlocked(@IndividualSensor int sensor, boolean blocked) {
-        mSensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, blocked);
+    public void setSensorBlocked(@Sensor int sensor, boolean blocked) {
+        mSensorPrivacyManager.setSensorPrivacyForProfileGroup(sensor, blocked);
     }
 
     @Override
@@ -72,7 +71,7 @@
         mCallbacks.remove(listener);
     }
 
-    private void onSensorPrivacyChanged(@IndividualSensor int sensor, boolean blocked) {
+    private void onSensorPrivacyChanged(@Sensor int sensor, boolean blocked) {
         mState.put(sensor, blocked);
 
         for (Callback callback : mCallbacks) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index b76e451..8845a05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -19,9 +19,13 @@
 import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
 import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.DataSetObserver;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.os.UserHandle;
@@ -50,7 +54,6 @@
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.ViewController;
 
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
 import javax.inject.Inject;
@@ -73,9 +76,10 @@
     private final KeyguardUserAdapter mAdapter;
     private final KeyguardStateController mKeyguardStateController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private WeakReference<KeyguardUserSwitcherListener> mKeyguardUserSwitcherCallback;
     protected final SysuiStatusBarStateController mStatusBarStateController;
     private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
+    private ObjectAnimator mBgAnimator;
+    private final KeyguardUserSwitcherScrim mBackground;
 
     // Child views of KeyguardUserSwitcherView
     private KeyguardUserSwitcherListView mListView;
@@ -171,6 +175,7 @@
                 mUserSwitcherController, this);
         mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
                 keyguardStateController, dozeParameters);
+        mBackground = new KeyguardUserSwitcherScrim(context);
     }
 
     @Override
@@ -204,6 +209,9 @@
         mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
         mStatusBarStateController.addCallback(mStatusBarStateListener);
         mScreenLifecycle.addObserver(mScreenObserver);
+        mView.addOnLayoutChangeListener(mBackground);
+        mView.setBackground(mBackground);
+        mBackground.setAlpha(0);
     }
 
     @Override
@@ -217,6 +225,9 @@
         mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
         mScreenLifecycle.removeObserver(mScreenObserver);
+        mView.removeOnLayoutChangeListener(mBackground);
+        mView.setBackground(null);
+        mBackground.setAlpha(0);
     }
 
     /**
@@ -338,6 +349,13 @@
                 animate);
         PropertyAnimator.setProperty(mListView, AnimatableProperty.TRANSLATION_X, -Math.abs(x),
                 ANIMATION_PROPERTIES, animate);
+
+        Rect r = new Rect();
+        mListView.getDrawingRect(r);
+        mView.offsetDescendantRectToMyCoords(mListView, r);
+        mBackground.setGradientCenter(
+                (int) (mListView.getTranslationX() + r.left + r.width() / 2),
+                (int) (mListView.getTranslationY() + r.top + r.height() / 2));
     }
 
     /**
@@ -372,49 +390,52 @@
     }
 
     /**
-     * Remove the callback if it exists.
-     */
-    public void removeCallback() {
-        if (DEBUG) Log.d(TAG, "removeCallback");
-        mKeyguardUserSwitcherCallback = null;
-    }
-
-    /**
-     * Register to receive notifications about keyguard user switcher state
-     * (see {@link KeyguardUserSwitcherListener}.
-     *
-     * Only one callback can be used at a time.
-     *
-     * @param callback The callback to register
-     */
-    public void setCallback(KeyguardUserSwitcherListener callback) {
-        if (DEBUG) Log.d(TAG, "setCallback");
-        mKeyguardUserSwitcherCallback = new WeakReference<>(callback);
-    }
-
-    /**
-     * If user switcher state changes, notifies all {@link KeyguardUserSwitcherListener}.
-     * Switcher state is updatd before animations finish.
+     * NOTE: switcher state is updated before animations finish.
      *
      * @param animate true to animate transition. The user switcher state (i.e.
      *                {@link #isUserSwitcherOpen()}) is updated before animation is finished.
      */
     private void setUserSwitcherOpened(boolean open, boolean animate) {
-        boolean wasOpen = mUserSwitcherOpen;
         if (DEBUG) {
-            Log.d(TAG, String.format("setUserSwitcherOpened: %b -> %b (animate=%b)", wasOpen,
-                    open, animate));
+            Log.d(TAG,
+                    String.format("setUserSwitcherOpened: %b -> %b (animate=%b)",
+                            mUserSwitcherOpen, open, animate));
         }
         mUserSwitcherOpen = open;
-        if (mUserSwitcherOpen != wasOpen) {
-            notifyUserSwitcherStateChanged();
-        }
         updateVisibilities(animate);
     }
 
     private void updateVisibilities(boolean animate) {
         if (DEBUG) Log.d(TAG, String.format("updateVisibilities: animate=%b", animate));
         mEndGuestButton.animate().cancel();
+        if (mBgAnimator != null) {
+            mBgAnimator.cancel();
+        }
+
+        if (mUserSwitcherOpen) {
+            mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
+            mBgAnimator.setDuration(400);
+            mBgAnimator.setInterpolator(Interpolators.ALPHA_IN);
+            mBgAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mBgAnimator = null;
+                }
+            });
+            mBgAnimator.start();
+        } else {
+            mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 255, 0);
+            mBgAnimator.setDuration(400);
+            mBgAnimator.setInterpolator(Interpolators.ALPHA_OUT);
+            mBgAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mBgAnimator = null;
+                }
+            });
+            mBgAnimator.start();
+        }
+
         if (mUserSwitcherOpen && mCurrentUserIsGuest) {
             // Show the "End guest session" button
             mEndGuestButton.setVisibility(View.VISIBLE);
@@ -459,34 +480,6 @@
         return mUserSwitcherOpen;
     }
 
-    private void notifyUserSwitcherStateChanged() {
-        if (DEBUG) {
-            Log.d(TAG, String.format("notifyUserSwitcherStateChanged: mUserSwitcherOpen=%b",
-                    mUserSwitcherOpen));
-        }
-        if (mKeyguardUserSwitcherCallback != null) {
-            KeyguardUserSwitcherListener cb = mKeyguardUserSwitcherCallback.get();
-            if (cb != null) {
-                cb.onKeyguardUserSwitcherChanged(mUserSwitcherOpen);
-            }
-        }
-    }
-
-    /**
-     * Callback for keyguard user switcher state information
-     */
-    public interface KeyguardUserSwitcherListener {
-
-        /**
-         * Called when the keyguard enters or leaves user switcher mode. This will be called
-         * before the animations are finished.
-         *
-         * @param open if true, keyguard is showing the user switcher or transitioning from/to user
-         *             switcher mode.
-         */
-        void onKeyguardUserSwitcherChanged(boolean open);
-    }
-
     static class KeyguardUserAdapter extends
             UserSwitcherController.BaseUserAdapter implements View.OnClickListener {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
index 49f5bcd..1d9d33d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
@@ -26,7 +26,6 @@
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
-import android.util.LayoutDirection;
 import android.view.View;
 
 import com.android.systemui.R;
@@ -38,13 +37,14 @@
         implements View.OnLayoutChangeListener {
 
     private static final float OUTER_EXTENT = 2.5f;
-    private static final float INNER_EXTENT = 0.75f;
+    private static final float INNER_EXTENT = 0.25f;
 
     private int mDarkColor;
-    private int mTop;
     private int mAlpha = 255;
     private Paint mRadialGradientPaint = new Paint();
-    private int mLayoutWidth;
+    private int mCircleX;
+    private int mCircleY;
+    private int mSize;
 
     public KeyguardUserSwitcherScrim(Context context) {
         mDarkColor = context.getColor(
@@ -53,14 +53,11 @@
 
     @Override
     public void draw(Canvas canvas) {
-        boolean isLtr = getLayoutDirection() == LayoutDirection.LTR;
+        if (mAlpha == 0) {
+            return;
+        }
         Rect bounds = getBounds();
-        float width = bounds.width() * OUTER_EXTENT;
-        float height = (mTop + bounds.height()) * OUTER_EXTENT;
-        canvas.translate(0, -mTop);
-        canvas.scale(1, height / width);
-        canvas.drawRect(isLtr ? bounds.right - width : 0, 0,
-                isLtr ? bounds.right : bounds.left + width, width, mRadialGradientPaint);
+        canvas.drawRect(bounds.left, bounds.top, bounds.right, bounds.bottom, mRadialGradientPaint);
     }
 
     @Override
@@ -88,24 +85,36 @@
     public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
             int oldTop, int oldRight, int oldBottom) {
         if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
-            mLayoutWidth = right - left;
-            mTop = top;
+            int width = right - left;
+            int height = bottom - top;
+            mSize = Math.max(width, height);
             updatePaint();
         }
     }
 
     private void updatePaint() {
-        if (mLayoutWidth == 0) {
+        if (mSize == 0) {
             return;
         }
-        float radius = mLayoutWidth * OUTER_EXTENT;
-        boolean isLtr = getLayoutDirection() == LayoutDirection.LTR;
+        float outerRadius = mSize * OUTER_EXTENT;
         mRadialGradientPaint.setShader(
-                new RadialGradient(isLtr ? mLayoutWidth : 0, 0, radius,
+                new RadialGradient(mCircleX, mCircleY, outerRadius,
                         new int[] { Color.argb(
                                         (int) (Color.alpha(mDarkColor) * mAlpha / 255f), 0, 0, 0),
                                 Color.TRANSPARENT },
-                        new float[] { Math.max(0f, mLayoutWidth * INNER_EXTENT / radius), 1f },
+                        new float[] { Math.max(0f, INNER_EXTENT / OUTER_EXTENT), 1f },
                         Shader.TileMode.CLAMP));
     }
+
+    /**
+     * Sets the center of the radial gradient used as a background
+     *
+     * @param x
+     * @param y
+     */
+    public void setGradientCenter(int x, int y) {
+        mCircleX = x;
+        mCircleY = y;
+        updatePaint();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index 0a3e833..bbb2f1a 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -94,6 +94,7 @@
      */
     static final List<String> THEME_CATEGORIES = Lists.newArrayList(
             OVERLAY_CATEGORY_SYSTEM_PALETTE,
+            OVERLAY_CATEGORY_NEUTRAL_PALETTE,
             OVERLAY_CATEGORY_ICON_LAUNCHER,
             OVERLAY_CATEGORY_SHAPE,
             OVERLAY_CATEGORY_FONT,
@@ -107,6 +108,7 @@
     @VisibleForTesting
     static final Set<String> SYSTEM_USER_CATEGORIES = Sets.newHashSet(
             OVERLAY_CATEGORY_SYSTEM_PALETTE,
+            OVERLAY_CATEGORY_NEUTRAL_PALETTE,
             OVERLAY_CATEGORY_ACCENT_COLOR,
             OVERLAY_CATEGORY_FONT,
             OVERLAY_CATEGORY_SHAPE,
@@ -129,8 +131,9 @@
         mLauncherPackage = launcherPackage;
         mThemePickerPackage = themePickerPackage;
         mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet(
-                OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR,
-                OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID));
+                OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_NEUTRAL_PALETTE,
+                OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE,
+                OVERLAY_CATEGORY_ICON_ANDROID));
         mTargetPackageToCategories.put(SYSUI_PACKAGE,
                 Sets.newHashSet(OVERLAY_CATEGORY_ICON_SYSUI));
         mTargetPackageToCategories.put(SETTINGS_PACKAGE,
@@ -158,10 +161,10 @@
     void applyCurrentUserOverlays(
             Map<String, OverlayIdentifier> categoryToPackage,
             FabricatedOverlay[] pendingCreation,
-            Set<UserHandle> userHandles) {
+            int currentUser,
+            Set<UserHandle> managedProfiles) {
         // Disable all overlays that have not been specified in the user setting.
         final Set<String> overlayCategoriesToDisable = new HashSet<>(THEME_CATEGORIES);
-        overlayCategoriesToDisable.removeAll(categoryToPackage.keySet());
         final Set<String> targetPackagesToQuery = overlayCategoriesToDisable.stream()
                 .map(category -> mCategoryToTargetPackage.get(category))
                 .collect(Collectors.toSet());
@@ -172,6 +175,7 @@
                 .filter(o ->
                         mTargetPackageToCategories.get(o.targetPackageName).contains(o.category))
                 .filter(o -> overlayCategoriesToDisable.contains(o.category))
+                .filter(o -> !categoryToPackage.containsValue(new OverlayIdentifier(o.packageName)))
                 .filter(o -> o.isEnabled())
                 .map(o -> new Pair<>(o.category, o.packageName))
                 .collect(Collectors.toList());
@@ -183,17 +187,18 @@
             }
         }
 
-        // Toggle overlays in the order of THEME_CATEGORIES.
+        for (Pair<String, String> packageToDisable : overlaysToDisable) {
+            OverlayIdentifier overlayInfo = new OverlayIdentifier(packageToDisable.second);
+            setEnabled(transaction, overlayInfo, packageToDisable.first, currentUser,
+                    managedProfiles, false);
+        }
+
         for (String category : THEME_CATEGORIES) {
             if (categoryToPackage.containsKey(category)) {
                 OverlayIdentifier overlayInfo = categoryToPackage.get(category);
-                setEnabled(transaction, overlayInfo, category, userHandles, true);
+                setEnabled(transaction, overlayInfo, category, currentUser, managedProfiles, true);
             }
         }
-        for (Pair<String, String> packageToDisable : overlaysToDisable) {
-            OverlayIdentifier overlayInfo = new OverlayIdentifier(packageToDisable.second);
-            setEnabled(transaction, overlayInfo, packageToDisable.first, userHandles, false);
-        }
 
         mExecutor.execute(() -> {
             try {
@@ -210,18 +215,30 @@
     }
 
     private void setEnabled(OverlayManagerTransaction.Builder transaction,
-            OverlayIdentifier identifier, String category, Set<UserHandle> handles,
-            boolean enabled) {
+            OverlayIdentifier identifier, String category, int currentUser,
+            Set<UserHandle> managedProfiles, boolean enabled) {
         if (DEBUG) {
             Log.d(TAG, "setEnabled: " + identifier.getPackageName() + " category: "
                     + category + ": " + enabled);
         }
-        for (UserHandle userHandle : handles) {
-            transaction.setEnabled(identifier, enabled, userHandle.getIdentifier());
-        }
-        if (!handles.contains(UserHandle.SYSTEM) && SYSTEM_USER_CATEGORIES.contains(category)) {
+
+        transaction.setEnabled(identifier, enabled, currentUser);
+        if (currentUser != UserHandle.SYSTEM.getIdentifier()
+                && SYSTEM_USER_CATEGORIES.contains(category)) {
             transaction.setEnabled(identifier, enabled, UserHandle.SYSTEM.getIdentifier());
         }
+
+        // Do not apply Launcher or Theme picker overlays to managed users. Apps are not
+        // installed in there.
+        OverlayInfo overlayInfo = mOverlayManager.getOverlayInfo(identifier, UserHandle.SYSTEM);
+        if (overlayInfo == null || overlayInfo.targetPackageName.equals(mLauncherPackage)
+                || overlayInfo.targetPackageName.equals(mThemePickerPackage)) {
+            return;
+        }
+
+        for (UserHandle userHandle : managedProfiles) {
+            transaction.setEnabled(identifier, enabled, userHandle.getIdentifier());
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 1f222d8..5d02845 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -55,14 +55,13 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.settings.SecureSettings;
 
-import com.google.android.collect.Sets;
-
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -86,7 +85,7 @@
 
     protected static final int PRIMARY = 0;
     protected static final int SECONDARY = 1;
-    protected static final int NEUTRAL = 1;
+    protected static final int NEUTRAL = 2;
 
     // If lock screen wallpaper colors should also be considered when selecting the theme.
     // Doing this has performance impact, given that overlays would need to be swapped when
@@ -151,7 +150,7 @@
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
-                updateThemeOverlays();
+                reevaluateSystemTheme(true /* forceReload */);
             }
         }, filter, mBgExecutor, UserHandle.ALL);
         mSecureSettings.registerContentObserverForUser(
@@ -163,7 +162,7 @@
                             int userId) {
                         if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId);
                         if (ActivityManager.getCurrentUser() == userId) {
-                            updateThemeOverlays();
+                            reevaluateSystemTheme(true /* forceReload */);
                         }
                     }
                 },
@@ -180,7 +179,7 @@
                     mLockColors = lockColors;
                 }
                 mSystemColors = systemColor;
-                reevaluateSystemTheme();
+                reevaluateSystemTheme(false /* forceReload */);
             });
         });
         if (USE_LOCK_SCREEN_WALLPAPER) {
@@ -192,7 +191,7 @@
                     }
                     // It's possible that the user has a lock screen wallpaper. On this case we'll
                     // end up with different colors after unlocking.
-                    reevaluateSystemTheme();
+                    reevaluateSystemTheme(false /* forceReload */);
                 }
             });
         }
@@ -209,11 +208,11 @@
                     Log.d(TAG, "got new lock colors: " + wallpaperColors + " where: " + which);
                 }
             }
-            reevaluateSystemTheme();
+            reevaluateSystemTheme(false /* forceReload */);
         }, null, UserHandle.USER_ALL);
     }
 
-    private void reevaluateSystemTheme() {
+    private void reevaluateSystemTheme(boolean forceReload) {
         WallpaperColors currentColors =
                 mKeyguardStateController.isShowing() && mLockColors != null
                         ? mLockColors : mSystemColors;
@@ -228,7 +227,8 @@
             accentCandidate = getAccentColor(currentColors);
         }
 
-        if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate) {
+        if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate
+                && !forceReload) {
             return;
         }
 
@@ -309,6 +309,16 @@
             } catch (NumberFormatException e) {
                 Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName());
             }
+        } else if (!mIsMonetEnabled && systemPalette != null) {
+            try {
+                // It's possible that we flipped the flag off and still have a @ColorInt in the
+                // setting. We need to sanitize the input, otherwise the overlay transaction will
+                // fail.
+                Integer.parseInt(systemPalette.getPackageName().toLowerCase(), 16);
+                categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+            } catch (NumberFormatException e) {
+                // This is a package name. All good, let's continue
+            }
         }
 
         // Same for accent color.
@@ -322,6 +332,13 @@
             } catch (NumberFormatException e) {
                 Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName());
             }
+        } else if (!mIsMonetEnabled && accentPalette != null) {
+            try {
+                Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16);
+                categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
+            } catch (NumberFormatException e) {
+                // This is a package name. All good, let's continue
+            }
         }
 
         // Compatibility with legacy themes, where full packages were defined, instead of just
@@ -337,10 +354,10 @@
             categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, mSecondaryOverlay.getIdentifier());
         }
 
-        Set<UserHandle> userHandles = Sets.newHashSet(UserHandle.of(currentUser));
+        Set<UserHandle> managedProfiles = new HashSet<>();
         for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) {
             if (userInfo.isManagedProfile()) {
-                userHandles.add(userInfo.getUserHandle());
+                managedProfiles.add(userInfo.getUserHandle());
             }
         }
         if (DEBUG) {
@@ -352,9 +369,10 @@
             mNeedsOverlayCreation = false;
             mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
                     mPrimaryOverlay, mSecondaryOverlay, mNeutralOverlay
-            }, userHandles);
+            }, currentUser, managedProfiles);
         } else {
-            mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, userHandles);
+            mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
+                    managedProfiles);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index df54eab..25345d5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -33,7 +33,11 @@
 
 import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.Dialog;
@@ -51,6 +55,9 @@
 import android.graphics.PixelFormat;
 import android.graphics.Region;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.RotateDrawable;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.os.Debug;
@@ -81,6 +88,8 @@
 import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 import android.widget.TextView;
@@ -88,6 +97,7 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
@@ -99,6 +109,8 @@
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.AlphaTintDrawableWrapper;
+import com.android.systemui.util.RoundedCornerProgressDrawable;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -124,8 +136,14 @@
     static final int DIALOG_ODI_CAPTIONS_TOOLTIP_TIMEOUT_MILLIS = 5000;
     static final int DIALOG_HOVERING_TIMEOUT_MILLIS = 16000;
 
+    private static final int DRAWER_ANIMATION_DURATION_SHORT = 175;
+    private static final int DRAWER_ANIMATION_DURATION = 250;
+
     private final int mDialogShowAnimationDurationMs;
     private final int mDialogHideAnimationDurationMs;
+    private final int mRingerDrawerItemSize;
+    private final boolean mShowVibrate;
+    private final int mRingerCount;
     private final boolean mShowLowMediaVolumeIcon;
     private final boolean mChangeVolumeRowTintWhenInactive;
 
@@ -140,6 +158,30 @@
     private ViewGroup mDialogView;
     private ViewGroup mDialogRowsView;
     private ViewGroup mRinger;
+
+    private ViewGroup mSelectedRingerContainer;
+    private ImageView mSelectedRingerIcon;
+
+    private ViewGroup mRingerDrawerContainer;
+    private ViewGroup mRingerDrawerMute;
+    private ViewGroup mRingerDrawerVibrate;
+    private ViewGroup mRingerDrawerNormal;
+    private ImageView mRingerDrawerMuteIcon;
+    private ImageView mRingerDrawerVibrateIcon;
+    private ImageView mRingerDrawerNormalIcon;
+
+    /**
+     * View that draws the 'selected' background behind one of the three ringer choices in the
+     * drawer.
+     */
+    private ViewGroup mRingerDrawerNewSelectionBg;
+
+    private final ValueAnimator mRingerDrawerIconColorAnimator = ValueAnimator.ofFloat(0f, 1f);
+    private ImageView mRingerDrawerIconAnimatingSelected;
+    private ImageView mRingerDrawerIconAnimatingDeselected;
+
+    private boolean mIsRingerDrawerOpen = false;
+
     private ImageButton mRingerIcon;
     private ViewGroup mODICaptionsView;
     private CaptionsToggleImageButton mODICaptionsIcon;
@@ -191,6 +233,12 @@
             mContext.getResources().getInteger(R.integer.config_dialogShowAnimationDurationMs);
         mDialogHideAnimationDurationMs =
             mContext.getResources().getInteger(R.integer.config_dialogHideAnimationDurationMs);
+        mRingerDrawerItemSize = mContext.getResources().getDimensionPixelSize(
+                R.dimen.volume_ringer_drawer_item_size);
+        mShowVibrate = mController.hasVibrator();
+
+        // Normal, mute, and possibly vibrate.
+        mRingerCount = mShowVibrate ? 3 : 2;
     }
 
     @Override
@@ -314,6 +362,20 @@
             mZenIcon = mRinger.findViewById(R.id.dnd_icon);
         }
 
+        mSelectedRingerIcon = mDialog.findViewById(R.id.volume_new_ringer_active_icon);
+        mSelectedRingerContainer = mDialog.findViewById(
+                R.id.volume_new_ringer_active_icon_container);
+
+        mRingerDrawerMute = mDialog.findViewById(R.id.volume_drawer_mute);
+        mRingerDrawerNormal = mDialog.findViewById(R.id.volume_drawer_normal);
+        mRingerDrawerVibrate = mDialog.findViewById(R.id.volume_drawer_vibrate);
+        mRingerDrawerMuteIcon = mDialog.findViewById(R.id.volume_drawer_mute_icon);
+        mRingerDrawerVibrateIcon = mDialog.findViewById(R.id.volume_drawer_vibrate_icon);
+        mRingerDrawerNormalIcon = mDialog.findViewById(R.id.volume_drawer_normal_icon);
+        mRingerDrawerNewSelectionBg = mDialog.findViewById(R.id.volume_drawer_selection_background);
+
+        setupRingerDrawer();
+
         mODICaptionsView = mDialog.findViewById(R.id.odi_captions);
         if (mODICaptionsView != null) {
             mODICaptionsIcon = mODICaptionsView.findViewById(R.id.odi_captions_icon);
@@ -475,40 +537,275 @@
 
         row.anim = null;
 
+        final LayerDrawable seekbarDrawable =
+                (LayerDrawable) mContext.getDrawable(R.drawable.volume_row_seekbar);
+
+        final LayerDrawable seekbarBgDrawable =
+                (LayerDrawable) seekbarDrawable.findDrawableByLayerId(android.R.id.background);
+
+        row.sliderBgSolid = seekbarBgDrawable.findDrawableByLayerId(
+                R.id.volume_seekbar_background_solid);
+
+        row.sliderBgIcon = (AlphaTintDrawableWrapper)
+                ((RotateDrawable) seekbarBgDrawable.findDrawableByLayerId(
+                        R.id.volume_seekbar_background_icon)).getDrawable();
+
+        final LayerDrawable seekbarProgressDrawable = (LayerDrawable)
+                ((RoundedCornerProgressDrawable) seekbarDrawable.findDrawableByLayerId(
+                        android.R.id.progress)).getDrawable();
+
+        row.sliderProgressSolid = seekbarProgressDrawable.findDrawableByLayerId(
+                R.id.volume_seekbar_progress_solid);
+
+        row.sliderProgressIcon = (AlphaTintDrawableWrapper)
+                ((RotateDrawable) seekbarProgressDrawable.findDrawableByLayerId(
+                        R.id.volume_seekbar_progress_icon)).getDrawable();
+
+        row.slider.setProgressDrawable(seekbarDrawable);
+        row.slider.setThumb(null);
+
         row.icon = row.view.findViewById(R.id.volume_row_icon);
-        row.icon.setImageResource(iconRes);
-        if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
-            row.icon.setOnClickListener(v -> {
-                Events.writeEvent(Events.EVENT_ICON_CLICK, row.stream, row.iconState);
-                mController.setActiveStream(row.stream);
-                if (row.stream == AudioManager.STREAM_RING) {
-                    final boolean hasVibrator = mController.hasVibrator();
-                    if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
-                        if (hasVibrator) {
-                            mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
+
+        row.setIcon(iconRes, mContext.getTheme());
+
+        if (row.icon != null) {
+            if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
+                row.icon.setOnClickListener(v -> {
+                    Events.writeEvent(Events.EVENT_ICON_CLICK, row.stream, row.iconState);
+                    mController.setActiveStream(row.stream);
+                    if (row.stream == AudioManager.STREAM_RING) {
+                        final boolean hasVibrator = mController.hasVibrator();
+                        if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
+                            if (hasVibrator) {
+                                mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
+                            } else {
+                                final boolean wasZero = row.ss.level == 0;
+                                mController.setStreamVolume(stream,
+                                        wasZero ? row.lastAudibleLevel : 0);
+                            }
                         } else {
-                            final boolean wasZero = row.ss.level == 0;
-                            mController.setStreamVolume(stream,
-                                    wasZero ? row.lastAudibleLevel : 0);
+                            mController.setRingerMode(
+                                    AudioManager.RINGER_MODE_NORMAL, false);
+                            if (row.ss.level == 0) {
+                                mController.setStreamVolume(stream, 1);
+                            }
                         }
                     } else {
-                        mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
-                        if (row.ss.level == 0) {
-                            mController.setStreamVolume(stream, 1);
-                        }
+                        final boolean vmute = row.ss.level == row.ss.levelMin;
+                        mController.setStreamVolume(stream,
+                                vmute ? row.lastAudibleLevel : row.ss.levelMin);
                     }
-                } else {
-                    final boolean vmute = row.ss.level == row.ss.levelMin;
-                    mController.setStreamVolume(stream,
-                            vmute ? row.lastAudibleLevel : row.ss.levelMin);
-                }
-                row.userAttempt = 0;  // reset the grace period, slider updates immediately
-            });
-        } else {
-            row.icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+                    row.userAttempt = 0;  // reset the grace period, slider updates immediately
+                });
+            } else {
+                row.icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+            }
         }
     }
 
+    private void setRingerMode(int newRingerMode) {
+        Events.writeEvent(Events.EVENT_RINGER_TOGGLE, newRingerMode);
+        incrementManualToggleCount();
+        updateRingerH();
+        provideTouchFeedbackH(newRingerMode);
+        mController.setRingerMode(newRingerMode, false);
+        maybeShowToastH(newRingerMode);
+    }
+
+    private void setupRingerDrawer() {
+        mRingerDrawerContainer = mDialog.findViewById(R.id.volume_drawer_container);
+
+        if (mRingerDrawerContainer == null) {
+            return;
+        }
+
+        if (!mShowVibrate) {
+            mRingerDrawerVibrate.setVisibility(GONE);
+        }
+
+        // In portrait, add padding to the bottom to account for the height of the open ringer
+        // drawer.
+        if (!isLandscape()) {
+            mDialogView.setPadding(
+                    mDialogView.getPaddingLeft(),
+                    mDialogView.getPaddingTop(),
+                    mDialogView.getPaddingRight(),
+                    mDialogView.getPaddingBottom() + (mRingerCount - 1) * mRingerDrawerItemSize);
+        } else {
+            mDialogView.setPadding(
+                    mDialogView.getPaddingLeft() + (mRingerCount - 1) * mRingerDrawerItemSize,
+                    mDialogView.getPaddingTop(),
+                    mDialogView.getPaddingRight(),
+                    mDialogView.getPaddingBottom());
+        }
+
+        ((LinearLayout) mRingerDrawerContainer.findViewById(R.id.volume_drawer_options))
+                .setOrientation(isLandscape() ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
+
+        mSelectedRingerContainer.setOnClickListener(view -> {
+            if (mIsRingerDrawerOpen) {
+                hideRingerDrawer();
+            } else {
+                showRingerDrawer();
+            }
+        });
+
+        mRingerDrawerVibrate.setOnClickListener(
+                new RingerDrawerItemClickListener(RINGER_MODE_VIBRATE));
+        mRingerDrawerMute.setOnClickListener(
+                new RingerDrawerItemClickListener(RINGER_MODE_SILENT));
+        mRingerDrawerNormal.setOnClickListener(
+                new RingerDrawerItemClickListener(RINGER_MODE_NORMAL));
+
+        final int unselectedColor = Utils.getColorAccentDefaultColor(mContext);
+        final int selectedColor = Utils.getColorAttrDefaultColor(
+                mContext, android.R.attr.colorBackgroundFloating);
+
+        // Add an update listener that animates the deselected icon to the unselected color, and the
+        // selected icon to the selected color.
+        mRingerDrawerIconColorAnimator.addUpdateListener(
+                anim -> {
+                    final float currentValue = (float) anim.getAnimatedValue();
+                    final int curUnselectedColor = (int) ArgbEvaluator.getInstance().evaluate(
+                            currentValue, selectedColor, unselectedColor);
+                    final int curSelectedColor = (int) ArgbEvaluator.getInstance().evaluate(
+                            currentValue, unselectedColor, selectedColor);
+
+                    mRingerDrawerIconAnimatingDeselected.setColorFilter(curUnselectedColor);
+                    mRingerDrawerIconAnimatingSelected.setColorFilter(curSelectedColor);
+                });
+        mRingerDrawerIconColorAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mRingerDrawerIconAnimatingDeselected.clearColorFilter();
+                mRingerDrawerIconAnimatingSelected.clearColorFilter();
+            }
+        });
+        mRingerDrawerIconColorAnimator.setDuration(DRAWER_ANIMATION_DURATION_SHORT);
+    }
+
+    private ImageView getDrawerIconViewForMode(int mode) {
+        if (mode == RINGER_MODE_VIBRATE) {
+            return mRingerDrawerVibrateIcon;
+        } else if (mode == RINGER_MODE_SILENT) {
+            return mRingerDrawerMuteIcon;
+        } else {
+            return mRingerDrawerNormalIcon;
+        }
+    }
+
+    /**
+     * Translation to apply form the origin (either top or left) to overlap the selection background
+     * with the given mode in the drawer.
+     */
+    private float getTranslationInDrawerForRingerMode(int mode) {
+        return mode == RINGER_MODE_VIBRATE
+                ? -mRingerDrawerItemSize * 2
+                : mode == RINGER_MODE_SILENT
+                        ? -mRingerDrawerItemSize
+                        : 0;
+    }
+
+    /** Animates in the ringer drawer. */
+    private void showRingerDrawer() {
+        // Show all ringer icons except the currently selected one, since we're going to animate the
+        // ringer button to that position.
+        mRingerDrawerVibrateIcon.setVisibility(
+                mState.ringerModeInternal == RINGER_MODE_VIBRATE ? INVISIBLE : VISIBLE);
+        mRingerDrawerMuteIcon.setVisibility(
+                mState.ringerModeInternal == RINGER_MODE_SILENT ? INVISIBLE : VISIBLE);
+        mRingerDrawerNormalIcon.setVisibility(
+                mState.ringerModeInternal == RINGER_MODE_NORMAL ? INVISIBLE : VISIBLE);
+
+        // Hide the selection background - we use this to show a selection when one is
+        // tapped, so it should be invisible until that happens. However, position it below
+        // the currently selected ringer so that it's ready to animate.
+        mRingerDrawerNewSelectionBg.setAlpha(0f);
+
+        if (!isLandscape()) {
+            mRingerDrawerNewSelectionBg.setTranslationY(
+                    getTranslationInDrawerForRingerMode(mState.ringerModeInternal));
+        } else {
+            mRingerDrawerNewSelectionBg.setTranslationX(
+                    getTranslationInDrawerForRingerMode(mState.ringerModeInternal));
+        }
+
+        // Move the drawer so that the top/rightmost ringer choice overlaps with the selected ringer
+        // icon.
+        if (!isLandscape()) {
+            mRingerDrawerContainer.setTranslationY(mRingerDrawerItemSize * (mRingerCount - 1));
+        } else {
+            mRingerDrawerContainer.setTranslationX(mRingerDrawerItemSize * (mRingerCount - 1));
+        }
+        mRingerDrawerContainer.setAlpha(0f);
+        mRingerDrawerContainer.setVisibility(VISIBLE);
+
+        // Animate the drawer up and visible.
+        mRingerDrawerContainer.animate()
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                // Vibrate is way farther up, so give the selected ringer icon a head start if
+                // vibrate is selected.
+                .setDuration(mState.ringerModeInternal == RINGER_MODE_VIBRATE
+                        ? DRAWER_ANIMATION_DURATION_SHORT
+                        : DRAWER_ANIMATION_DURATION)
+                .setStartDelay(mState.ringerModeInternal == RINGER_MODE_VIBRATE
+                        ? DRAWER_ANIMATION_DURATION - DRAWER_ANIMATION_DURATION_SHORT
+                        : 0)
+                .alpha(1f)
+                .translationX(0f)
+                .translationY(0f)
+                .start();
+
+        // Animate the selected ringer view up to that ringer's position in the drawer.
+        mSelectedRingerContainer.animate()
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .setDuration(DRAWER_ANIMATION_DURATION)
+                .withEndAction(() ->
+                        getDrawerIconViewForMode(mState.ringerModeInternal).setVisibility(VISIBLE));
+
+        if (!isLandscape()) {
+            mSelectedRingerContainer.animate()
+                    .translationY(getTranslationInDrawerForRingerMode(mState.ringerModeInternal))
+                    .start();
+        } else {
+            mSelectedRingerContainer.animate()
+                    .translationX(getTranslationInDrawerForRingerMode(mState.ringerModeInternal))
+                    .start();
+        }
+
+        mIsRingerDrawerOpen = true;
+    }
+
+    /** Animates away the ringer drawer. */
+    private void hideRingerDrawer() {
+        // Hide the drawer icon for the selected ringer - it's visible in the ringer button and we
+        // don't want to be able to see it while it animates away.
+        getDrawerIconViewForMode(mState.ringerModeInternal).setVisibility(INVISIBLE);
+
+        mRingerDrawerContainer.animate()
+                .alpha(0f)
+                .setDuration(DRAWER_ANIMATION_DURATION)
+                .setStartDelay(0)
+                .withEndAction(() -> mRingerDrawerContainer.setVisibility(INVISIBLE));
+
+        if (!isLandscape()) {
+            mRingerDrawerContainer.animate()
+                    .translationY(mRingerDrawerItemSize * 2)
+                    .start();
+        } else {
+            mRingerDrawerContainer.animate()
+                    .translationX(mRingerDrawerItemSize * 2)
+                    .start();
+        }
+
+        mSelectedRingerContainer.animate()
+                .translationX(0f)
+                .translationY(0f)
+                .start();
+
+        mIsRingerDrawerOpen = false;
+    }
+
     public void initSettingsH() {
         if (mSettingsView != null) {
             mSettingsView.setVisibility(
@@ -555,12 +852,8 @@
                         mController.setStreamVolume(AudioManager.STREAM_RING, 1);
                     }
                 }
-                Events.writeEvent(Events.EVENT_RINGER_TOGGLE, newRingerMode);
-                incrementManualToggleCount();
-                updateRingerH();
-                provideTouchFeedbackH(newRingerMode);
-                mController.setRingerMode(newRingerMode, false);
-                maybeShowToastH(newRingerMode);
+
+                setRingerMode(newRingerMode);
             });
         }
         updateRingerH();
@@ -809,6 +1102,8 @@
                     mDialog.dismiss();
                     tryToRemoveCaptionsTooltip();
                     mIsAnimatingDismiss = false;
+
+                    hideRingerDrawer();
                 }, 50));
         if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2.0f);
         animator.start();
@@ -889,12 +1184,14 @@
             switch (mState.ringerModeInternal) {
                 case AudioManager.RINGER_MODE_VIBRATE:
                     mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
+                    mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
                     addAccessibilityDescription(mRingerIcon, RINGER_MODE_VIBRATE,
                             mContext.getString(R.string.volume_ringer_hint_mute));
                     mRingerIcon.setTag(Events.ICON_STATE_VIBRATE);
                     break;
                 case AudioManager.RINGER_MODE_SILENT:
                     mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+                    mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
                     mRingerIcon.setTag(Events.ICON_STATE_MUTE);
                     addAccessibilityDescription(mRingerIcon, RINGER_MODE_SILENT,
                             mContext.getString(R.string.volume_ringer_hint_unmute));
@@ -904,11 +1201,13 @@
                     boolean muted = (mAutomute && ss.level == 0) || ss.muted;
                     if (!isZenMuted && muted) {
                         mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+                        mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
                         addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
                                 mContext.getString(R.string.volume_ringer_hint_unmute));
                         mRingerIcon.setTag(Events.ICON_STATE_MUTE);
                     } else {
                         mRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
+                        mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
                         if (mController.hasVibrator()) {
                             addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
                                     mContext.getString(R.string.volume_ringer_hint_vibrate));
@@ -1075,8 +1374,6 @@
 
         // update icon
         final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted;
-        row.icon.setEnabled(iconEnabled);
-        row.icon.setAlpha(iconEnabled ? 1 : 0.5f);
         final int iconRes;
         if (isRingVibrate) {
             iconRes = R.drawable.ic_volume_ringer_vibrate;
@@ -1092,7 +1389,7 @@
                       ? R.drawable.ic_volume_media_low : row.iconRes;
         }
 
-        row.icon.setImageResource(iconRes);
+        row.setIcon(iconRes, mContext.getTheme());
         row.iconState =
                 iconRes == R.drawable.ic_volume_ringer_vibrate ? Events.ICON_STATE_VIBRATE
                 : (iconRes == R.drawable.ic_volume_media_bt_mute || iconRes == row.iconMuteRes)
@@ -1101,18 +1398,35 @@
                         || iconRes == R.drawable.ic_volume_media_low)
                         ? Events.ICON_STATE_UNMUTE
                 : Events.ICON_STATE_UNKNOWN;
-        if (iconEnabled) {
-            if (isRingStream) {
-                if (isRingVibrate) {
-                    row.icon.setContentDescription(mContext.getString(
-                            R.string.volume_stream_content_description_unmute,
-                            getStreamLabelH(ss)));
-                } else {
-                    if (mController.hasVibrator()) {
+
+        if (row.icon != null) {
+            if (iconEnabled) {
+                if (isRingStream) {
+                    if (isRingVibrate) {
                         row.icon.setContentDescription(mContext.getString(
-                                mShowA11yStream
-                                        ? R.string.volume_stream_content_description_vibrate_a11y
-                                        : R.string.volume_stream_content_description_vibrate,
+                                R.string.volume_stream_content_description_unmute,
+                                getStreamLabelH(ss)));
+                    } else {
+                        if (mController.hasVibrator()) {
+                            row.icon.setContentDescription(mContext.getString(
+                                    mShowA11yStream
+                                            ? R.string.volume_stream_content_description_vibrate_a11y
+                                            : R.string.volume_stream_content_description_vibrate,
+                                    getStreamLabelH(ss)));
+                        } else {
+                            row.icon.setContentDescription(mContext.getString(
+                                    mShowA11yStream
+                                            ? R.string.volume_stream_content_description_mute_a11y
+                                            : R.string.volume_stream_content_description_mute,
+                                    getStreamLabelH(ss)));
+                        }
+                    }
+                } else if (isA11yStream) {
+                    row.icon.setContentDescription(getStreamLabelH(ss));
+                } else {
+                    if (ss.muted || mAutomute && ss.level == 0) {
+                        row.icon.setContentDescription(mContext.getString(
+                                R.string.volume_stream_content_description_unmute,
                                 getStreamLabelH(ss)));
                     } else {
                         row.icon.setContentDescription(mContext.getString(
@@ -1122,23 +1436,9 @@
                                 getStreamLabelH(ss)));
                     }
                 }
-            } else if (isA11yStream) {
-                row.icon.setContentDescription(getStreamLabelH(ss));
             } else {
-                if (ss.muted || mAutomute && ss.level == 0) {
-                   row.icon.setContentDescription(mContext.getString(
-                           R.string.volume_stream_content_description_unmute,
-                           getStreamLabelH(ss)));
-                } else {
-                    row.icon.setContentDescription(mContext.getString(
-                            mShowA11yStream
-                                    ? R.string.volume_stream_content_description_mute_a11y
-                                    : R.string.volume_stream_content_description_mute,
-                            getStreamLabelH(ss)));
-                }
+                row.icon.setContentDescription(getStreamLabelH(ss));
             }
-        } else {
-            row.icon.setContentDescription(getStreamLabelH(ss));
         }
 
         // ensure tracking is disabled if zenMuted
@@ -1167,22 +1467,29 @@
         if (!useActiveColoring && !mChangeVolumeRowTintWhenInactive) {
             return;
         }
-        final ColorStateList tint = useActiveColoring
+        final ColorStateList colorTint = useActiveColoring
                 ? Utils.getColorAccent(mContext)
                 : Utils.getColorAttr(mContext, android.R.attr.colorForeground);
         final int alpha = useActiveColoring
-                ? Color.alpha(tint.getDefaultColor())
+                ? Color.alpha(colorTint.getDefaultColor())
                 : getAlphaAttr(android.R.attr.secondaryContentAlpha);
-        if (tint == row.cachedTint) return;
-        row.slider.setProgressTintList(tint);
-        row.slider.setThumbTintList(tint);
-        row.slider.setProgressBackgroundTintList(tint);
-        row.slider.setAlpha(((float) alpha) / 255);
-        row.icon.setImageTintList(tint);
-        row.icon.setImageAlpha(alpha);
-        row.cachedTint = tint;
+
+        final ColorStateList bgTint = Utils.getColorAttr(
+                mContext, android.R.attr.colorBackgroundFloating);
+
+        row.sliderProgressSolid.setTintList(colorTint);
+        row.sliderBgIcon.setTintList(colorTint);
+
+        row.sliderBgSolid.setTintList(bgTint);
+        row.sliderProgressIcon.setTintList(bgTint);
+
+        if (row.icon != null) {
+            row.icon.setImageTintList(colorTint);
+            row.icon.setImageAlpha(alpha);
+        }
+
         if (row.number != null) {
-            row.number.setTextColor(tint);
+            row.number.setTextColor(colorTint);
             row.number.setAlpha(alpha);
         }
     }
@@ -1538,6 +1845,10 @@
         private View view;
         private TextView header;
         private ImageButton icon;
+        private Drawable sliderBgSolid;
+        private AlphaTintDrawableWrapper sliderBgIcon;
+        private Drawable sliderProgressSolid;
+        private AlphaTintDrawableWrapper sliderProgressIcon;
         private SeekBar slider;
         private TextView number;
         private int stream;
@@ -1555,5 +1866,69 @@
         private int animTargetProgress;
         private int lastAudibleLevel = 1;
         private FrameLayout dndIcon;
+
+        void setIcon(int iconRes, Resources.Theme theme) {
+            if (icon != null) {
+                icon.setImageResource(iconRes);
+            }
+
+            sliderProgressIcon.setDrawable(view.getResources().getDrawable(iconRes, theme));
+            sliderBgIcon.setDrawable(view.getResources().getDrawable(iconRes, theme));
+        }
+    }
+
+    /**
+     * Click listener added to each ringer option in the drawer. This will initiate the animation to
+     * select and then close the ringer drawer, and actually change the ringer mode.
+     */
+    private class RingerDrawerItemClickListener implements View.OnClickListener {
+        private final int mClickedRingerMode;
+
+        RingerDrawerItemClickListener(int clickedRingerMode) {
+            mClickedRingerMode = clickedRingerMode;
+        }
+
+        @Override
+        public void onClick(View view) {
+            setRingerMode(mClickedRingerMode);
+
+            mRingerDrawerIconAnimatingSelected = getDrawerIconViewForMode(mClickedRingerMode);
+            mRingerDrawerIconAnimatingDeselected = getDrawerIconViewForMode(
+                    mState.ringerModeInternal);
+
+            // Begin switching the selected icon and deselected icon colors since the background is
+            // going to animate behind the new selection.
+            mRingerDrawerIconColorAnimator.start();
+
+            mSelectedRingerContainer.setVisibility(View.INVISIBLE);
+            mRingerDrawerNewSelectionBg.setAlpha(1f);
+            mRingerDrawerNewSelectionBg.animate()
+                    .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
+                    .setDuration(DRAWER_ANIMATION_DURATION_SHORT)
+                    .withEndAction(() -> {
+                        mRingerDrawerNewSelectionBg.setAlpha(0f);
+
+                        if (!isLandscape()) {
+                            mSelectedRingerContainer.setTranslationY(
+                                    getTranslationInDrawerForRingerMode(mClickedRingerMode));
+                        } else {
+                            mSelectedRingerContainer.setTranslationX(
+                                    getTranslationInDrawerForRingerMode(mClickedRingerMode));
+                        }
+
+                        mSelectedRingerContainer.setVisibility(VISIBLE);
+                        hideRingerDrawer();
+                    });
+
+            if (!isLandscape()) {
+                mRingerDrawerNewSelectionBg.animate()
+                        .translationY(getTranslationInDrawerForRingerMode(mClickedRingerMode))
+                        .start();
+            } else {
+                mRingerDrawerNewSelectionBg.animate()
+                        .translationX(getTranslationInDrawerForRingerMode(mClickedRingerMode))
+                        .start();
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 5d94659..4611fe03 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -31,6 +31,7 @@
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.R;
 import com.android.systemui.dagger.WMComponent;
 import com.android.systemui.dagger.WMSingleton;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -97,7 +98,12 @@
 @Module
 public abstract class WMShellBaseModule {
 
-    private static final boolean ENABLE_SHELL_MAIN_THREAD = false;
+    /**
+     * Returns whether to enable a separate shell thread for the shell features.
+     */
+    private static boolean enableShellMainThread(Context context) {
+        return context.getResources().getBoolean(R.bool.config_enableShellMainThread);
+    }
 
     //
     // Shell Concurrency - Components used for managing threading in the Shell and SysUI
@@ -120,8 +126,8 @@
     @WMSingleton
     @Provides
     @ShellMainThread
-    public static Handler provideShellMainHandler(@Main Handler sysuiMainHandler) {
-        if (ENABLE_SHELL_MAIN_THREAD) {
+    public static Handler provideShellMainHandler(Context context, @Main Handler sysuiMainHandler) {
+        if (enableShellMainThread(context)) {
              HandlerThread mainThread = new HandlerThread("wmshell.main");
              mainThread.start();
              return mainThread.getThreadHandler();
@@ -135,9 +141,9 @@
     @WMSingleton
     @Provides
     @ShellMainThread
-    public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler mainHandler,
-            @Main ShellExecutor sysuiMainExecutor) {
-        if (ENABLE_SHELL_MAIN_THREAD) {
+    public static ShellExecutor provideShellMainExecutor(Context context,
+            @ShellMainThread Handler mainHandler, @Main ShellExecutor sysuiMainExecutor) {
+        if (enableShellMainThread(context)) {
             return new HandlerExecutor(mainHandler);
         }
         return sysuiMainExecutor;
@@ -409,10 +415,11 @@
             ShellTaskOrganizer shellTaskOrganizer,
             SyncTransactionQueue syncQueue, Context context,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
-            @ShellMainThread ShellExecutor mainExecutor) {
+            @ShellMainThread ShellExecutor mainExecutor,
+            DisplayImeController displayImeController) {
         if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
             return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
-                    rootTaskDisplayAreaOrganizer, mainExecutor));
+                    rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController));
         } else {
             return Optional.empty();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 997b488..754b6a6 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -104,9 +104,10 @@
     @Provides
     static AppPairsController provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
             SyncTransactionQueue syncQueue, DisplayController displayController,
-            @ShellMainThread ShellExecutor mainExecutor) {
+            @ShellMainThread ShellExecutor mainExecutor,
+            DisplayImeController displayImeController) {
         return new AppPairsController(shellTaskOrganizer, syncQueue, displayController,
-                mainExecutor);
+                mainExecutor, displayImeController);
     }
 
     //
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 14b4d02..854be1f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -26,16 +26,11 @@
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.testing.TestableResources;
-import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowInsetsController;
-import android.widget.FrameLayout;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
@@ -50,21 +45,12 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper()
 public class KeyguardSecurityContainerTest extends SysuiTestCase {
-    private static final int SCREEN_WIDTH = 1600;
-    private static final int FAKE_MEASURE_SPEC =
-            View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH, View.MeasureSpec.EXACTLY);
-
-    private static final SecurityMode ONE_HANDED_SECURITY_MODE = SecurityMode.PIN;
-    private static final SecurityMode TWO_HANDED_SECURITY_MODE = SecurityMode.Password;
-
-
 
     @Rule
     public MockitoRule mRule = MockitoJUnit.rule();
 
     @Mock
     private WindowInsetsController mWindowInsetsController;
-
     @Mock
     private KeyguardSecurityViewFlipper mSecurityViewFlipper;
 
@@ -72,18 +58,9 @@
 
     @Before
     public void setup() {
-        // Needed here, otherwise when mKeyguardSecurityContainer is created below, it'll cache
-        // the real references (rather than the TestableResources that this call creates).
-        mContext.ensureTestableResources();
-        FrameLayout.LayoutParams securityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
-
         when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
-        when(mSecurityViewFlipper.getLayoutParams()).thenReturn(securityViewFlipperLayoutParams);
         mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
         mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
-        mKeyguardSecurityContainer.addView(mSecurityViewFlipper, new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
     }
 
     @Test
@@ -92,75 +69,4 @@
         verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
                 any(), any());
     }
-
-    @Test
-    public void onMeasure_usesFullWidthWithoutOneHandedMode() {
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */false,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ false,
-                ONE_HANDED_SECURITY_MODE);
-
-        mKeyguardSecurityContainer.onMeasure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-
-        verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-    }
-
-    @Test
-    public void onMeasure_usesFullWidthWithDeviceFlagDisabled() {
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */false,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ true,
-                ONE_HANDED_SECURITY_MODE);
-
-        mKeyguardSecurityContainer.onMeasure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-        verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-    }
-
-    @Test
-    public void onMeasure_usesFullWidthWithSysUIFlagDisabled() {
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */true,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ false,
-                ONE_HANDED_SECURITY_MODE);
-
-        mKeyguardSecurityContainer.onMeasure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-        verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-    }
-
-    @Test
-    public void onMeasure_usesHalfWidthWithFlagsEnabled() {
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */true,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ true,
-                ONE_HANDED_SECURITY_MODE);
-
-        int halfWidthMeasureSpec =
-                View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY);
-        mKeyguardSecurityContainer.onMeasure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-
-        verify(mSecurityViewFlipper).measure(halfWidthMeasureSpec, FAKE_MEASURE_SPEC);
-    }
-
-    @Test
-    public void onMeasure_usesFullWidthForFullScreenIme() {
-        setUpKeyguard(
-                /* deviceConfigCanUseOneHandedKeyguard= */true,
-                /* sysuiResourceCanUseOneHandedKeyguard= */ true,
-                TWO_HANDED_SECURITY_MODE);
-
-        mKeyguardSecurityContainer.onMeasure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-        verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-    }
-
-    private void setUpKeyguard(
-            boolean deviceConfigCanUseOneHandedKeyguard,
-            boolean sysuiResourceCanUseOneHandedKeyguard,
-            SecurityMode securityMode) {
-        TestableResources testableResources = mContext.getOrCreateTestableResources();
-        testableResources.addOverride(com.android.internal.R.bool.config_enableOneHandedKeyguard,
-                deviceConfigCanUseOneHandedKeyguard);
-        testableResources.addOverride(R.bool.can_use_one_handed_bouncer,
-                sysuiResourceCanUseOneHandedKeyguard);
-        mKeyguardSecurityContainer.updateLayoutForSecurityMode(securityMode);
-    }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 97cb873..2526990 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.appops;
 
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 
 import static junit.framework.TestCase.assertFalse;
 
@@ -125,9 +125,9 @@
         when(mAudioManager.getActiveRecordingConfigurations())
                 .thenReturn(List.of(mPausedMockRecording));
 
-        when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA))
+        when(mSensorPrivacyController.isSensorBlocked(CAMERA))
                 .thenReturn(false);
-        when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA))
+        when(mSensorPrivacyController.isSensorBlocked(CAMERA))
                 .thenReturn(false);
 
         mController = new AppOpsControllerImpl(
@@ -505,7 +505,7 @@
         assertFalse(list.get(0).isDisabled());
 
         // Add a camera op, and disable the microphone. The camera op should be the only op returned
-        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, true);
+        mController.onSensorBlockedChanged(MICROPHONE, true);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
@@ -515,7 +515,7 @@
 
 
         // Re enable the microphone, and verify the op returns
-        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, false);
+        mController.onSensorBlockedChanged(MICROPHONE, false);
         mTestableLooper.processAllMessages();
 
         list = mController.getActiveAppOps();
@@ -538,7 +538,7 @@
         assertFalse(list.get(0).isDisabled());
 
         // Add an audio op, and disable the camera. The audio op should be the only op returned
-        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, true);
+        mController.onSensorBlockedChanged(CAMERA, true);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
@@ -547,7 +547,7 @@
         assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode());
 
         // Re enable the camera, and verify the op returns
-        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, false);
+        mController.onSensorBlockedChanged(CAMERA, false);
         mTestableLooper.processAllMessages();
 
         list = mController.getActiveAppOps();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index c8e9396..2a4b41c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -464,7 +464,7 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
                 .augmentTileFromNotification(mContext, tile, sbn);
@@ -482,7 +482,7 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_3, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
                 .augmentTileFromNotification(mContext, tile, sbn);
@@ -496,7 +496,7 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
                 .augmentTileFromVisibleNotifications(mContext, tile,
@@ -511,7 +511,7 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_4, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
                 .augmentTileFromVisibleNotifications(mContext, tile,
@@ -526,7 +526,7 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         List<PeopleSpaceTile> actualList = PeopleSpaceUtils
                 .augmentTilesFromVisibleNotifications(
@@ -545,13 +545,13 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(1)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         PeopleSpaceTile tile2 =
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_2, "userName2", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         List<PeopleSpaceTile> actualList = PeopleSpaceUtils
                 .augmentTilesFromVisibleNotifications(mContext, List.of(tile1, tile2),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 1c8324c..800d859 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -109,7 +109,7 @@
             new PeopleSpaceTile
                     .Builder(SHORTCUT_ID, "username", ICON, new Intent())
                     .setPackageName(TEST_PACKAGE_A)
-                    .setUid(0)
+                    .setUserHandle(new UserHandle(1))
                     .setNotificationKey(NOTIFICATION_KEY + "1")
                     .setNotificationContent(NOTIFICATION_CONTENT)
                     .setNotificationDataUri(URI)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index 2dfd388..d40e6a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -89,6 +89,8 @@
     private View mEdit;
     @Mock
     private MultiUserSwitch mMultiUserSwitch;
+    @Mock
+    private View mPowerMenuLiteView;
 
     private QSFooterViewController mController;
 
@@ -111,11 +113,12 @@
         when(mView.findViewById(R.id.build)).thenReturn(mBuildText);
         when(mView.findViewById(android.R.id.edit)).thenReturn(mEdit);
         when(mView.findViewById(R.id.multi_user_switch)).thenReturn(mMultiUserSwitch);
+        when(mView.findViewById(R.id.pm_lite)).thenReturn(mPowerMenuLiteView);
 
         mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
                 mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
                 new QSDetailDisplayer(), mQuickQSPanelController, mFakeTunerService,
-                mMetricsLogger);
+                mMetricsLogger, false);
 
         mController.init();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index 0dfebab..a2a179e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -121,7 +121,6 @@
                 mQSTileHost, mQSCustomizerController, true, mMediaHost,
                 mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
                 mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
-                /* labelsFlag */ false,
                 mFeatureFlags
         );
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 5870200..cb380d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -87,7 +87,6 @@
                 uiEventLogger,
                 qsLogger,
                 dumpManager,
-                false,
                 featureFlags
         )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
index a75c39c..9e62a62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
@@ -24,6 +24,7 @@
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RenderNode;
+import android.os.ICancellationSignal;
 import android.os.RemoteException;
 import android.view.IScrollCaptureCallbacks;
 import android.view.IScrollCaptureConnection;
@@ -46,7 +47,7 @@
     }
 
     @Override
-    public void startCapture(Surface surface) {
+    public ICancellationSignal startCapture(Surface surface) {
         mSurface = surface;
         mHwuiContext = new HwuiContext(false, surface);
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -56,27 +57,28 @@
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
+        return null;
     }
 
     @Override
-    public void requestImage(Rect rect) {
+    public ICancellationSignal requestImage(Rect rect) {
         Canvas canvas = mHwuiContext.lockCanvas(rect.width(), rect.height());
         mPaint.setColor(mColors[mNextColor]);
         canvas.drawRect(rect, mPaint);
         mNextColor = (mNextColor++) % mColors.length;
-        long frameNumber = mSurface.getNextFrameNumber();
         mHwuiContext.unlockAndPost(canvas);
         try {
-            mCallbacks.onCaptureBufferSent(frameNumber, rect);
+            mCallbacks.onImageRequestCompleted(0, rect);
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
+        return null;
     }
 
     @Override
-    public void endCapture() {
+    public ICancellationSignal endCapture() {
         try {
-            mCallbacks.onConnectionClosed();
+            mCallbacks.onCaptureEnded();
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         } finally {
@@ -84,6 +86,12 @@
             mSurface = null;
             mCallbacks = null;
         }
+        return null;
+    }
+
+    @Override
+    public void close() throws RemoteException {
+
     }
 
     // From android.view.Surface, but issues render requests synchronously with waitForPresent(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
index 580f800..802b462 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
@@ -29,7 +29,6 @@
 import static java.util.Objects.requireNonNull;
 
 import android.content.Context;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.RemoteException;
@@ -37,6 +36,7 @@
 import android.view.Display;
 import android.view.IScrollCaptureCallbacks;
 import android.view.IWindowManager;
+import android.view.ScrollCaptureResponse;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -82,10 +82,11 @@
     public void testBasicClientFlow() throws RemoteException {
         doAnswer((Answer<Void>) invocation -> {
             IScrollCaptureCallbacks cb = invocation.getArgument(3);
-            cb.onConnected(
-                    new FakeScrollCaptureConnection(cb),
-                    /* scrollBounds */ new Rect(0, 0, 100, 100),
-                    /* positionInWindow */ new Point(0, 0));
+            cb.onScrollCaptureResponse(new ScrollCaptureResponse.Builder()
+                    .setBoundsInWindow(new Rect(0, 0, 100, 100))
+                    .setWindowBounds(new Rect(0, 0, 100, 100))
+                    .setConnection(new FakeScrollCaptureConnection(cb))
+                    .build());
             return null;
         }).when(mWm).requestScrollCapture(/* displayId */ anyInt(), /* token */  isNull(),
                 /* taskId */ anyInt(), any(IScrollCaptureCallbacks.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
index 2b3ca7c..6564d58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
@@ -19,15 +19,14 @@
 import static org.junit.Assert.fail;
 
 import android.content.Intent;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
 import android.util.Log;
 import android.view.Display;
 import android.view.IScrollCaptureCallbacks;
-import android.view.IScrollCaptureConnection;
 import android.view.IWindowManager;
+import android.view.ScrollCaptureResponse;
 import android.view.WindowManagerGlobal;
 
 import androidx.test.filters.SmallTest;
@@ -67,31 +66,27 @@
             wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1,
                     new IScrollCaptureCallbacks.Stub() {
                         @Override
-                        public void onConnected(
-                                IScrollCaptureConnection connection, Rect scrollBounds,
-                                Point positionInWindow) {
-                            Log.d(TAG,
-                                    "client connected: " + connection + "[scrollBounds= "
-                                            + scrollBounds + ", "
-                                            + "positionInWindow=" + positionInWindow + "]");
+                        public void onScrollCaptureResponse(ScrollCaptureResponse response)
+                                throws RemoteException {
+                            Log.d(TAG, "onScrollCaptureResponse: " + response);
                             latch.countDown();
                         }
 
                         @Override
-                        public void onUnavailable() {
-                        }
-
-                        @Override
                         public void onCaptureStarted() {
                         }
 
                         @Override
-                        public void onCaptureBufferSent(long frameNumber, Rect capturedArea) {
+                        public void onImageRequestCompleted(int i, Rect rect)
+                                throws RemoteException {
+
                         }
 
                         @Override
-                        public void onConnectionClosed() {
+                        public void onCaptureEnded() throws RemoteException {
+
                         }
+
                     });
         } catch (RemoteException e) {
             Log.e(TAG, "request failed", e);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index ce0f122..9a5482c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -377,8 +377,6 @@
         // THEN we send the dismissal to system server
         verify(mStatusBarService).onNotificationClear(
                 notif2.sbn.getPackageName(),
-                notif2.sbn.getTag(),
-                88,
                 notif2.sbn.getUser().getIdentifier(),
                 notif2.sbn.getKey(),
                 stats.dismissalSurface,
@@ -528,8 +526,6 @@
         // THEN we never send the dismissal to system server
         verify(mStatusBarService, never()).onNotificationClear(
                 notif.sbn.getPackageName(),
-                notif.sbn.getTag(),
-                47,
                 notif.sbn.getUser().getIdentifier(),
                 notif.sbn.getKey(),
                 stats.dismissalSurface,
@@ -566,8 +562,6 @@
         // THEN the notification is never sent to system server to dismiss
         verify(mStatusBarService, never()).onNotificationClear(
                 eq(notif.sbn.getPackageName()),
-                eq(notif.sbn.getTag()),
-                eq(47),
                 eq(notif.sbn.getUser().getIdentifier()),
                 eq(notif.sbn.getKey()),
                 anyInt(),
@@ -596,8 +590,6 @@
         // THEN we send the dismissal to system server
         verify(mStatusBarService).onNotificationClear(
                 eq(notif.sbn.getPackageName()),
-                eq(notif.sbn.getTag()),
-                eq(47),
                 eq(notif.sbn.getUser().getIdentifier()),
                 eq(notif.sbn.getKey()),
                 anyInt(),
@@ -1125,8 +1117,6 @@
         // THEN we send the dismissals to system server
         verify(mStatusBarService).onNotificationClear(
                 notif1.sbn.getPackageName(),
-                notif1.sbn.getTag(),
-                47,
                 notif1.sbn.getUser().getIdentifier(),
                 notif1.sbn.getKey(),
                 stats1.dismissalSurface,
@@ -1135,8 +1125,6 @@
 
         verify(mStatusBarService).onNotificationClear(
                 notif2.sbn.getPackageName(),
-                notif2.sbn.getTag(),
-                88,
                 notif2.sbn.getUser().getIdentifier(),
                 notif2.sbn.getKey(),
                 stats2.dismissalSurface,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index 45828c3..6067b42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -23,6 +23,7 @@
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SETTINGS;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SYSUI;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_THEME_PICKER;
+import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_NEUTRAL_PALETTE;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SHAPE;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
 import static com.android.systemui.theme.ThemeOverlayApplier.SETTINGS_PACKAGE;
@@ -31,7 +32,6 @@
 import static com.android.systemui.theme.ThemeOverlayApplier.THEME_CATEGORIES;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
@@ -87,7 +87,9 @@
     private static final String THEMEPICKER_PACKAGE = "com.android.wallpaper";
     private static final String LAUNCHER_PACKAGE = "com.android.launcher3";
     private static final UserHandle TEST_USER = UserHandle.of(5);
-    private static final Set<UserHandle> TEST_USER_HANDLES = Sets.newHashSet(TEST_USER);
+    private static final UserHandle TEST_USER_MANAGED_PROFILE = UserHandle.of(6);
+    private static final Set<UserHandle> TEST_USER_HANDLES =
+            Sets.newHashSet(TEST_USER_MANAGED_PROFILE);
 
     @Mock
     OverlayManager mOverlayManager;
@@ -114,6 +116,8 @@
                                 ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, false),
                         createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE,
                                 ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, false),
+                        createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_NEUTRAL_PALETTE,
+                                ANDROID_PACKAGE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, false),
                         createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_FONT,
                                 ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, false),
                         createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SHAPE,
@@ -124,6 +128,8 @@
                                 ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, true),
                         createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE,
                                 ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, true),
+                        createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_NEUTRAL_PALETTE,
+                                ANDROID_PACKAGE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, true),
                         createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_FONT,
                                 ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, true),
                         createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SHAPE,
@@ -154,13 +160,19 @@
                                 THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, false),
                         createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER,
                                 THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, true)));
+
+        OverlayInfo launcherTargetInfo = new OverlayInfo("packageName", LAUNCHER_PACKAGE,
+                null, null, "/", 0, 0, 0, false);
+        when(mOverlayManager.getOverlayInfo(any(OverlayIdentifier.class), any()))
+                .thenReturn(launcherTargetInfo);
         clearInvocations(mOverlayManager);
         verify(mDumpManager).registerDumpable(any(), any());
     }
 
     @Test
     public void allCategoriesSpecified_allEnabledExclusively() {
-        mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES);
+        mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
+                TEST_USER_HANDLES);
         verify(mOverlayManager).commit(any());
 
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
@@ -171,7 +183,8 @@
 
     @Test
     public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() {
-        mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES);
+        mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
+                TEST_USER_HANDLES);
 
         for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) {
             if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) {
@@ -187,27 +200,25 @@
     @Test
     public void allCategoriesSpecified_enabledForAllUserHandles() {
         Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
-        UserHandle newUserHandle = UserHandle.of(10);
-        userHandles.add(newUserHandle);
-        mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, userHandles);
+        mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
+                userHandles);
 
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
             verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
                     eq(TEST_USER.getIdentifier()));
-            verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
-                    eq(newUserHandle.getIdentifier()));
+            // Not enabled for work profile because the target package is LAUNCHER_PACKAGE
+            verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true),
+                    eq(TEST_USER_MANAGED_PROFILE.getIdentifier()));
         }
     }
 
     @Test
     public void applyCurrentUserOverlays_createsPendingOverlays() {
-        Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
-        UserHandle newUserHandle = UserHandle.of(10);
-        userHandles.add(newUserHandle);
-        FabricatedOverlay[] pendingCreation = new FabricatedOverlay[] {
+        FabricatedOverlay[] pendingCreation = new FabricatedOverlay[]{
                 mock(FabricatedOverlay.class)
         };
-        mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation, userHandles);
+        mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation,
+                TEST_USER.getIdentifier(), TEST_USER_HANDLES);
 
         for (FabricatedOverlay overlay : pendingCreation) {
             verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay));
@@ -215,20 +226,13 @@
     }
 
     @Test
-    public void allCategoriesSpecified_overlayManagerNotQueried() {
-        mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES);
-
-        verify(mOverlayManager, never())
-                .getOverlayInfosForTarget(anyString(), any(UserHandle.class));
-    }
-
-    @Test
     public void someCategoriesSpecified_specifiedEnabled_unspecifiedDisabled() {
         Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
         categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS);
         categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID);
 
-        mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES);
+        mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
+                TEST_USER_HANDLES);
 
         for (OverlayIdentifier overlayPackage : categoryToPackage.values()) {
             verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
@@ -244,7 +248,8 @@
 
     @Test
     public void zeroCategoriesSpecified_allDisabled() {
-        mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER_HANDLES);
+        mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(),
+                TEST_USER_HANDLES);
 
         for (String category : THEME_CATEGORIES) {
             verify(mTransactionBuilder).setEnabled(
@@ -258,7 +263,8 @@
         Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
         categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category"));
 
-        mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES);
+        mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
+                TEST_USER_HANDLES);
 
         verify(mTransactionBuilder, never()).setEnabled(
                 eq(new OverlayIdentifier("com.example.blah.category")), eq(false),
@@ -268,23 +274,6 @@
                 eq(TEST_USER.getIdentifier()));
     }
 
-    @Test
-    public void overlayManagerOnlyQueriedForUnspecifiedPackages() {
-        Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
-        categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS);
-
-        mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES);
-
-        verify(mOverlayManager).getOverlayInfosForTarget(SETTINGS_PACKAGE, UserHandle.SYSTEM);
-        verify(mOverlayManager, never()).getOverlayInfosForTarget(ANDROID_PACKAGE,
-                UserHandle.SYSTEM);
-        verify(mOverlayManager, never()).getOverlayInfosForTarget(SYSUI_PACKAGE, UserHandle.SYSTEM);
-        verify(mOverlayManager, never()).getOverlayInfosForTarget(LAUNCHER_PACKAGE,
-                UserHandle.SYSTEM);
-        verify(mOverlayManager, never()).getOverlayInfosForTarget(THEMEPICKER_PACKAGE,
-                UserHandle.SYSTEM);
-    }
-
     private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName,
             String category, boolean enabled) {
         return new OverlayInfo(packageName, null, targetPackageName, null, category, "",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index aa385ef..d80c40f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -143,7 +143,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -175,7 +175,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -198,7 +198,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index c085689..3cea175 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -22,6 +22,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.app.KeyguardManager;
@@ -37,6 +38,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.plugins.VolumeDialogController.State;
@@ -58,6 +61,11 @@
 public class VolumeDialogImplTest extends SysuiTestCase {
 
     VolumeDialogImpl mDialog;
+    View mActiveRinger;
+    View mDrawerContainer;
+    View mDrawerVibrate;
+    View mDrawerMute;
+    View mDrawerNormal;
 
     @Mock
     VolumeDialogController mController;
@@ -80,6 +88,17 @@
         mDialog.init(0, null);
         State state = createShellState();
         mDialog.onStateChangedH(state);
+
+        mActiveRinger = mDialog.getDialogView().findViewById(
+                R.id.volume_new_ringer_active_icon_container);
+        mDrawerContainer = mDialog.getDialogView().findViewById(R.id.volume_drawer_container);
+        mDrawerVibrate = mDrawerContainer.findViewById(R.id.volume_drawer_vibrate);
+        mDrawerMute = mDrawerContainer.findViewById(R.id.volume_drawer_mute);
+        mDrawerNormal = mDrawerContainer.findViewById(R.id.volume_drawer_normal);
+
+        Prefs.putInt(mContext,
+                Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT,
+                VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1);
     }
 
     private State createShellState() {
@@ -207,6 +226,48 @@
         verify(mController, never()).vibrate(any());
     }
 
+    @Test
+    public void testSelectVibrateFromDrawer() {
+        final State initialUnsetState = new State();
+        initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
+        mDialog.onStateChangedH(initialUnsetState);
+
+        mActiveRinger.performClick();
+        mDrawerVibrate.performClick();
+
+        // Make sure we've actually changed the ringer mode.
+        verify(mController, times(1)).setRingerMode(
+                AudioManager.RINGER_MODE_VIBRATE, false);
+    }
+
+    @Test
+    public void testSelectMuteFromDrawer() {
+        final State initialUnsetState = new State();
+        initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
+        mDialog.onStateChangedH(initialUnsetState);
+
+        mActiveRinger.performClick();
+        mDrawerMute.performClick();
+
+        // Make sure we've actually changed the ringer mode.
+        verify(mController, times(1)).setRingerMode(
+                AudioManager.RINGER_MODE_SILENT, false);
+    }
+
+    @Test
+    public void testSelectNormalFromDrawer() {
+        final State initialUnsetState = new State();
+        initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
+        mDialog.onStateChangedH(initialUnsetState);
+
+        mActiveRinger.performClick();
+        mDrawerNormal.performClick();
+
+        // Make sure we've actually changed the ringer mode.
+        verify(mController, times(1)).setRingerMode(
+                AudioManager.RINGER_MODE_NORMAL, false);
+    }
+
 /*
     @Test
     public void testContentDescriptions() {
diff --git a/packages/overlays/AccentColorAmethystOverlay/Android.bp b/packages/overlays/AccentColorAmethystOverlay/Android.bp
index 7519b12..186d770 100644
--- a/packages/overlays/AccentColorAmethystOverlay/Android.bp
+++ b/packages/overlays/AccentColorAmethystOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorAmethystOverlay",
     theme: "AccentColorAmethyst",
diff --git a/packages/overlays/AccentColorAquamarineOverlay/Android.bp b/packages/overlays/AccentColorAquamarineOverlay/Android.bp
index 4469b36..7fd64f3 100644
--- a/packages/overlays/AccentColorAquamarineOverlay/Android.bp
+++ b/packages/overlays/AccentColorAquamarineOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorAquamarineOverlay",
     theme: "AccentColorAquamarine",
diff --git a/packages/overlays/AccentColorBlackOverlay/Android.bp b/packages/overlays/AccentColorBlackOverlay/Android.bp
index bfee26e..ac923eb 100644
--- a/packages/overlays/AccentColorBlackOverlay/Android.bp
+++ b/packages/overlays/AccentColorBlackOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorBlackOverlay",
     theme: "AccentColorBlack",
diff --git a/packages/overlays/AccentColorCarbonOverlay/Android.bp b/packages/overlays/AccentColorCarbonOverlay/Android.bp
index 47f66dd..f4f1b8b 100644
--- a/packages/overlays/AccentColorCarbonOverlay/Android.bp
+++ b/packages/overlays/AccentColorCarbonOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorCarbonOverlay",
     theme: "AccentColorCarbon",
diff --git a/packages/overlays/AccentColorCinnamonOverlay/Android.bp b/packages/overlays/AccentColorCinnamonOverlay/Android.bp
index 8250315..53899bf 100644
--- a/packages/overlays/AccentColorCinnamonOverlay/Android.bp
+++ b/packages/overlays/AccentColorCinnamonOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorCinnamonOverlay",
     theme: "AccentColorCinnamon",
diff --git a/packages/overlays/AccentColorGreenOverlay/Android.bp b/packages/overlays/AccentColorGreenOverlay/Android.bp
index 15b50c7..5b1f744 100644
--- a/packages/overlays/AccentColorGreenOverlay/Android.bp
+++ b/packages/overlays/AccentColorGreenOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorGreenOverlay",
     theme: "AccentColorGreen",
diff --git a/packages/overlays/AccentColorOceanOverlay/Android.bp b/packages/overlays/AccentColorOceanOverlay/Android.bp
index 6ad63bc..a8588304 100644
--- a/packages/overlays/AccentColorOceanOverlay/Android.bp
+++ b/packages/overlays/AccentColorOceanOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorOceanOverlay",
     theme: "AccentColorOcean",
diff --git a/packages/overlays/AccentColorOrchidOverlay/Android.bp b/packages/overlays/AccentColorOrchidOverlay/Android.bp
index b669333..31ed309 100644
--- a/packages/overlays/AccentColorOrchidOverlay/Android.bp
+++ b/packages/overlays/AccentColorOrchidOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorOrchidOverlay",
     theme: "AccentColorOrchid",
diff --git a/packages/overlays/AccentColorPaletteOverlay/Android.bp b/packages/overlays/AccentColorPaletteOverlay/Android.bp
index eeefd16..a6cc1de 100644
--- a/packages/overlays/AccentColorPaletteOverlay/Android.bp
+++ b/packages/overlays/AccentColorPaletteOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorPaletteOverlay",
     theme: "AccentColorPalette",
diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.bp b/packages/overlays/AccentColorPurpleOverlay/Android.bp
index ead95df..80e0ab1 100644
--- a/packages/overlays/AccentColorPurpleOverlay/Android.bp
+++ b/packages/overlays/AccentColorPurpleOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorPurpleOverlay",
     theme: "AccentColorPurple",
diff --git a/packages/overlays/AccentColorSandOverlay/Android.bp b/packages/overlays/AccentColorSandOverlay/Android.bp
index f70578a..771abca 100644
--- a/packages/overlays/AccentColorSandOverlay/Android.bp
+++ b/packages/overlays/AccentColorSandOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorSandOverlay",
     theme: "AccentColorSand",
diff --git a/packages/overlays/AccentColorSpaceOverlay/Android.bp b/packages/overlays/AccentColorSpaceOverlay/Android.bp
index 1d713df..8e4abac 100644
--- a/packages/overlays/AccentColorSpaceOverlay/Android.bp
+++ b/packages/overlays/AccentColorSpaceOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorSpaceOverlay",
     theme: "AccentColorSpace",
diff --git a/packages/overlays/AccentColorTangerineOverlay/Android.bp b/packages/overlays/AccentColorTangerineOverlay/Android.bp
index d3b1e54..75c708e 100644
--- a/packages/overlays/AccentColorTangerineOverlay/Android.bp
+++ b/packages/overlays/AccentColorTangerineOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "AccentColorTangerineOverlay",
     theme: "AccentColorTangerine",
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp
index b8def987..8e03809 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "DisplayCutoutEmulationCornerOverlay",
     theme: "DisplayCutoutEmulationCorner",
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp
index b64ddfd..afa5b64 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "DisplayCutoutEmulationDoubleOverlay",
     theme: "DisplayCutoutEmulationDouble",
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp
index 86cfebf..eae907d 100644
--- a/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "DisplayCutoutEmulationHoleOverlay",
     theme: "DisplayCutoutEmulationHole",
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp
index 28ad9db..25bc676 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "DisplayCutoutEmulationNarrowOverlay",
     theme: "DisplayCutoutEmulationNarrow",
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp
index cd00386..2828612 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "DisplayCutoutEmulationTallOverlay",
     theme: "DisplayCutoutEmulationTall",
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp
index d5fe683..66be777 100644
--- a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "DisplayCutoutEmulationWaterfallOverlay",
     theme: "DisplayCutoutEmulationWaterfall",
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp
index 0157ec4..e71cefe 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "DisplayCutoutEmulationWideOverlay",
     theme: "DisplayCutoutEmulationWide",
diff --git a/packages/overlays/FontNotoSerifSourceOverlay/Android.bp b/packages/overlays/FontNotoSerifSourceOverlay/Android.bp
index 7fd145b..231295b 100644
--- a/packages/overlays/FontNotoSerifSourceOverlay/Android.bp
+++ b/packages/overlays/FontNotoSerifSourceOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "FontNotoSerifSourceOverlay",
     theme: "FontNotoSerifSource",
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/Android.bp b/packages/overlays/IconPackCircularAndroidOverlay/Android.bp
index cd5829a..7040358 100644
--- a/packages/overlays/IconPackCircularAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackCircularAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackCircularAndroidOverlay",
     theme: "IconPackCircularAndroid",
diff --git a/packages/overlays/IconPackCircularLauncherOverlay/Android.bp b/packages/overlays/IconPackCircularLauncherOverlay/Android.bp
index 5f2491d..4f8b663 100644
--- a/packages/overlays/IconPackCircularLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackCircularLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackCircularLauncherOverlay",
     theme: "IconPackCircularLauncher",
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/Android.bp b/packages/overlays/IconPackCircularSettingsOverlay/Android.bp
index d7bc657..93220c8 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackCircularSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackCircularSettingsOverlay",
     theme: "IconPackCircularSettings",
diff --git a/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp b/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp
index 73b8cd8..4eaa420 100644
--- a/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackCircularSystemUIOverlay",
     theme: "IconPackCircularSystemUI",
diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp b/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp
index 5063932..5105b79 100644
--- a/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackCircularThemePickerOverlay",
     theme: "IconPackCircularThemePicker",
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/Android.bp b/packages/overlays/IconPackFilledAndroidOverlay/Android.bp
index 83f3656..3c4025d 100644
--- a/packages/overlays/IconPackFilledAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackFilledAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackFilledAndroidOverlay",
     theme: "IconPackFilledAndroid",
diff --git a/packages/overlays/IconPackFilledLauncherOverlay/Android.bp b/packages/overlays/IconPackFilledLauncherOverlay/Android.bp
index 6ca2566..3c5078c 100644
--- a/packages/overlays/IconPackFilledLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackFilledLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackFilledLauncherOverlay",
     theme: "IconPackFilledLauncher",
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/Android.bp b/packages/overlays/IconPackFilledSettingsOverlay/Android.bp
index 8551bd5..b5148c2 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackFilledSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackFilledSettingsOverlay",
     theme: "IconPackFilledSettings",
diff --git a/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp b/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp
index 684deb4..eb040a5 100644
--- a/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackFilledSystemUIOverlay",
     theme: "IconPackFilledSystemUI",
diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp b/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp
index dae378f..bee4808 100644
--- a/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackFilledThemePickerOverlay",
     theme: "IconPackFilledThemePicker",
diff --git a/packages/overlays/IconPackKaiAndroidOverlay/Android.bp b/packages/overlays/IconPackKaiAndroidOverlay/Android.bp
index 4161e25..ee588c1 100644
--- a/packages/overlays/IconPackKaiAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackKaiAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackKaiAndroidOverlay",
     theme: "IconPackKaiAndroid",
diff --git a/packages/overlays/IconPackKaiLauncherOverlay/Android.bp b/packages/overlays/IconPackKaiLauncherOverlay/Android.bp
index 4bf8f11..dcdad7a 100644
--- a/packages/overlays/IconPackKaiLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackKaiLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackKaiLauncherOverlay",
     theme: "IconPackKaiLauncher",
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/Android.bp b/packages/overlays/IconPackKaiSettingsOverlay/Android.bp
index c43f9e5..974bb54 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackKaiSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackKaiSettingsOverlay",
     theme: "IconPackKaiSettings",
diff --git a/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp b/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp
index 22a8c28..b04ca61 100644
--- a/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackKaiSystemUIOverlay",
     theme: "IconPackKaiSystemUI",
diff --git a/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp b/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp
index 85eb1c4..875cd1d 100644
--- a/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackKaiThemePickerOverlay",
     theme: "IconPackKaiThemePicker",
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp b/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp
index 948f015..cb7b013 100644
--- a/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackRoundedAndroidOverlay",
     theme: "IconPackRoundedAndroid",
diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp b/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp
index 5fbe635..8ab6d95 100644
--- a/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackRoundedLauncherOverlay",
     theme: "IconPackRoundedLauncher",
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp b/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp
index b1a0713..ee2f98a9 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackRoundedSettingsOverlay",
     theme: "IconPackRoundedSettings",
diff --git a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp
index 6432998..ee0220a 100644
--- a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackRoundedSystemUIOverlay",
     theme: "IconPackRoundedSystemUI",
diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp
index 95e1d3a..d74765c 100644
--- a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackRoundedThemePickerOverlay",
     theme: "IconPackRoundedTheme",
diff --git a/packages/overlays/IconPackSamAndroidOverlay/Android.bp b/packages/overlays/IconPackSamAndroidOverlay/Android.bp
index e8c33b1..2e9dc34 100644
--- a/packages/overlays/IconPackSamAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackSamAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackSamAndroidOverlay",
     theme: "IconPackSamAndroid",
diff --git a/packages/overlays/IconPackSamLauncherOverlay/Android.bp b/packages/overlays/IconPackSamLauncherOverlay/Android.bp
index a469646..aa0cf00 100644
--- a/packages/overlays/IconPackSamLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackSamLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackSamLauncherOverlay",
     theme: "IconPackSamLauncher",
diff --git a/packages/overlays/IconPackSamSettingsOverlay/Android.bp b/packages/overlays/IconPackSamSettingsOverlay/Android.bp
index bc1fa45..a62037f 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackSamSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackSamSettingsOverlay",
     theme: "IconPackSamSettings",
diff --git a/packages/overlays/IconPackSamSystemUIOverlay/Android.bp b/packages/overlays/IconPackSamSystemUIOverlay/Android.bp
index db77352..96ba7a0 100644
--- a/packages/overlays/IconPackSamSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackSamSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackSamSystemUIOverlay",
     theme: "IconPackSamSystemUI",
diff --git a/packages/overlays/IconPackSamThemePickerOverlay/Android.bp b/packages/overlays/IconPackSamThemePickerOverlay/Android.bp
index c216868..7376f03 100644
--- a/packages/overlays/IconPackSamThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackSamThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackSamThemePickerOverlay",
     theme: "IconPackSamThemePicker",
diff --git a/packages/overlays/IconPackVictorAndroidOverlay/Android.bp b/packages/overlays/IconPackVictorAndroidOverlay/Android.bp
index de62af1..ee73778 100644
--- a/packages/overlays/IconPackVictorAndroidOverlay/Android.bp
+++ b/packages/overlays/IconPackVictorAndroidOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackVictorAndroidOverlay",
     theme: "IconPackVictorAndroid",
diff --git a/packages/overlays/IconPackVictorLauncherOverlay/Android.bp b/packages/overlays/IconPackVictorLauncherOverlay/Android.bp
index fc4c360..a0cd45a 100644
--- a/packages/overlays/IconPackVictorLauncherOverlay/Android.bp
+++ b/packages/overlays/IconPackVictorLauncherOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackVictorLauncherOverlay",
     theme: "IconPackVictorLauncher",
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/Android.bp b/packages/overlays/IconPackVictorSettingsOverlay/Android.bp
index 046bb3d..7807c6b 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/Android.bp
+++ b/packages/overlays/IconPackVictorSettingsOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackVictorSettingsOverlay",
     theme: "IconPackVictorSettings",
diff --git a/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp b/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp
index b8a9e77..2deb6cd 100644
--- a/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp
+++ b/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackVictorSystemUIOverlay",
     theme: "IconPackVictorSystemUI",
diff --git a/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp b/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp
index 6f0144e..a18ebb3 100644
--- a/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp
+++ b/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconPackVictorThemePickerOverlay",
     theme: "IconPackVictorThemePicker",
diff --git a/packages/overlays/IconShapeHeartOverlay/Android.bp b/packages/overlays/IconShapeHeartOverlay/Android.bp
index ec55712..1da8f4f 100644
--- a/packages/overlays/IconShapeHeartOverlay/Android.bp
+++ b/packages/overlays/IconShapeHeartOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconShapeHeartOverlay",
     theme: "IconShapeHeart",
diff --git a/packages/overlays/IconShapePebbleOverlay/Android.bp b/packages/overlays/IconShapePebbleOverlay/Android.bp
index 7dc4fde..fa2a5bb 100644
--- a/packages/overlays/IconShapePebbleOverlay/Android.bp
+++ b/packages/overlays/IconShapePebbleOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconShapePebbleOverlay",
     theme: "IconShapePebble",
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.bp b/packages/overlays/IconShapeRoundedRectOverlay/Android.bp
index b8b8531..5052d08f 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/Android.bp
+++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconShapeRoundedRectOverlay",
     theme: "IconShapeRoundedRect",
diff --git a/packages/overlays/IconShapeSquareOverlay/Android.bp b/packages/overlays/IconShapeSquareOverlay/Android.bp
index fdeffee..1176abd 100644
--- a/packages/overlays/IconShapeSquareOverlay/Android.bp
+++ b/packages/overlays/IconShapeSquareOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconShapeSquareOverlay",
     theme: "IconShapeSquare",
diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.bp b/packages/overlays/IconShapeSquircleOverlay/Android.bp
index 468f0f7..8c219f3 100644
--- a/packages/overlays/IconShapeSquircleOverlay/Android.bp
+++ b/packages/overlays/IconShapeSquircleOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconShapeSquircleOverlay",
     theme: "IconShapeSquircle",
diff --git a/packages/overlays/IconShapeTaperedRectOverlay/Android.bp b/packages/overlays/IconShapeTaperedRectOverlay/Android.bp
index 1e48cd1..78855e8 100644
--- a/packages/overlays/IconShapeTaperedRectOverlay/Android.bp
+++ b/packages/overlays/IconShapeTaperedRectOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconShapeTaperedRectOverlay",
     theme: "IconShapeTaperedRect",
diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.bp b/packages/overlays/IconShapeTeardropOverlay/Android.bp
index 017d58ec..dd36f4f 100644
--- a/packages/overlays/IconShapeTeardropOverlay/Android.bp
+++ b/packages/overlays/IconShapeTeardropOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconShapeTeardropOverlay",
     theme: "IconShapeTeardrop",
diff --git a/packages/overlays/IconShapeVesselOverlay/Android.bp b/packages/overlays/IconShapeVesselOverlay/Android.bp
index ba3b309..2e7f8bc 100644
--- a/packages/overlays/IconShapeVesselOverlay/Android.bp
+++ b/packages/overlays/IconShapeVesselOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "IconShapeVesselOverlay",
     theme: "IconShapeVessel",
diff --git a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp
index e4fcce1..35f671ba 100644
--- a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp
+++ b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "NavigationBarMode2ButtonOverlay",
     theme: "NavigationBarMode2Button",
diff --git a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp
index 9b6c998..fe9cc81 100644
--- a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp
+++ b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "NavigationBarMode3ButtonOverlay",
     theme: "NavigationBarMode3Button",
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp
index ba29045..791f420 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "NavigationBarModeGesturalOverlay",
     theme: "NavigationBarModeGestural",
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp
index 0c688a9..28f9f33 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp
+++ b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "NavigationBarModeGesturalOverlayExtraWideBack",
     theme: "NavigationBarModeGesturalExtraWideBack",
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp
index 85d514f..f8a5603 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp
+++ b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "NavigationBarModeGesturalOverlayNarrowBack",
     theme: "NavigationBarModeGesturalNarrowBack",
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp
index 84be626..60ee6d5 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp
+++ b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "NavigationBarModeGesturalOverlayWideBack",
     theme: "NavigationBarModeGesturalWideBack",
diff --git a/packages/overlays/OneHandedModeGesturalOverlay/Android.bp b/packages/overlays/OneHandedModeGesturalOverlay/Android.bp
index 9c9d0ef..468069d 100644
--- a/packages/overlays/OneHandedModeGesturalOverlay/Android.bp
+++ b/packages/overlays/OneHandedModeGesturalOverlay/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 runtime_resource_overlay {
     name: "OneHandedModeGesturalOverlay",
     theme: "OneHandedModeGestural",
diff --git a/packages/services/CameraExtensionsProxy/Android.bp b/packages/services/CameraExtensionsProxy/Android.bp
index e2e4af2..ea0703e 100644
--- a/packages/services/CameraExtensionsProxy/Android.bp
+++ b/packages/services/CameraExtensionsProxy/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_app {
     name: "CameraExtensionsProxy",
     srcs: ["src/**/*.java"],
diff --git a/services/api/Android.bp b/services/api/Android.bp
new file mode 100644
index 0000000..b8ca548
--- /dev/null
+++ b/services/api/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_visibility: ["//visibility:private"],
+}
+
+filegroup {
+    name: "non-updatable-system-server-current.txt",
+    srcs: ["non-updatable-current.txt"],
+    visibility: ["//frameworks/base/api"],
+}
+
+filegroup {
+    name: "non-updatable-system-server-removed.txt",
+    srcs: ["non-updatable-removed.txt"],
+    visibility: ["//frameworks/base/api"],
+}
\ No newline at end of file
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 809304b..50ad661 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -137,7 +137,6 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -181,9 +180,6 @@
             }
 
             switch (action) {
-                case Intent.ACTION_CONFIGURATION_CHANGED:
-                    onConfigurationChanged();
-                    break;
                 case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
                 case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
                     synchronized (mLock) {
@@ -243,8 +239,6 @@
     private Handler mSaveStateHandler;
     private Handler mCallbackHandler;
 
-    private Locale mLocale;
-
     private final SparseIntArray mNextAppWidgetIds = new SparseIntArray();
 
     private boolean mSafeMode;
@@ -290,13 +284,6 @@
     }
 
     private void registerBroadcastReceiver() {
-        // Register for configuration changes so we can update the names
-        // of the widgets when the locale changes.
-        IntentFilter configFilter = new IntentFilter();
-        configFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                configFilter, null, null);
-
         // Register for broadcasts about package install, etc., so we can
         // update the provider list.
         IntentFilter packageFilter = new IntentFilter();
@@ -338,62 +325,6 @@
         mSafeMode = safeMode;
     }
 
-    private void onConfigurationChanged() {
-        if (DEBUG) {
-            Slog.i(TAG, "onConfigurationChanged()");
-        }
-
-        Locale revised = Locale.getDefault();
-        if (revised == null || mLocale == null || !revised.equals(mLocale)) {
-            mLocale = revised;
-
-            synchronized (mLock) {
-                SparseIntArray changedGroups = null;
-
-                // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
-                // list of installed providers and skip providers that we don't need to update.
-                // Also note that remove the provider does not clear the Provider component data.
-                ArrayList<Provider> installedProviders = new ArrayList<>(mProviders);
-                HashSet<ProviderId> removedProviders = new HashSet<>();
-
-                int N = installedProviders.size();
-                for (int i = N - 1; i >= 0; i--) {
-                    Provider provider = installedProviders.get(i);
-
-                    final int userId = provider.getUserId();
-                    if (!mUserManager.isUserUnlockingOrUnlocked(userId) ||
-                            isProfileWithLockedParent(userId)) {
-                        continue;
-                    }
-                    ensureGroupStateLoadedLocked(userId);
-
-                    if (!removedProviders.contains(provider.id)) {
-                        final boolean changed = updateProvidersForPackageLocked(
-                                provider.id.componentName.getPackageName(),
-                                provider.getUserId(), removedProviders);
-
-                        if (changed) {
-                            if (changedGroups == null) {
-                                changedGroups = new SparseIntArray();
-                            }
-                            final int groupId = mSecurityPolicy.getGroupParent(
-                                    provider.getUserId());
-                            changedGroups.put(groupId, groupId);
-                        }
-                    }
-                }
-
-                if (changedGroups != null) {
-                    final int groupCount = changedGroups.size();
-                    for (int i = 0; i < groupCount; i++) {
-                        final int groupId = changedGroups.get(i);
-                        saveGroupStateAsync(groupId);
-                    }
-                }
-            }
-        }
-    }
-
     private void onPackageBroadcastReceived(Intent intent, int userId) {
         final String action = intent.getAction();
         boolean added = false;
@@ -2673,7 +2604,7 @@
                     AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
             info.widgetFeatures = sa.getInt(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures, 0);
-            info.descriptionResource = sa.getResourceId(
+            info.descriptionRes = sa.getResourceId(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_description,
                     Resources.ID_NULL);
             sa.recycle();
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 6c30999..38275f7 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1245,10 +1245,9 @@
 
     @Override
     public IRestoreSession beginRestoreSessionForUser(
-            int userId, String packageName, String transportID,
-            @OperationType int operationType) throws RemoteException {
+            int userId, String packageName, String transportID) throws RemoteException {
         return isUserReadyForBackup(userId)
-                ? beginRestoreSession(userId, packageName, transportID, operationType) : null;
+                ? beginRestoreSession(userId, packageName, transportID) : null;
     }
 
     /**
@@ -1257,15 +1256,13 @@
      */
     @Nullable
     public IRestoreSession beginRestoreSession(
-            @UserIdInt int userId, String packageName, String transportName,
-            @OperationType int operationType) {
+            @UserIdInt int userId, String packageName, String transportName) {
         UserBackupManagerService userBackupManagerService =
                 getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
 
         return userBackupManagerService == null
                 ? null
-                : userBackupManagerService.beginRestoreSession(packageName, transportName,
-                        operationType);
+                : userBackupManagerService.beginRestoreSession(packageName, transportName);
     }
 
     @Override
@@ -1350,15 +1347,15 @@
         if (!isUserReadyForBackup(userId)) {
             return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
         }
-        return requestBackup(userId, packages, observer, monitor, flags, OperationType.BACKUP);
+        return requestBackup(userId, packages, observer, monitor, flags);
     }
 
     @Override
     public int requestBackup(String[] packages, IBackupObserver observer,
-            IBackupManagerMonitor monitor, int flags, @OperationType int operationType)
+            IBackupManagerMonitor monitor, int flags)
             throws RemoteException {
         return requestBackup(binderGetCallingUserId(), packages,
-                observer, monitor, flags, operationType);
+                observer, monitor, flags);
     }
 
     /**
@@ -1370,15 +1367,13 @@
             String[] packages,
             IBackupObserver observer,
             IBackupManagerMonitor monitor,
-            int flags,
-            @OperationType int operationType) {
+            int flags) {
         UserBackupManagerService userBackupManagerService =
                 getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
 
         return userBackupManagerService == null
                 ? BackupManager.ERROR_BACKUP_NOT_ALLOWED
-                : userBackupManagerService.requestBackup(packages, observer, monitor, flags,
-                        operationType);
+                : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
     }
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 136cd22f..9ee0159 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -96,6 +96,7 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.EventLog;
+import android.util.FeatureFlagUtils;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -127,6 +128,7 @@
 import com.android.server.backup.restore.ActiveRestoreSession;
 import com.android.server.backup.restore.PerformUnifiedRestoreTask;
 import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportNotAvailableException;
 import com.android.server.backup.transport.TransportNotRegisteredException;
 import com.android.server.backup.utils.BackupEligibilityRules;
 import com.android.server.backup.utils.BackupManagerMonitorUtils;
@@ -1860,19 +1862,10 @@
 
     /**
      * Requests a backup for the inputted {@code packages} with a specified {@link
-     * IBackupManagerMonitor}.
-     */
-    public int requestBackup(String[] packages, IBackupObserver observer,
-            IBackupManagerMonitor monitor, int flags) {
-        return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP);
-    }
-
-    /**
-     * Requests a backup for the inputted {@code packages} with a specified {@link
      * IBackupManagerMonitor} and {@link OperationType}.
      */
     public int requestBackup(String[] packages, IBackupObserver observer,
-            IBackupManagerMonitor monitor, int flags, @OperationType int operationType) {
+            IBackupManagerMonitor monitor, int flags) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
 
         if (packages == null || packages.length < 1) {
@@ -1903,13 +1896,16 @@
 
         final TransportClient transportClient;
         final String transportDirName;
+        int operationType;
         try {
             transportDirName =
                     mTransportManager.getTransportDirName(
                             mTransportManager.getCurrentTransportName());
             transportClient =
                     mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
-        } catch (TransportNotRegisteredException e) {
+            operationType = getOperationTypeFromTransport(transportClient);
+        } catch (TransportNotRegisteredException | TransportNotAvailableException
+                | RemoteException e) {
             BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
             monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
                     BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
@@ -4024,15 +4020,13 @@
     }
 
     /** Hand off a restore session. */
-    public IRestoreSession beginRestoreSession(String packageName, String transport,
-            @OperationType int operationType) {
+    public IRestoreSession beginRestoreSession(String packageName, String transport) {
         if (DEBUG) {
             Slog.v(
                     TAG,
                     addUserIdToLogMessage(
                             mUserId,
-                            "beginRestoreSession: pkg=" + packageName + " transport=" + transport
-                                + "operationType=" + operationType));
+                            "beginRestoreSession: pkg=" + packageName + " transport=" + transport));
         }
 
         boolean needPermission = true;
@@ -4073,6 +4067,17 @@
             }
         }
 
+        int operationType;
+        try {
+            operationType = getOperationTypeFromTransport(
+                    mTransportManager.getTransportClientOrThrow(transport, /* caller */
+                            "BMS.beginRestoreSession"));
+        } catch (TransportNotAvailableException | TransportNotRegisteredException
+                | RemoteException e) {
+            Slog.w(TAG, "Failed to get operation type from transport: " + e);
+            return null;
+        }
+
         synchronized (this) {
             if (mActiveRestoreSession != null) {
                 Slog.i(
@@ -4356,6 +4361,34 @@
         }
     }
 
+    @VisibleForTesting
+    @OperationType int getOperationTypeFromTransport(TransportClient transportClient)
+            throws TransportNotAvailableException, RemoteException {
+        if (!shouldUseNewBackupEligibilityRules()) {
+            // Return the default to stick to the legacy behaviour.
+            return OperationType.BACKUP;
+        }
+
+        long oldCallingId = Binder.clearCallingIdentity();
+        try {
+            IBackupTransport transport = transportClient.connectOrThrow(
+                    /* caller */ "BMS.getOperationTypeFromTransport");
+            if ((transport.getTransportFlags() & BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER) != 0) {
+                return OperationType.MIGRATION;
+            } else {
+                return OperationType.BACKUP;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldCallingId);
+        }
+    }
+
+    @VisibleForTesting
+    boolean shouldUseNewBackupEligibilityRules() {
+        return FeatureFlagUtils.isEnabled(mContext,
+                FeatureFlagUtils.SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES);
+    }
+
     private static String addUserIdToLogMessage(int userId, String message) {
         return "[UserID:" + userId + "] " + message;
     }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 01055be..21cae45 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -142,18 +142,13 @@
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
 
-//TODO onStop schedule unbind in 5 seconds
-//TODO make sure APIs are only callable from currently focused app
-//TODO schedule stopScan on activity destroy(except if configuration change)
-//TODO on associate called again after configuration change -> replace old callback with new
-//TODO avoid leaking calling activity in IFindDeviceCallback (see PrintManager#print for example)
 /** @hide */
 @SuppressLint("LongLogTag")
 public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient {
 
     private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative(
             CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
-            ".DeviceDiscoveryService");
+            ".CompanionDeviceDiscoveryService");
 
     private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000;
     private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000;
@@ -747,6 +742,15 @@
                 }
             }
         }
+
+        if (association.isNotifyOnDeviceNearby()) {
+            ServiceConnector<ICompanionDeviceService> serviceConnector =
+                    mDeviceListenerServiceConnectors.forUser(association.getUserId())
+                            .get(association.getPackageName());
+            if (serviceConnector != null) {
+                serviceConnector.unbind();
+            }
+        }
     }
 
     private void updateSpecialAccessPermissionForAssociatedPackage(Association association) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d26a7b1..f4138d1 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -197,6 +197,7 @@
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.PermissionUtils;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.AutodestructReference;
 import com.android.server.connectivity.DataConnectionStats;
@@ -1511,7 +1512,7 @@
 
     @Override
     public Network getActiveNetworkForUid(int uid, boolean ignoreBlocked) {
-        NetworkStack.checkNetworkStackPermission(mContext);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         return getActiveNetworkForUidInternal(uid, ignoreBlocked);
     }
 
@@ -1534,7 +1535,7 @@
 
     @Override
     public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
-        NetworkStack.checkNetworkStackPermission(mContext);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         final NetworkState state = getUnfilteredActiveNetworkState(uid);
         filterNetworkStateForUid(state, uid, ignoreBlocked);
         return state.networkInfo;
@@ -1878,7 +1879,7 @@
     @Override
     public NetworkState[] getAllNetworkState() {
         // This contains IMSI details, so make sure the caller is privileged.
-        NetworkStack.checkNetworkStackPermission(mContext);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
 
         final ArrayList<NetworkState> result = new ArrayList<>();
         for (Network network : getAllNetworks()) {
@@ -2302,7 +2303,7 @@
 
     // Public because it's used by mLockdownTracker.
     public void sendConnectedBroadcast(NetworkInfo info) {
-        NetworkStack.checkNetworkStackPermission(mContext);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
     }
 
@@ -4685,7 +4686,7 @@
 
     @Override
     public void setGlobalProxy(final ProxyInfo proxyProperties) {
-        NetworkStack.checkNetworkStackPermission(mContext);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         mProxyTracker.setGlobalProxy(proxyProperties);
     }
 
@@ -4887,7 +4888,7 @@
 
     @Override
     public void setRequireVpnForUids(boolean requireVpn, UidRange[] ranges) {
-        NetworkStack.checkNetworkStackPermission(mContext);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_REQUIRE_VPN_FOR_UIDS,
                 encodeBool(requireVpn), 0 /* arg2 */, ranges));
     }
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 84e429d..f2782f6 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -25,9 +25,9 @@
 import static android.content.Intent.EXTRA_PACKAGE_NAME;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 import static android.os.UserHandle.USER_SYSTEM;
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
 import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
 
 import android.annotation.NonNull;
@@ -406,12 +406,12 @@
          */
         @Override
         public void setSensorPrivacy(boolean enable) {
+            enforceManageSensorPrivacyPermission();
             // Keep the state consistent between all users to make it a single global state
             forAllUsers(userId -> setSensorPrivacy(userId, enable));
         }
 
         private void setSensorPrivacy(@UserIdInt int userId, boolean enable) {
-            enforceSensorPrivacyPermission();
             synchronized (mLock) {
                 mEnabled.put(userId, enable);
                 persistSensorPrivacyStateLocked();
@@ -421,7 +421,7 @@
 
         @Override
         public void setIndividualSensorPrivacy(@UserIdInt int userId, int sensor, boolean enable) {
-            enforceSensorPrivacyPermission();
+            enforceManageSensorPrivacyPermission();
             synchronized (mLock) {
                 SparseBooleanArray userIndividualEnabled = mIndividualEnabled.get(userId,
                         new SparseBooleanArray());
@@ -448,6 +448,7 @@
         @Override
         public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId, int sensor,
                 boolean enable) {
+            enforceManageSensorPrivacyPermission();
             int parentId = mUserManagerInternal.getProfileParentId(userId);
             forAllUsers(userId2 -> {
                 if (parentId == mUserManagerInternal.getProfileParentId(userId2)) {
@@ -460,21 +461,35 @@
          * Enforces the caller contains the necessary permission to change the state of sensor
          * privacy.
          */
-        private void enforceSensorPrivacyPermission() {
-            if (mContext.checkCallingOrSelfPermission(
-                    MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) {
-                return;
-            }
-            throw new SecurityException(
+        private void enforceManageSensorPrivacyPermission() {
+            enforcePermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY,
                     "Changing sensor privacy requires the following permission: "
                             + MANAGE_SENSOR_PRIVACY);
         }
 
         /**
+         * Enforces the caller contains the necessary permission to observe changes to the sate of
+         * sensor privacy.
+         */
+        private void enforceObserveSensorPrivacyPermission() {
+            enforcePermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY,
+                    "Observing sensor privacy changes requires the following permission: "
+                            + android.Manifest.permission.OBSERVE_SENSOR_PRIVACY);
+        }
+
+        private void enforcePermission(String permission, String message) {
+            if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+                return;
+            }
+            throw new SecurityException(message);
+        }
+
+        /**
          * Returns whether sensor privacy is enabled.
          */
         @Override
         public boolean isSensorPrivacyEnabled() {
+            enforceObserveSensorPrivacyPermission();
             return isSensorPrivacyEnabled(USER_SYSTEM);
         }
 
@@ -486,6 +501,7 @@
 
         @Override
         public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) {
+            enforceObserveSensorPrivacyPermission();
             synchronized (mLock) {
                 SparseBooleanArray states = mIndividualEnabled.get(userId);
                 if (states == null) {
@@ -703,6 +719,7 @@
          */
         @Override
         public void addSensorPrivacyListener(ISensorPrivacyListener listener) {
+            enforceObserveSensorPrivacyPermission();
             if (listener == null) {
                 throw new NullPointerException("listener cannot be null");
             }
@@ -715,6 +732,7 @@
         @Override
         public void addIndividualSensorPrivacyListener(int userId, int sensor,
                 ISensorPrivacyListener listener) {
+            enforceObserveSensorPrivacyPermission();
             if (listener == null) {
                 throw new NullPointerException("listener cannot be null");
             }
@@ -726,6 +744,7 @@
          */
         @Override
         public void removeSensorPrivacyListener(ISensorPrivacyListener listener) {
+            enforceObserveSensorPrivacyPermission();
             if (listener == null) {
                 throw new NullPointerException("listener cannot be null");
             }
@@ -735,6 +754,7 @@
         @Override
         public void suppressIndividualSensorPrivacyReminders(int userId, String packageName,
                 IBinder token, boolean suppress) {
+            enforceManageSensorPrivacyPermission();
             Objects.requireNonNull(packageName);
             Objects.requireNonNull(token);
 
@@ -886,13 +906,13 @@
         }
 
         /**
-         * Convert a string into a {@link SensorPrivacyManager.IndividualSensor id}.
+         * Convert a string into a {@link SensorPrivacyManager.Sensors.Sensor id}.
          *
          * @param sensor The name to convert
          *
          * @return The id corresponding to the name
          */
-        private @SensorPrivacyManager.IndividualSensor int sensorStrToId(@Nullable String sensor) {
+        private @SensorPrivacyManager.Sensors.Sensor int sensorStrToId(@Nullable String sensor) {
             if (sensor == null) {
                 return UNKNOWN;
             }
@@ -950,7 +970,7 @@
                                 return -1;
                             }
 
-                            enforceSensorPrivacyPermission();
+                            enforceManageSensorPrivacyPermission();
 
                             synchronized (mLock) {
                                 SparseBooleanArray individualEnabled =
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index c2d8fa2..8a2894c 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -45,14 +45,18 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.annotations.Immutable;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.function.Predicate;
 
@@ -87,26 +91,30 @@
     /** Function to run on binder interface when first bound. */
     public interface OnBindRunner {
         /** Called to run client code with the binder. */
-        void run(IBinder binder, ComponentName service) throws RemoteException;
+        void run(IBinder binder, BoundService service) throws RemoteException;
     }
 
     /**
      * Information on the service ServiceWatcher has selected as the best option for binding.
      */
-    private static final class ServiceInfo implements Comparable<ServiceInfo> {
+    @Immutable
+    public static final class BoundService implements Comparable<BoundService> {
 
-        public static final ServiceInfo NONE = new ServiceInfo(Integer.MIN_VALUE, null,
-                UserHandle.USER_NULL, false);
+        public static final BoundService NONE = new BoundService(Integer.MIN_VALUE, null,
+                false, null, -1);
 
         public final int version;
-        @Nullable public final ComponentName component;
-        @UserIdInt public final int userId;
+        @Nullable
+        public final ComponentName component;
         public final boolean serviceIsMultiuser;
+        public final int uid;
+        @Nullable
+        public final Bundle metadata;
 
-        ServiceInfo(ResolveInfo resolveInfo, int currentUserId) {
+        BoundService(ResolveInfo resolveInfo) {
             Preconditions.checkArgument(resolveInfo.serviceInfo.getComponentName() != null);
 
-            Bundle metadata = resolveInfo.serviceInfo.metaData;
+            metadata = resolveInfo.serviceInfo.metaData;
             if (metadata != null) {
                 version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
                 serviceIsMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false);
@@ -116,16 +124,17 @@
             }
 
             component = resolveInfo.serviceInfo.getComponentName();
-            userId = serviceIsMultiuser ? UserHandle.USER_SYSTEM : currentUserId;
+            uid = resolveInfo.serviceInfo.applicationInfo.uid;
         }
 
-        private ServiceInfo(int version, @Nullable ComponentName component, int userId,
-                boolean serviceIsMultiuser) {
+        private BoundService(int version, @Nullable ComponentName component,
+                boolean serviceIsMultiuser, @Nullable Bundle metadata, int uid) {
             Preconditions.checkArgument(component != null || version == Integer.MIN_VALUE);
             this.version = version;
             this.component = component;
-            this.userId = userId;
             this.serviceIsMultiuser = serviceIsMultiuser;
+            this.metadata = metadata;
+            this.uid = uid;
         }
 
         public @Nullable String getPackageName() {
@@ -137,21 +146,21 @@
             if (this == o) {
                 return true;
             }
-            if (!(o instanceof ServiceInfo)) {
+            if (!(o instanceof BoundService)) {
                 return false;
             }
-            ServiceInfo that = (ServiceInfo) o;
-            return version == that.version && userId == that.userId
+            BoundService that = (BoundService) o;
+            return version == that.version && uid == that.uid
                     && Objects.equals(component, that.component);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(version, component, userId);
+            return Objects.hash(version, component, uid);
         }
 
         @Override
-        public int compareTo(ServiceInfo that) {
+        public int compareTo(BoundService that) {
             // ServiceInfos with higher version numbers always win (having a version number >
             // MIN_VALUE implies having a non-null component). if version numbers are equal, a
             // non-null component wins over a null component. if the version numbers are equal and
@@ -164,10 +173,11 @@
                 } else if (component != null && that.component == null) {
                     ret = 1;
                 } else {
-                    if (userId != UserHandle.USER_SYSTEM && that.userId == UserHandle.USER_SYSTEM) {
+                    if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM
+                            && UserHandle.getUserId(that.uid) == UserHandle.USER_SYSTEM) {
                         ret = -1;
-                    } else if (userId == UserHandle.USER_SYSTEM
-                            && that.userId != UserHandle.USER_SYSTEM) {
+                    } else if (UserHandle.getUserId(uid) == UserHandle.USER_SYSTEM
+                            && UserHandle.getUserId(that.uid) != UserHandle.USER_SYSTEM) {
                         ret = 1;
                     }
                 }
@@ -180,7 +190,8 @@
             if (component == null) {
                 return "none";
             } else {
-                return component.toShortString() + "@" + version + "[u" + userId + "]";
+                return component.toShortString() + "@" + version + "[u"
+                        + UserHandle.getUserId(uid) + "]";
             }
         }
     }
@@ -227,17 +238,23 @@
         }
     };
 
-    @Nullable private final OnBindRunner mOnBind;
-    @Nullable private final Runnable mOnUnbind;
+    // read/write from handler thread only
+    private final Map<ComponentName, BoundService> mPendingBinds = new ArrayMap<>();
 
-    // write from caller thread only, read anywhere
-    private volatile boolean mRegistered;
+    @Nullable
+    private final OnBindRunner mOnBind;
+
+    @Nullable
+    private final Runnable mOnUnbind;
+
+    // read/write from handler thread only
+    private boolean mRegistered;
 
     // read/write from handler thread only
     private int mCurrentUserId;
 
     // write from handler thread only, read anywhere
-    private volatile ServiceInfo mTargetService;
+    private volatile BoundService mTargetService;
     private volatile IBinder mBinder;
 
     public ServiceWatcher(Context context, String action,
@@ -274,7 +291,7 @@
 
         mCurrentUserId = UserHandle.USER_NULL;
 
-        mTargetService = ServiceInfo.NONE;
+        mTargetService = BoundService.NONE;
         mBinder = null;
     }
 
@@ -299,6 +316,11 @@
      * Starts the process of determining the best matching service and maintaining a binding to it.
      */
     public void register() {
+        mHandler.sendMessage(PooledLambda.obtainMessage(ServiceWatcher::registerInternal,
+                ServiceWatcher.this));
+    }
+
+    private void registerInternal() {
         Preconditions.checkState(!mRegistered);
 
         mPackageMonitor.register(mContext, UserHandle.ALL, true, mHandler);
@@ -309,6 +331,8 @@
         mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, intentFilter, null,
                 mHandler);
 
+        // TODO: This makes the behavior of the class unpredictable as the caller needs
+        // to know the internal impl detail that calling register would pick the current user.
         mCurrentUserId = ActivityManager.getCurrentUser();
 
         mRegistered = true;
@@ -320,6 +344,11 @@
      * Stops the process of determining the best matching service and releases any binding.
      */
     public void unregister() {
+        mHandler.sendMessage(PooledLambda.obtainMessage(ServiceWatcher::unregisterInternal,
+                ServiceWatcher.this));
+    }
+
+    private void unregisterInternal() {
         Preconditions.checkState(mRegistered);
 
         mRegistered = false;
@@ -333,7 +362,7 @@
     private void onBestServiceChanged(boolean forceRebind) {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
-        ServiceInfo bestServiceInfo = ServiceInfo.NONE;
+        BoundService bestServiceInfo = BoundService.NONE;
 
         if (mRegistered) {
             List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser(
@@ -344,7 +373,7 @@
                 if (!mServiceCheckPredicate.test(resolveInfo)) {
                     continue;
                 }
-                ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId);
+                BoundService serviceInfo = new BoundService(resolveInfo);
                 if (serviceInfo.compareTo(bestServiceInfo) > 0) {
                     bestServiceInfo = serviceInfo;
                 }
@@ -356,21 +385,22 @@
         }
     }
 
-    private void rebind(ServiceInfo newServiceInfo) {
+    private void rebind(BoundService newServiceInfo) {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
-        if (!mTargetService.equals(ServiceInfo.NONE)) {
+        if (!mTargetService.equals(BoundService.NONE)) {
             if (D) {
                 Log.d(TAG, "[" + mIntent.getAction() + "] unbinding from " + mTargetService);
             }
 
             mContext.unbindService(this);
             onServiceDisconnected(mTargetService.component);
-            mTargetService = ServiceInfo.NONE;
+            mPendingBinds.remove(mTargetService.component);
+            mTargetService = BoundService.NONE;
         }
 
         mTargetService = newServiceInfo;
-        if (mTargetService.equals(ServiceInfo.NONE)) {
+        if (mTargetService.equals(BoundService.NONE)) {
             return;
         }
 
@@ -381,10 +411,12 @@
         Intent bindIntent = new Intent(mIntent).setComponent(mTargetService.component);
         if (!mContext.bindServiceAsUser(bindIntent, this,
                 BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE,
-                mHandler, UserHandle.of(mTargetService.userId))) {
-            mTargetService = ServiceInfo.NONE;
+                mHandler, UserHandle.of(UserHandle.getUserId(mTargetService.uid)))) {
+            mTargetService = BoundService.NONE;
             Log.e(TAG, getLogPrefix() + " unexpected bind failure - retrying later");
             mHandler.postDelayed(() -> onBestServiceChanged(false), RETRY_DELAY_MS);
+        } else {
+            mPendingBinds.put(mTargetService.component, mTargetService);
         }
     }
 
@@ -397,10 +429,15 @@
             Log.d(TAG, getLogPrefix() + " connected to " + component.toShortString());
         }
 
+        final BoundService boundService = mPendingBinds.remove(component);
+        if (boundService == null) {
+            return;
+        }
+
         mBinder = binder;
         if (mOnBind != null) {
             try {
-                mOnBind.run(binder, component);
+                mOnBind.run(binder, boundService);
             } catch (RuntimeException | RemoteException e) {
                 // binders may propagate some specific non-RemoteExceptions from the other side
                 // through the binder as well - we cannot allow those to crash the system server
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 2f98199..6be7f05 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -51,6 +51,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.AnrController;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.KeyguardManager;
@@ -938,14 +939,29 @@
 
         if (transcodeEnabled) {
             LocalServices.getService(ActivityManagerInternal.class)
-                    .registerAnrController((packageName, uid) -> {
-                        try {
-                            return mStorageSessionController.getAnrDelayMillis(packageName, uid);
-                        } catch (ExternalStorageServiceException e) {
-                            Log.e(TAG, "Failed to get ANR delay for " + packageName, e);
-                            return 0;
-                        }
-                    });
+                .registerAnrController(new ExternalStorageServiceAnrController());
+        }
+    }
+
+    // TODO(b/170486601): Check transcoding status based on events pushed from the MediaProvider
+    private class ExternalStorageServiceAnrController implements AnrController {
+        @Override
+        public long getAnrDelayMillis(String packageName, int uid) {
+            int delay = SystemProperties.getInt("sys.fuse.transcode_anr_delay", 0);
+            Log.d(TAG, "getAnrDelayMillis: " + packageName + ". Delaying for " + delay + "ms");
+            return delay;
+        }
+
+        @Override
+        public void onAnrDelayStarted(String packageName, int uid) {
+            Log.d(TAG, "onAnrDelayStarted: " + packageName);
+        }
+
+        @Override
+        public boolean onAnrDelayCompleted(String packageName, int uid) {
+            boolean show = SystemProperties.getBoolean("sys.fuse.transcode_anr_dialog_show", true);
+            Log.d(TAG, "onAnrDelayCompleted: " + packageName + ". Show: " + show);
+            return show;
         }
     }
 
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 95af842..a09dbc7 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -21,7 +21,9 @@
                 }
             ],
             "file_patterns": ["NotificationManagerService\\.java"]
-        },
+        }
+    ],
+    "presubmit-large": [
         {
             "name": "CtsScopedStorageCoreHostTest",
             "file_patterns": ["StorageManagerService\\.java"]
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index b09b6ca..5a5f1a3 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -313,9 +313,9 @@
 
     private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
 
-    private boolean mIsDataEnabled = false;
+    private boolean[] mIsDataEnabled;
 
-    private int mDataEnabledReason;
+    private int[] mDataEnabledReason;
 
     private Map<Integer, Long> mAllowedNetworkTypesList;
 
@@ -524,6 +524,8 @@
         mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones);
         mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones);
         mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones);
+        mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones);
+        mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones);
 
         // ds -> ss switch.
         if (mNumPhones < oldNumPhones) {
@@ -565,6 +567,8 @@
             mPreciseDataConnectionStates.add(new ArrayMap<>());
             mBarringInfo.add(i, new BarringInfo());
             mTelephonyDisplayInfos[i] = null;
+            mIsDataEnabled[i] = false;
+            mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
             mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
         }
     }
@@ -626,6 +630,8 @@
         mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
         mPhysicalChannelConfigs = new ArrayList<>();
         mAllowedNetworkTypesList = new HashMap<>();
+        mIsDataEnabled = new boolean[numPhones];
+        mDataEnabledReason = new int[numPhones];
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
             mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -655,6 +661,8 @@
             mPreciseDataConnectionStates.add(new ArrayMap<>());
             mBarringInfo.add(i, new BarringInfo());
             mTelephonyDisplayInfos[i] = null;
+            mIsDataEnabled[i] = false;
+            mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
             mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
         }
 
@@ -1150,7 +1158,8 @@
                 if (events.contains(
                         PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) {
                     try {
-                        r.callback.onDataEnabledChanged(mIsDataEnabled, mDataEnabledReason);
+                        r.callback.onDataEnabledChanged(
+                                mIsDataEnabled[phoneId], mDataEnabledReason[phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
@@ -2370,30 +2379,36 @@
     /**
      * Notify that the data enabled has changed.
      *
+     * @param phoneId the phone id.
+     * @param subId the subId.
      * @param enabled True if data is enabled, otherwise disabled.
      * @param reason  Reason for data enabled/disabled. See {@code DATA_*} in
      *                {@link TelephonyManager}.
      */
-    public void notifyDataEnabled(boolean enabled,
+    public void notifyDataEnabled(int phoneId, int subId, boolean enabled,
                                   @TelephonyManager.DataEnabledReason int reason) {
         if (!checkNotifyPermission("notifyDataEnabled()")) {
             return;
         }
 
         if (VDBG) {
-            log("notifyDataEnabled: enabled=" + enabled + " reason=" + reason);
+            log("notifyDataEnabled: PhoneId=" + phoneId + " subId=" + subId +
+                    " enabled=" + enabled + " reason=" + reason);
         }
 
-        mIsDataEnabled = enabled;
-        mDataEnabledReason = reason;
         synchronized (mRecords) {
-            for (Record r : mRecords) {
-                if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) {
-                    try {
-                        r.callback.onDataEnabledChanged(enabled, reason);
-                    } catch (RemoteException ex) {
-                        mRemoveList.add(r.binder);
+            if (validatePhoneId(phoneId)) {
+                mIsDataEnabled[phoneId] = enabled;
+                mDataEnabledReason[phoneId] = reason;
+                for (Record r : mRecords) {
+                    if (r.matchPhoneStateListenerEvent(
+                            PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)
+                            && idMatch(r.subId, subId, phoneId)) {
+                        try {
+                            r.callback.onDataEnabledChanged(enabled, reason);
+                        } catch (RemoteException ex) {
+                            mRemoveList.add(r.binder);
+                        }
                     }
                 }
             }
@@ -2481,6 +2496,8 @@
                 pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]);
                 pw.println("mBarringInfo=" + mBarringInfo.get(i));
                 pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
+                pw.println("mIsDataEnabled=" + mIsDataEnabled);
+                pw.println("mDataEnabledReason=" + mDataEnabledReason);
                 pw.decreaseIndent();
             }
             pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
@@ -2491,8 +2508,6 @@
             pw.println("mDefaultPhoneId=" + mDefaultPhoneId);
             pw.println("mDefaultSubId=" + mDefaultSubId);
             pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs);
-            pw.println("mIsDataEnabled=" + mIsDataEnabled);
-            pw.println("mDataEnabledReason=" + mDataEnabledReason);
 
             pw.decreaseIndent();
 
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 329ab99..8d5d3d9 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
 
@@ -837,7 +839,10 @@
                 // Notify all registered StatusCallbacks for this subGroup
                 for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
                     if (isCallbackPermissioned(cbInfo)) {
-                        Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode());
+                        Binder.withCleanCallingIdentity(
+                                () ->
+                                        cbInfo.mCallback.onVcnStatusChanged(
+                                                VCN_STATUS_CODE_SAFE_MODE));
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2efc83c..e5ef935 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -20,10 +20,35 @@
 import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
 import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
 import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
 import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
+import static android.os.PowerWhitelistManager.REASON_ACTIVITY_STARTER;
+import static android.os.PowerWhitelistManager.REASON_ALLOWLISTED_PACKAGE;
+import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
+import static android.os.PowerWhitelistManager.REASON_BACKGROUND_FGS_PERMISSION;
+import static android.os.PowerWhitelistManager.REASON_COMPANION_DEVICE_MANAGER;
+import static android.os.PowerWhitelistManager.REASON_DENIED;
+import static android.os.PowerWhitelistManager.REASON_DEVICE_DEMO_MODE;
+import static android.os.PowerWhitelistManager.REASON_DEVICE_OWNER;
+import static android.os.PowerWhitelistManager.REASON_EXEMPTED_PACKAGE;
+import static android.os.PowerWhitelistManager.REASON_FGS_BINDING;
+import static android.os.PowerWhitelistManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
+import static android.os.PowerWhitelistManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION;
+import static android.os.PowerWhitelistManager.REASON_PROC_STATE_PERSISTENT;
+import static android.os.PowerWhitelistManager.REASON_PROC_STATE_PERSISTENT_UI;
+import static android.os.PowerWhitelistManager.REASON_PROC_STATE_TOP;
+import static android.os.PowerWhitelistManager.REASON_PROFILE_OWNER;
+import static android.os.PowerWhitelistManager.REASON_START_ACTIVITY_FLAG;
+import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
+import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED;
+import static android.os.PowerWhitelistManager.REASON_SYSTEM_UID;
+import static android.os.PowerWhitelistManager.REASON_UID_VISIBLE;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.PowerWhitelistManager.getReasonCodeFromProcState;
+import static android.os.PowerWhitelistManager.reasonCodeToString;
 import static android.os.Process.NFC_UID;
 import static android.os.Process.ROOT_UID;
 import static android.os.Process.SHELL_UID;
@@ -43,7 +68,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -86,6 +110,8 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerWhitelistManager;
+import android.os.PowerWhitelistManager.ReasonCode;
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
@@ -130,8 +156,6 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -152,58 +176,6 @@
 
     private static final boolean SHOW_DUNGEON_NOTIFICATION = false;
 
-    public static final int FGS_FEATURE_DENIED = 0;
-    public static final int FGS_FEATURE_ALLOWED_BY_UID_STATE = 1;
-    public static final int FGS_FEATURE_ALLOWED_BY_PROC_STATE = 2;
-    public static final int FGS_FEATURE_ALLOWED_BY_UID_VISIBLE = 3;
-    public static final int FGS_FEATURE_ALLOWED_BY_FLAG = 4;
-    public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_UID = 5;
-    public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 6;
-    public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION = 7;
-    public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN = 8;
-    public static final int FGS_FEATURE_ALLOWED_BY_FGS_TOKEN = 9;
-    public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION = 10;
-    public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION = 12;
-    public static final int FGS_FEATURE_ALLOWED_BY_ALLOWLIST = 13;
-    public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER = 14;
-    public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST = 15;
-    public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION = 16;
-    public static final int FGS_FEATURE_ALLOWED_BY_FGS_BINDING = 17;
-    public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE = 18;
-    public static final int FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD = 19;
-    public static final int FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES = 20;
-    public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER = 21;
-    public static final int FGS_FEATURE_ALLOWED_BY_COMPANION_APP = 22;
-    public static final int FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER = 23;
-
-    @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = {
-            FGS_FEATURE_DENIED,
-            FGS_FEATURE_ALLOWED_BY_UID_STATE,
-            FGS_FEATURE_ALLOWED_BY_PROC_STATE,
-            FGS_FEATURE_ALLOWED_BY_UID_VISIBLE,
-            FGS_FEATURE_ALLOWED_BY_FLAG,
-            FGS_FEATURE_ALLOWED_BY_SYSTEM_UID,
-            FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION,
-            FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION,
-            FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN,
-            FGS_FEATURE_ALLOWED_BY_FGS_TOKEN,
-            FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION,
-            FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION,
-            FGS_FEATURE_ALLOWED_BY_ALLOWLIST,
-            FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER,
-            FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST,
-            FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION,
-            FGS_FEATURE_ALLOWED_BY_FGS_BINDING,
-            FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE,
-            FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD,
-            FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES,
-            FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER,
-            FGS_FEATURE_ALLOWED_BY_COMPANION_APP,
-            FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FgsFeatureRetCode {}
-
     // How long we wait for a service to finish executing.
     static final int SERVICE_TIMEOUT = 20*1000;
 
@@ -275,7 +247,7 @@
 
     AppWidgetManagerInternal mAppWidgetManagerInternal;
 
-    // white listed packageName.
+    // allowlisted packageName.
     ArraySet<String> mAllowListWhileInUsePermissionInFgs = new ArraySet<>();
 
     // TODO: remove this after feature development is done
@@ -675,7 +647,7 @@
 
         if (fgRequired) {
             logFgsBackgroundStart(r);
-            if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) {
+            if (r.mAllowStartForeground == REASON_DENIED && isBgFgsRestrictionEnabled(r)) {
                 String msg = "startForegroundService() not allowed due to "
                         + "mAllowStartForeground false: service "
                         + r.shortInstanceName;
@@ -1768,8 +1740,7 @@
 
                 if (!ignoreForeground) {
                     logFgsBackgroundStart(r);
-                    if (r.mAllowStartForeground == FGS_FEATURE_DENIED
-                            && isBgFgsRestrictionEnabled(r)) {
+                    if (r.mAllowStartForeground == REASON_DENIED && isBgFgsRestrictionEnabled(r)) {
                         final String msg = "Service.startForeground() not allowed due to "
                                 + "mAllowStartForeground false: service "
                                 + r.shortInstanceName;
@@ -2250,7 +2221,7 @@
         psr.mAllowlistManager = false;
         for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
             ServiceRecord sr = psr.getRunningServiceAt(i);
-            if (sr.whitelistManager) {
+            if (sr.allowlistManager) {
                 psr.mAllowlistManager = true;
                 break;
             }
@@ -2261,7 +2232,7 @@
         final ProcessServiceRecord psr = service.app.mServices;
         psr.stopService(service);
         psr.updateBoundClientUids();
-        if (service.whitelistManager) {
+        if (service.allowlistManager) {
             updateAllowlistManagerLocked(psr);
         }
     }
@@ -2483,7 +2454,7 @@
                 clientPsr.setHasAboveClient(true);
             }
             if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
-                s.whitelistManager = true;
+                s.allowlistManager = true;
             }
             if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
                 s.setAllowedBgActivityStartsByBinding(true);
@@ -2520,7 +2491,7 @@
                 if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
                     servicePsr.setTreatLikeActivity(true);
                 }
-                if (s.whitelistManager) {
+                if (s.allowlistManager) {
                     servicePsr.mAllowlistManager = true;
                 }
                 // This could have made the service more important.
@@ -2949,7 +2920,6 @@
                     final ServiceRestarter res = new ServiceRestarter();
                     r = new ServiceRecord(mAm, className, name, definingPackageName,
                             definingUid, filter, sInfo, callingFromFg, res);
-                    r.mRecentCallingPackage = callingPackage;
                     res.setService(r);
                     smap.mServicesByInstanceName.put(name, r);
                     smap.mServicesByIntent.put(filter, r);
@@ -2978,6 +2948,8 @@
             }
         }
         if (r != null) {
+            r.mRecentCallingPackage = callingPackage;
+            r.mRecentCallingUid = callingUid;
             if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid, r.packageName,
                     r.appInfo.uid)) {
                 String msg = "association not allowed between packages "
@@ -3440,12 +3412,13 @@
 
         if (r.fgRequired) {
             if (DEBUG_FOREGROUND_SERVICE) {
-                Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid)
+                Slog.v(TAG, "Allowlisting " + UserHandle.formatUid(r.appInfo.uid)
                         + " for fg-service launch");
             }
             mAm.tempAllowlistUidLocked(r.appInfo.uid,
-                    SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch",
-                    BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED);
+                    SERVICE_START_FOREGROUND_TIMEOUT, PowerWhitelistManager.REASON_SERVICE_LAUNCH,
+                    "fg-service-launch", TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                    r.mRecentCallingUid);
         }
 
         if (!mPendingServices.contains(r)) {
@@ -3551,7 +3524,7 @@
             }
         }
 
-        if (r.whitelistManager) {
+        if (r.allowlistManager) {
             psr.mAllowlistManager = true;
         }
 
@@ -3941,11 +3914,11 @@
             if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                 psr.updateHasAboveClientLocked();
             }
-            // If this connection requested whitelist management, see if we should
+            // If this connection requested allowlist management, see if we should
             // now clear that state.
             if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
-                s.updateWhitelistManager();
-                if (!s.whitelistManager && s.app != null) {
+                s.updateAllowlistManager();
+                if (!s.allowlistManager && s.app != null) {
                     updateAllowlistManagerLocked(s.app.mServices);
                 }
             }
@@ -5400,13 +5373,13 @@
         }
 
         if (!r.mAllowWhileInUsePermissionInFgs
-                || (r.mAllowStartForeground == FGS_FEATURE_DENIED)) {
-            final @FgsFeatureRetCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
+                || (r.mAllowStartForeground == REASON_DENIED)) {
+            final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
                     callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts);
             if (!r.mAllowWhileInUsePermissionInFgs) {
-                r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != FGS_FEATURE_DENIED);
+                r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED);
             }
-            if (r.mAllowStartForeground == FGS_FEATURE_DENIED) {
+            if (r.mAllowStartForeground == REASON_DENIED) {
                 r.mAllowStartForeground = shouldAllowFgsStartForegroundLocked(allowWhileInUse,
                         callingPackage, callingPid, callingUid, intent, r,
                         allowBackgroundActivityStarts);
@@ -5420,37 +5393,37 @@
      * @param callingPackage caller app's package name.
      * @param callingUid caller app's uid.
      * @param r the service to start.
-     * @return {@link FgsFeatureRetCode}
+     * @return {@link ReasonCode}
      */
-    private @FgsFeatureRetCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
+    private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
             int callingPid, int callingUid, ServiceRecord r,
             boolean allowBackgroundActivityStarts) {
-        int ret = FGS_FEATURE_DENIED;
+        int ret = REASON_DENIED;
 
         final int uidState = mAm.getUidStateLocked(callingUid);
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             // Is the calling UID at PROCESS_STATE_TOP or above?
             if (uidState <= PROCESS_STATE_TOP) {
-                ret = FGS_FEATURE_ALLOWED_BY_UID_STATE;
+                ret = getReasonCodeFromProcState(uidState);
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             // Does the calling UID have any visible activity?
             final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
             if (isCallingUidVisible) {
-                ret = FGS_FEATURE_ALLOWED_BY_UID_VISIBLE;
+                ret = REASON_UID_VISIBLE;
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             // Is the allow activity background start flag on?
             if (allowBackgroundActivityStarts) {
-                ret = FGS_FEATURE_ALLOWED_BY_FLAG;
+                ret = REASON_START_ACTIVITY_FLAG;
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             boolean isCallerSystem = false;
             final int callingAppId = UserHandle.getAppId(callingUid);
             switch (callingAppId) {
@@ -5466,15 +5439,15 @@
             }
 
             if (isCallerSystem) {
-                ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_UID;
+                ret = REASON_SYSTEM_UID;
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, pr -> {
                 if (pr.uid == callingUid) {
                     if (pr.getWindowProcessController().areBackgroundFgsStartsAllowed()) {
-                        return FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER;
+                        return REASON_ACTIVITY_STARTER;
                     }
                 }
                 return null;
@@ -5484,35 +5457,35 @@
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             if (r.app != null) {
                 ActiveInstrumentation instr = r.app.getActiveInstrumentation();
                 if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
-                    ret = FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
+                    ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
                 }
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
                     == PERMISSION_GRANTED) {
-                ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION;
+                ret = REASON_BACKGROUND_ACTIVITY_PERMISSION;
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             final boolean isAllowedPackage =
                     mAllowListWhileInUsePermissionInFgs.contains(callingPackage);
             if (isAllowedPackage) {
-                ret = FGS_FEATURE_ALLOWED_BY_ALLOWLIST;
+                ret = REASON_ALLOWLISTED_PACKAGE;
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             // Is the calling UID a device owner app?
             final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid);
             if (isDeviceOwner) {
-                ret = FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER;
+                ret = REASON_DEVICE_OWNER;
             }
         }
         return ret;
@@ -5527,38 +5500,40 @@
      * @param callingUid caller app's uid.
      * @param intent intent to start/bind service.
      * @param r the service to start.
-     * @return {@link FgsFeatureRetCode}
+     * @return {@link ReasonCode}
      */
-    private @FgsFeatureRetCode int shouldAllowFgsStartForegroundLocked(
-            @FgsFeatureRetCode int allowWhileInUse, String callingPackage, int callingPid,
+    private @ReasonCode int shouldAllowFgsStartForegroundLocked(
+            @ReasonCode int allowWhileInUse, String callingPackage, int callingPid,
             int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) {
         int ret = allowWhileInUse;
+        FgsStartTempAllowList.TempFgsAllowListEntry tempAllowListReason =
+                r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid);
 
         final StringBuilder sb = new StringBuilder(64);
         final int uidState = mAm.getUidStateLocked(callingUid);
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             // Is the calling UID at PROCESS_STATE_TOP or above?
             if (uidState <= PROCESS_STATE_TOP) {
                 sb.append("uidState=").append(uidState);
-                ret = FGS_FEATURE_ALLOWED_BY_UID_STATE;
+                ret = getReasonCodeFromProcState(uidState);
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, app -> {
                 if (app.uid == callingUid) {
                     final ProcessStateRecord state = app.mState;
-                    if (state.getAllowedStartFgs() != FGS_FEATURE_DENIED) {
+                    if (state.getAllowedStartFgs() != REASON_DENIED) {
                         return state.getAllowedStartFgs();
                     } else if (state.isAllowedStartFgsState()) {
-                        return FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+                        return getReasonCodeFromProcState(state.getAllowStartFgsState());
                     } else if (state.areBackgroundFgsStartsAllowedByToken()) {
-                        return FGS_FEATURE_ALLOWED_BY_FGS_BINDING;
+                        return REASON_FGS_BINDING;
                     } else {
                         final ActiveInstrumentation instr = app.getActiveInstrumentation();
                         if (instr != null
                                 && instr.mHasBackgroundForegroundServiceStartsPermission) {
-                            return FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION;
+                            return REASON_INSTR_BACKGROUND_FGS_PERMISSION;
                         }
                     }
                 }
@@ -5569,55 +5544,59 @@
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             if (mAm.checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callingPid,
                     callingUid) == PERMISSION_GRANTED) {
-                ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION;
+                ret = REASON_BACKGROUND_FGS_PERMISSION;
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             if (mAm.checkPermission(SYSTEM_ALERT_WINDOW, callingPid,
                     callingUid) == PERMISSION_GRANTED) {
-                ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION;
+                ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
-            if (mAm.isAllowlistedForFgsStartLOSP(callingUid)) {
-                // uid is on DeviceIdleController's user/system allowlist
-                // or AMS's FgsStartTempAllowList.
-                ret = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
+        if (ret == REASON_DENIED) {
+            FgsStartTempAllowList.TempFgsAllowListEntry entry =
+                    mAm.isAllowlistedForFgsStartLOSP(callingUid);
+            if (entry != null) {
+                if (entry == ActivityManagerService.FAKE_TEMP_ALLOWLIST_ENTRY) {
+                    ret = REASON_SYSTEM_ALLOW_LISTED;
+                } else {
+                    ret = entry.mReasonCode;
+                }
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             if (UserManager.isDeviceInDemoMode(mAm.mContext)) {
-                ret = FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE;
+                ret = REASON_DEVICE_DEMO_MODE;
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             // Is the calling UID a profile owner app?
             final boolean isProfileOwner = mAm.mInternal.isProfileOwner(callingUid);
             if (isProfileOwner) {
-                ret = FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER;
+                ret = REASON_PROFILE_OWNER;
             }
         }
 
         // NOTE this should always be the last check.
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             if (isPackageExemptedFromFgsRestriction(r.appInfo.packageName, r.appInfo.uid)
                     || isPackageExemptedFromFgsRestriction(callingPackage, callingUid)) {
-                ret = FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES;
+                ret = REASON_EXEMPTED_PACKAGE;
             }
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (ret == REASON_DENIED) {
             final boolean isCompanionApp = mAm.mInternal.isAssociatedCompanionApp(
                     UserHandle.getUserId(callingUid), callingUid);
             if (isCompanionApp) {
-                ret = FGS_FEATURE_ALLOWED_BY_COMPANION_APP;
+                ret = REASON_COMPANION_DEVICE_MANAGER;
             }
         }
 
@@ -5626,7 +5605,15 @@
                         + "; callingUid: " + callingUid
                         + "; uidState: " + ProcessList.makeProcStateString(uidState)
                         + "; intent: " + intent
-                        + "; code:" + fgsCodeToString(ret)
+                        + "; code:" + reasonCodeToString(ret)
+                        + "; tempAllowListReason:<" +
+                        (tempAllowListReason == null ? null :
+                                (tempAllowListReason.mReason
+                                + ",reasonCode:"
+                                + reasonCodeToString(tempAllowListReason.mReasonCode)
+                                + ",duration:" + tempAllowListReason.mDuration
+                                + ",callingUid:" + tempAllowListReason.mCallingUid))
+                        + ">"
                         + "; extra:" + sb.toString()
                         + "; targetSdkVersion:" + r.appInfo.targetSdkVersion
                         + "]";
@@ -5660,62 +5647,11 @@
         return CompatChanges.isChangeEnabled(FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID, uid);
     }
 
-    static String fgsCodeToString(@FgsFeatureRetCode int code) {
-        switch (code) {
-            case FGS_FEATURE_DENIED:
-                return "DENIED";
-            case FGS_FEATURE_ALLOWED_BY_UID_STATE:
-                return "ALLOWED_BY_UID_STATE";
-            case FGS_FEATURE_ALLOWED_BY_PROC_STATE:
-                return "ALLOWED_BY_PROC_STATE";
-            case FGS_FEATURE_ALLOWED_BY_UID_VISIBLE:
-                return "ALLOWED_BY_UID_VISIBLE";
-            case FGS_FEATURE_ALLOWED_BY_FLAG:
-                return "ALLOWED_BY_FLAG";
-            case FGS_FEATURE_ALLOWED_BY_SYSTEM_UID:
-                return "ALLOWED_BY_SYSTEM_UID";
-            case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION:
-                return "ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION";
-            case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION:
-                return "ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION";
-            case FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN:
-                return "ALLOWED_BY_ACTIVITY_TOKEN";
-            case FGS_FEATURE_ALLOWED_BY_FGS_TOKEN:
-                return "ALLOWED_BY_FGS_TOKEN";
-            case FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION:
-                return "ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION";
-            case FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION:
-                return "ALLOWED_BY_BACKGROUND_FGS_PERMISSION";
-            case FGS_FEATURE_ALLOWED_BY_ALLOWLIST:
-                return "ALLOWED_BY_ALLOWLIST";
-            case FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER:
-                return "ALLOWED_BY_DEVICE_OWNER";
-            case FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST:
-                return "ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST";
-            case FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION:
-                return "ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION";
-            case FGS_FEATURE_ALLOWED_BY_FGS_BINDING:
-                return "ALLOWED_BY_FGS_BINDING";
-            case FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE:
-                return "ALLOWED_BY_DEVICE_DEMO_MODE";
-            case FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD:
-                return "ALLOWED_BY_PROCESS_RECORD";
-            case FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES:
-                return "FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES";
-            case FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER:
-                return "ALLOWED_BY_ACTIVITY_STARTER";
-            case FGS_FEATURE_ALLOWED_BY_COMPANION_APP:
-                return "ALLOWED_BY_COMPANION_APP";
-            case FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER:
-                return "ALLOWED_BY_PROFILE_OWNER";
-            default:
-                return "";
-        }
-    }
-
-    private static boolean isFgsBgStart(@FgsFeatureRetCode int code) {
-        return code != FGS_FEATURE_ALLOWED_BY_UID_STATE
-                && code != FGS_FEATURE_ALLOWED_BY_UID_VISIBLE;
+    private static boolean isFgsBgStart(@ReasonCode int code) {
+        return code != REASON_PROC_STATE_PERSISTENT
+                && code != REASON_PROC_STATE_PERSISTENT_UI
+                && code != REASON_PROC_STATE_TOP
+                && code != REASON_UID_VISIBLE;
     }
 
     // TODO: remove this notification after feature development is done
@@ -5754,10 +5690,10 @@
         }
         if (!r.mLoggedInfoAllowStartForeground) {
             final String msg = "Background started FGS: "
-                    + ((r.mAllowStartForeground != FGS_FEATURE_DENIED) ? "Allowed " : "Disallowed ")
+                    + ((r.mAllowStartForeground != REASON_DENIED) ? "Allowed " : "Disallowed ")
                     + r.mInfoAllowStartForeground;
             Slog.wtfQuiet(TAG, msg);
-            if (r.mAllowStartForeground != FGS_FEATURE_DENIED) {
+            if (r.mAllowStartForeground != REASON_DENIED) {
                 Slog.i(TAG, msg);
             } else {
                 Slog.w(TAG, msg);
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 171b20c..9d1c838 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -68,7 +68,7 @@
     static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
     static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false;
     static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
-    static final boolean DEBUG_WHITELISTS = DEBUG_ALL || false;
+    static final boolean DEBUG_ALLOWLISTS = DEBUG_ALL || false;
 
     static final String POSTFIX_BACKUP = (APPEND_CATEGORY_NAME) ? "_Backup" : "";
     static final String POSTFIX_BROADCAST = (APPEND_CATEGORY_NAME) ? "_Broadcast" : "";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d03eea2..5e61f94 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -33,7 +33,6 @@
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.AppOpsManager.OP_NONE;
-import static android.app.BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
 import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
 import static android.content.pm.PackageManager.MATCH_ALL;
@@ -50,8 +49,12 @@
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
+import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED;
+import static android.os.PowerWhitelistManager.REASON_UNKNOWN;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.os.Process.BLUETOOTH_UID;
 import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.os.Process.INVALID_UID;
 import static android.os.Process.NETWORK_STACK_UID;
 import static android.os.Process.NFC_UID;
 import static android.os.Process.PHONE_UID;
@@ -101,7 +104,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
@@ -142,6 +145,8 @@
 import android.app.Activity;
 import android.app.ActivityClient;
 import android.app.ActivityManager;
+import android.app.ActivityManager.PendingIntentInfo;
+import android.app.ActivityManager.ProcessCapability;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityTaskManager.RootTaskInfo;
@@ -182,7 +187,6 @@
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
-import android.compat.Compatibility;
 import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
@@ -252,6 +256,7 @@
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
+import android.os.PowerWhitelistManager.ReasonCode;
 import android.os.PowerWhitelistManager.TempAllowListType;
 import android.os.Process;
 import android.os.RemoteCallback;
@@ -306,8 +311,8 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.ProcessMap;
 import com.android.internal.app.SystemUserHomeActivity;
+import com.android.internal.app.procstats.ProcessState;
 import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.compat.CompatibilityChangeConfig;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
@@ -624,7 +629,7 @@
      */
     private volatile String mDeviceOwnerName;
 
-    private volatile int mDeviceOwnerUid = Process.INVALID_UID;
+    private volatile int mDeviceOwnerUid = INVALID_UID;
 
     /**
      * Map userId to its companion app uids.
@@ -1150,13 +1155,13 @@
     DeviceIdleInternal mLocalDeviceIdleController;
 
     /**
-     * Power-save whitelisted app-ids (not including except-idle-whitelisted ones).
+     * Power-save allowlisted app-ids (not including except-idle-allowlisted ones).
      */
     @CompositeRWLock({"this", "mProcLock"})
     int[] mDeviceIdleAllowlist = new int[0];
 
     /**
-     * Power-save whitelisted app-ids (including except-idle-whitelisted ones).
+     * Power-save allowlisted app-ids (including except-idle-allowlisted ones).
      */
     @CompositeRWLock({"this", "mProcLock"})
     int[] mDeviceIdleExceptIdleAllowlist = new int[0];
@@ -1172,20 +1177,27 @@
         final long duration;
         final String tag;
         final int type;
+        final @ReasonCode int reasonCode;
 
-        PendingTempAllowlist(int targetUid, long duration, String tag, int type) {
+        PendingTempAllowlist(int targetUid, long duration, @ReasonCode int reasonCode, String tag,
+                int type) {
             this.targetUid = targetUid;
             this.duration = duration;
             this.tag = tag;
             this.type = type;
+            this.reasonCode = reasonCode;
         }
 
         void dumpDebug(ProtoOutputStream proto, long fieldId) {
             final long token = proto.start(fieldId);
-            proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid);
-            proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, duration);
+            proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID,
+                    targetUid);
+            proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS,
+                    duration);
             proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TAG, tag);
             proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TYPE, type);
+            proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.REASON_CODE,
+                    reasonCode);
             proto.end(token);
         }
     }
@@ -1199,6 +1211,9 @@
     @CompositeRWLock({"this", "mProcLock"})
     final FgsStartTempAllowList mFgsStartTempAllowList = new FgsStartTempAllowList();
 
+    static final FgsStartTempAllowList.TempFgsAllowListEntry FAKE_TEMP_ALLOWLIST_ENTRY = new
+            FgsStartTempAllowList.TempFgsAllowListEntry(Long.MAX_VALUE, Long.MAX_VALUE,
+            REASON_SYSTEM_ALLOW_LISTED, "", INVALID_UID);
     /**
      * Information about and control over application operations
      */
@@ -1820,6 +1835,15 @@
         ncl.start();
     }
 
+    /**
+     * Sets a policy for handling app ops.
+     *
+     * @param appOpsPolicy The policy.
+     */
+    public void setAppOpsPolicy(@Nullable CheckOpsDelegate appOpsPolicy) {
+        mAppOpsService.setAppOpsPolicy(appOpsPolicy);
+    }
+
     public IAppOpsService getAppOpsService() {
         return mAppOpsService;
     }
@@ -3779,10 +3803,11 @@
                                 mi.getTotalUss(), mi.getTotalRss(), false,
                                 ProcessStats.ADD_PSS_EXTERNAL_SLOW, duration);
                         proc.getPkgList().forEachPackageProcessStats(holder -> {
+                            final ProcessState state = holder.state;
                             FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
                                     proc.info.uid,
-                                    holder.state.getName(),
-                                    holder.state.getPackage(),
+                                    state != null ? state.getName() : proc.processName,
+                                    state != null ? state.getPackage() : proc.info.packageName,
                                     mi.getTotalPss(),
                                     mi.getTotalUss(),
                                     mi.getTotalRss(),
@@ -4818,12 +4843,12 @@
     }
 
     @Override
-    public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code,
+    public int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
             Intent intent, String resolvedType,
             IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
         if (target instanceof PendingIntentRecord) {
             return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType,
-                    whitelistToken, finishedReceiver, requiredPermission, options);
+                    allowlistToken, finishedReceiver, requiredPermission, options);
         } else {
             if (intent == null) {
                 // Weird case: someone has given us their own custom IIntentSender, and now
@@ -4835,7 +4860,7 @@
                 intent = new Intent(Intent.ACTION_MAIN);
             }
             try {
-                target.send(code, intent, resolvedType, whitelistToken, null,
+                target.send(code, intent, resolvedType, allowlistToken, null,
                         requiredPermission, options);
             } catch (RemoteException e) {
             }
@@ -4860,19 +4885,6 @@
     }
 
     @Override
-    public String getPackageForIntentSender(IIntentSender pendingResult) {
-        if (!(pendingResult instanceof PendingIntentRecord)) {
-            return null;
-        }
-        try {
-            PendingIntentRecord res = (PendingIntentRecord)pendingResult;
-            return res.key.packageName;
-        } catch (ClassCastException e) {
-        }
-        return null;
-    }
-
-    @Override
     public void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
         mPendingIntentController.registerIntentSenderCancelListener(sender, receiver);
     }
@@ -4884,15 +4896,17 @@
     }
 
     @Override
-    public int getUidForIntentSender(IIntentSender sender) {
+    public PendingIntentInfo getInfoForIntentSender(IIntentSender sender) {
         if (sender instanceof PendingIntentRecord) {
-            try {
-                PendingIntentRecord res = (PendingIntentRecord)sender;
-                return res.uid;
-            } catch (ClassCastException e) {
-            }
+            PendingIntentRecord res = (PendingIntentRecord) sender;
+            return new PendingIntentInfo(
+                    res.key.packageName,
+                    res.uid,
+                    (res.key.flags & PendingIntent.FLAG_IMMUTABLE) != 0,
+                    res.key.type);
+        } else {
+            throw new IllegalArgumentException();
         }
-        return -1;
     }
 
     @Override
@@ -4918,15 +4932,6 @@
     }
 
     @Override
-    public boolean isIntentSenderImmutable(IIntentSender pendingResult) {
-        if (pendingResult instanceof PendingIntentRecord) {
-            final PendingIntentRecord res = (PendingIntentRecord) pendingResult;
-            return (res.key.flags & PendingIntent.FLAG_IMMUTABLE) != 0;
-        }
-        return false;
-    }
-
-    @Override
     public boolean isIntentSenderAnActivity(IIntentSender pendingResult) {
         if (!(pendingResult instanceof PendingIntentRecord)) {
             return false;
@@ -4943,33 +4948,6 @@
     }
 
     @Override
-    public boolean isIntentSenderAForegroundService(IIntentSender pendingResult) {
-        if (pendingResult instanceof PendingIntentRecord) {
-            final PendingIntentRecord res = (PendingIntentRecord) pendingResult;
-            return res.key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isIntentSenderAService(IIntentSender pendingResult) {
-        if (pendingResult instanceof PendingIntentRecord) {
-            final PendingIntentRecord res = (PendingIntentRecord) pendingResult;
-            return res.key.type == ActivityManager.INTENT_SENDER_SERVICE;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isIntentSenderABroadcast(IIntentSender pendingResult) {
-        if (pendingResult instanceof PendingIntentRecord) {
-            final PendingIntentRecord res = (PendingIntentRecord) pendingResult;
-            return res.key.type == ActivityManager.INTENT_SENDER_BROADCAST;
-        }
-        return false;
-    }
-
-    @Override
     public Intent getIntentForIntentSender(IIntentSender pendingResult) {
         enforceCallingPermission(Manifest.permission.GET_INTENT_SENDER_INTENT,
                 "getIntentForIntentSender()");
@@ -5450,7 +5428,7 @@
         }
         switch (appop) {
             case AppOpsManager.MODE_ALLOWED:
-                // If force-background-check is enabled, restrict all apps that aren't whitelisted.
+                // If force-background-check is enabled, restrict all apps that aren't allowlisted.
                 if (mForceBackgroundCheck &&
                         !UserHandle.isCore(uid) &&
                         !isOnDeviceIdleAllowlistLOSP(uid, /*allowExceptIdleToo=*/ true)) {
@@ -5486,7 +5464,7 @@
         if (uidOnBackgroundAllowlistLOSP(uid)) {
             if (DEBUG_BACKGROUND_CHECK) {
                 Slog.i(TAG, "App " + uid + "/" + packageName
-                        + " on background whitelist; not restricted in background");
+                        + " on background allowlist; not restricted in background");
             }
             return ActivityManager.APP_START_MODE_NORMAL;
         }
@@ -5495,7 +5473,7 @@
         if (isOnDeviceIdleAllowlistLOSP(uid, /*allowExceptIdleToo=*/ false)) {
             if (DEBUG_BACKGROUND_CHECK) {
                 Slog.i(TAG, "App " + uid + "/" + packageName
-                        + " on idle whitelist; not restricted in background");
+                        + " on idle allowlist; not restricted in background");
             }
             return ActivityManager.APP_START_MODE_NORMAL;
         }
@@ -5583,10 +5561,19 @@
                 || mPendingTempAllowlist.indexOfKey(uid) >= 0;
     }
 
+    /**
+     * Is the uid allowlisted to start FGS?
+     * @param uid
+     * @return a TempAllowListEntry if the uid is allowed.
+     *         null if the uid is not allowed.
+     */
+    @Nullable
     @GuardedBy(anyOf = {"this", "mProcLock"})
-    boolean isAllowlistedForFgsStartLOSP(int uid) {
-        return Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, UserHandle.getAppId(uid)) >= 0
-                || mFgsStartTempAllowList.isAllowed(uid);
+    FgsStartTempAllowList.TempFgsAllowListEntry isAllowlistedForFgsStartLOSP(int uid) {
+        if (Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, UserHandle.getAppId(uid)) >= 0) {
+            return FAKE_TEMP_ALLOWLIST_ENTRY;
+        }
+        return mFgsStartTempAllowList.getAllowedDurationAndReason(uid);
     }
 
     /**
@@ -6058,13 +6045,13 @@
     }
 
     @Override
-    public void backgroundWhitelistUid(final int uid) {
+    public void backgroundAllowlistUid(final int uid) {
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only the OS may call backgroundWhitelistUid()");
+            throw new SecurityException("Only the OS may call backgroundAllowlistUid()");
         }
 
         if (DEBUG_BACKGROUND_CHECK) {
-            Slog.i(TAG, "Adding uid " + uid + " to bg uid whitelist");
+            Slog.i(TAG, "Adding uid " + uid + " to bg uid allowlist");
         }
         synchronized (this) {
             synchronized (mProcLock) {
@@ -6634,6 +6621,18 @@
     }
 
     @Override
+    public @ProcessCapability int getUidProcessCapabilities(int uid, String callingPackage) {
+        if (!hasUsageStatsPermission(callingPackage)) {
+            enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
+                    "getUidProcessState");
+        }
+
+        synchronized (mProcLock) {
+            return mProcessList.getUidProcessCapabilityLOSP(uid);
+        }
+    }
+
+    @Override
     public void registerUidObserver(IUidObserver observer, int which, int cutpoint,
             String callingPackage) {
         if (!hasUsageStatsPermission(callingPackage)) {
@@ -7249,6 +7248,9 @@
             final long memoryGrowthThreshold =
                     Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD);
             mProcessList.forEachLruProcessesLOSP(false, proc -> {
+                if (proc.getThread() == null) {
+                    return;
+                }
                 final ProcessProfileRecord pr = proc.mProfile;
                 final ProcessStateRecord state = proc.mState;
                 final int setProcState = state.getSetProcState();
@@ -8292,7 +8294,7 @@
         if (!TextUtils.isEmpty(packageName)) {
             final int uid = enforceDumpPermissionForPackage(packageName, userId, callingUid,
                       "getHistoricalProcessExitReasons");
-            if (uid != Process.INVALID_UID) {
+            if (uid != INVALID_UID) {
                 mProcessList.mAppExitInfoTracker.getExitInfo(
                         packageName, uid, pid, maxNum, results);
                 tombstoneService.collectTombstones(results, uid, pid, maxNum);
@@ -8326,7 +8328,7 @@
     int enforceDumpPermissionForPackage(String packageName, int userId, int callingUid,
             String function) {
         final long identity = Binder.clearCallingIdentity();
-        int uid = Process.INVALID_UID;
+        int uid = INVALID_UID;
         try {
             uid = mPackageManagerInt.getPackageUid(packageName,
                     MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
@@ -9218,6 +9220,8 @@
                     pw.println(ptw.tag);
                     pw.print(" ");
                     pw.print(ptw.type);
+                    pw.print(" ");
+                    pw.print(ptw.reasonCode);
                 }
             }
         }
@@ -10079,6 +10083,7 @@
             ProcessList.PERSISTENT_SERVICE_ADJ, ProcessList.FOREGROUND_APP_ADJ,
             ProcessList.VISIBLE_APP_ADJ,
             ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_LOW_APP_ADJ,
+            ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ,
             ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
             ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ,
             ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MIN_ADJ
@@ -10086,7 +10091,7 @@
     static final String[] DUMP_MEM_OOM_LABEL = new String[] {
             "Native",
             "System", "Persistent", "Persistent Service", "Foreground",
-            "Visible", "Perceptible", "Perceptible Low",
+            "Visible", "Perceptible", "Perceptible Low", "Perceptible Medium",
             "Heavy Weight", "Backup",
             "A Services", "Home",
             "Previous", "B Services", "Cached"
@@ -10094,7 +10099,7 @@
     static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] {
             "native",
             "sys", "pers", "persvc", "fore",
-            "vis", "percept", "perceptl",
+            "vis", "percept", "perceptl", "perceptm",
             "heavy", "backup",
             "servicea", "home",
             "prev", "serviceb", "cached"
@@ -10402,6 +10407,8 @@
                     }
                     endTime = SystemClock.currentThreadTimeMillis();
                     hasSwapPss = mi.hasSwappedOutPss;
+                    memtrackGraphics = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GRAPHICS);
+                    memtrackGl = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GL);
                 } else {
                     reportType = ProcessStats.ADD_PSS_EXTERNAL;
                     startTime = SystemClock.currentThreadTimeMillis();
@@ -10553,6 +10560,8 @@
                         if (!Debug.getMemoryInfo(st.pid, info)) {
                             return;
                         }
+                        memtrackGraphics = info.getOtherPrivate(Debug.MemoryInfo.OTHER_GRAPHICS);
+                        memtrackGl = info.getOtherPrivate(Debug.MemoryInfo.OTHER_GL);
                     } else {
                         long pss = Debug.getPss(st.pid, tmpLong, memtrackTmp);
                         if (pss == 0) {
@@ -12570,7 +12579,7 @@
         BroadcastOptions brOptions = null;
         if (bOptions != null) {
             brOptions = new BroadcastOptions(bOptions);
-            if (brOptions.getTemporaryAppWhitelistDuration() > 0) {
+            if (brOptions.getTemporaryAppAllowlistDuration() > 0) {
                 // See if the caller is allowed to do this.  Note we are checking against
                 // the actual real caller (not whoever provided the operation as say a
                 // PendingIntent), because that who is actually supplied the arguments.
@@ -13571,8 +13580,6 @@
             if (disableHiddenApiChecks || disableTestApiChecks) {
                 enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
                         "disable hidden API checks");
-
-                enableTestApiAccess(ai.packageName);
             }
 
             final long origId = Binder.clearCallingIdentity();
@@ -13746,25 +13753,6 @@
                     app.userId,
                     "finished inst");
         }
-
-        disableTestApiAccess(app.info.packageName);
-    }
-
-    private void enableTestApiAccess(String packageName) {
-        if (mPlatformCompat != null) {
-            Compatibility.ChangeConfig config = new Compatibility.ChangeConfig(
-                    Collections.singleton(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */),
-                    Collections.emptySet());
-            CompatibilityChangeConfig override = new CompatibilityChangeConfig(config);
-            mPlatformCompat.setOverridesForTest(override, packageName);
-        }
-    }
-
-    private void disableTestApiAccess(String packageName) {
-        if (mPlatformCompat != null) {
-            mPlatformCompat.clearOverrideForTest(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */,
-                    packageName);
-        }
     }
 
     public void finishInstrumentation(IApplicationThread target,
@@ -13973,7 +13961,7 @@
     }
 
     void noteUidProcessState(final int uid, final int state,
-                final @ActivityManager.ProcessCapability int capability) {
+                final @ProcessCapability int capability) {
         mBatteryStatsService.noteUidProcessState(uid, state);
         mAppOpsService.updateUidProcState(uid, state, capability);
         if (mTrackingAssociations) {
@@ -14024,6 +14012,9 @@
             final long uptimeSince = curUptime - mLastPowerCheckUptime;
             mLastPowerCheckUptime = curUptime;
             mProcessList.forEachLruProcessesLOSP(false, app -> {
+                if (app.getThread() == null) {
+                    return;
+                }
                 if (app.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) {
                     int cpuLimit;
                     long checkDur = curUptime - app.mState.getWhenUnimportant();
@@ -14125,11 +14116,12 @@
                 mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName,
                         uptimeSince, cputimeUsed);
                 app.getPkgList().forEachPackageProcessStats(holder -> {
+                    final ProcessState state = holder.state;
                     FrameworkStatsLog.write(
                             FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED,
                             app.info.uid,
                             processName,
-                            holder.state.getPackage(),
+                            state != null ? state.getPackage() : app.info.packageName,
                             holder.appVersion);
                 });
                 return true;
@@ -14469,8 +14461,8 @@
      */
     @GuardedBy("this")
     void tempAllowlistForPendingIntentLocked(int callerPid, int callerUid, int targetUid,
-            long duration, int type, String tag) {
-        if (DEBUG_WHITELISTS) {
+            long duration, int type, @ReasonCode int reasonCode, String reason) {
+        if (DEBUG_ALLOWLISTS) {
             Slog.d(TAG, "tempAllowlistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", "
                     + targetUid + ", " + duration + ", " + type + ")");
         }
@@ -14489,7 +14481,7 @@
                         != PackageManager.PERMISSION_GRANTED
                         && checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callerPid,
                         callerUid) != PackageManager.PERMISSION_GRANTED) {
-                    if (DEBUG_WHITELISTS) {
+                    if (DEBUG_ALLOWLISTS) {
                         Slog.d(TAG, "tempAllowlistForPendingIntentLocked() for target " + targetUid
                                 + ": pid " + callerPid + " is not allowed");
                     }
@@ -14498,22 +14490,23 @@
             }
         }
 
-        tempAllowlistUidLocked(targetUid, duration, tag, type);
+        tempAllowlistUidLocked(targetUid, duration, reasonCode, reason, type, callerUid);
     }
 
     /**
      * Allowlists {@code targetUid} to temporarily bypass Power Save mode.
      */
     @GuardedBy("this")
-    void tempAllowlistUidLocked(int targetUid, long duration, String tag, int type) {
+    void tempAllowlistUidLocked(int targetUid, long duration, @ReasonCode int reasonCode,
+            String reason, int type, int callingUid) {
         synchronized (mProcLock) {
             mPendingTempAllowlist.put(targetUid,
-                    new PendingTempAllowlist(targetUid, duration, tag, type));
+                    new PendingTempAllowlist(targetUid, duration, reasonCode, reason, type));
             setUidTempAllowlistStateLSP(targetUid, true);
             mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget();
 
-            if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
-                mFgsStartTempAllowList.add(targetUid, duration);
+            if (type == TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+                mFgsStartTempAllowList.add(targetUid, duration, reasonCode, reason, callingUid);
             }
         }
     }
@@ -14539,7 +14532,7 @@
             for (int i = 0; i < N; i++) {
                 PendingTempAllowlist ptw = list[i];
                 mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
-                        ptw.duration, ptw.type, true, ptw.tag);
+                        ptw.duration, ptw.type, true, ptw.reasonCode, ptw.tag);
             }
         }
 
@@ -15127,10 +15120,17 @@
         }
 
         @Override
-        public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken,
+        public void setPendingIntentAllowlistDuration(IIntentSender target, IBinder allowlistToken,
+                long duration, int type, @ReasonCode int reasonCode, @Nullable String reason) {
+            mPendingIntentController.setPendingIntentAllowlistDuration(target, allowlistToken,
+                    duration, type, reasonCode, reason);
+        }
+
+        @Override
+        public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder allowlistToken,
                 long duration, int type) {
-            mPendingIntentController.setPendingIntentWhitelistDuration(target, whitelistToken,
-                    duration, type);
+            mPendingIntentController.setPendingIntentAllowlistDuration(target, allowlistToken,
+                    duration, type, REASON_UNKNOWN, "");
         }
 
         @Override
@@ -15140,32 +15140,32 @@
 
         @Override
         public void setPendingIntentAllowBgActivityStarts(IIntentSender target,
-                IBinder whitelistToken, int flags) {
+                IBinder allowlistToken, int flags) {
             if (!(target instanceof PendingIntentRecord)) {
                 Slog.w(TAG, "setPendingIntentAllowBgActivityStarts():"
                         + " not a PendingIntentRecord: " + target);
                 return;
             }
             synchronized (ActivityManagerService.this) {
-                ((PendingIntentRecord) target).setAllowBgActivityStarts(whitelistToken, flags);
+                ((PendingIntentRecord) target).setAllowBgActivityStarts(allowlistToken, flags);
             }
         }
 
         @Override
         public void clearPendingIntentAllowBgActivityStarts(IIntentSender target,
-                IBinder whitelistToken) {
+                IBinder allowlistToken) {
             if (!(target instanceof PendingIntentRecord)) {
                 Slog.w(TAG, "clearPendingIntentAllowBgActivityStarts():"
                         + " not a PendingIntentRecord: " + target);
                 return;
             }
             synchronized (ActivityManagerService.this) {
-                ((PendingIntentRecord) target).clearAllowBgActivityStarts(whitelistToken);
+                ((PendingIntentRecord) target).clearAllowBgActivityStarts(allowlistToken);
             }
         }
 
         @Override
-        public void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids) {
+        public void setDeviceIdleAllowlist(int[] allAppids, int[] exceptIdleAppids) {
             synchronized (ActivityManagerService.this) {
                 synchronized (mProcLock) {
                     mDeviceIdleAllowlist = allAppids;
@@ -15175,17 +15175,19 @@
         }
 
         @Override
-        public void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, boolean adding,
-                long durationMs, @TempAllowListType int type) {
+        public void updateDeviceIdleTempAllowlist(int[] appids, int changingUid, boolean adding,
+                long durationMs, @TempAllowListType int type, @ReasonCode int reasonCode,
+                @Nullable String reason, int callingUid) {
             synchronized (ActivityManagerService.this) {
                 synchronized (mProcLock) {
                     mDeviceIdleTempAllowlist = appids;
                     if (adding) {
-                        if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
-                            mFgsStartTempAllowList.add(changingUid, durationMs);
+                        if (type == TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+                            mFgsStartTempAllowList.add(changingUid, durationMs, reasonCode, reason,
+                                    callingUid);
                         }
+                        setAppIdTempAllowlistStateLSP(changingUid, adding);
                     }
-                    setAppIdTempAllowlistStateLSP(changingUid, adding);
                 }
             }
         }
@@ -15546,11 +15548,11 @@
         }
 
         @Override
-        public void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
-                long duration, int type, String tag) {
+        public void tempAllowlistForPendingIntent(int callerPid, int callerUid, int targetUid,
+                long duration, int type, @ReasonCode int reasonCode, String reason) {
             synchronized (ActivityManagerService.this) {
                 ActivityManagerService.this.tempAllowlistForPendingIntentLocked(
-                        callerPid, callerUid, targetUid, duration, type, tag);
+                        callerPid, callerUid, targetUid, duration, type, reasonCode, reason);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index 48222cb..a9e5571 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -18,10 +18,7 @@
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Bundle;
@@ -138,19 +135,6 @@
         findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE);
     }
 
-    @Override
-    public void onStart() {
-        super.onStart();
-        getContext().registerReceiver(mReceiver,
-                new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        getContext().unregisterReceiver(mReceiver);
-    }
-
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
             setResult(msg.what);
@@ -204,15 +188,6 @@
         }
     }
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
-                cancel();
-            }
-        }
-    };
-
     static class Data {
         AppErrorResult result;
         int taskId;
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index e5a5cff..3602f44 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -29,6 +29,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.AnrController;
 import android.app.ApplicationErrorReport;
 import android.app.ApplicationExitInfo;
 import android.content.ActivityNotFoundException;
@@ -1058,7 +1059,26 @@
                     Settings.Secure.ANR_SHOW_BACKGROUND, 0,
                     mService.mUserController.getCurrentUserId()) != 0;
             if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) {
-                errState.getDialogController().showAnrDialogs(data);
+                AnrController anrController = errState.getDialogController().getAnrController();
+                if (anrController == null) {
+                    errState.getDialogController().showAnrDialogs(data);
+                } else {
+                    String packageName = proc.info.packageName;
+                    int uid = proc.info.uid;
+                    boolean showDialog = anrController.onAnrDelayCompleted(packageName, uid);
+
+                    if (showDialog) {
+                        Slog.d(TAG, "ANR delay completed. Showing ANR dialog for package: "
+                                + packageName);
+                        errState.getDialogController().showAnrDialogs(data);
+                    } else {
+                        Slog.d(TAG, "ANR delay completed. Cancelling ANR dialog for package: "
+                                + packageName);
+                        errState.setNotResponding(false);
+                        errState.setNotRespondingReport(null);
+                        errState.getDialogController().clearAnrDialogs();
+                    }
+                }
             } else {
                 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
                         AppNotRespondingDialog.CANT_SHOW);
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 77d2898..b233a2c 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -181,6 +181,11 @@
         }
     };
 
+    @Override
+    protected void closeDialog() {
+        mHandler.obtainMessage(FORCE_CLOSE).sendToTarget();
+    }
+
     static class Data {
         final ProcessRecord proc;
         final ApplicationInfo aInfo;
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index c8630fa..f8494d8 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1327,6 +1327,8 @@
         // Get a list of Stats that have vsize > 0
         final List<ProcessCpuTracker.Stats> stats = getCpuStats(st -> st.vsize > 0);
         final int statsCount = stats.size();
+        long totalMemtrackGraphics = 0;
+        long totalMemtrackGl = 0;
         for (int i = 0; i < statsCount; i++) {
             ProcessCpuTracker.Stats st = stats.get(i);
             long pss = Debug.getPss(st.pid, swaptrackTmp, memtrackTmp);
@@ -1337,6 +1339,8 @@
                     mi.pss = pss;
                     mi.swapPss = swaptrackTmp[1];
                     mi.memtrack = memtrackTmp[0];
+                    totalMemtrackGraphics += memtrackTmp[1];
+                    totalMemtrackGl += memtrackTmp[2];
                     memInfos.add(mi);
                 }
             }
@@ -1345,20 +1349,18 @@
         long totalPss = 0;
         long totalSwapPss = 0;
         long totalMemtrack = 0;
-        long totalMemtrackGraphics = 0;
-        long totalMemtrackGl = 0;
         for (int i = 0, size = memInfos.size(); i < size; i++) {
             ProcessMemInfo mi = memInfos.get(i);
             if (mi.pss == 0) {
                 mi.pss = Debug.getPss(mi.pid, swaptrackTmp, memtrackTmp);
                 mi.swapPss = swaptrackTmp[1];
                 mi.memtrack = memtrackTmp[0];
+                totalMemtrackGraphics += memtrackTmp[1];
+                totalMemtrackGl += memtrackTmp[2];
             }
             totalPss += mi.pss;
             totalSwapPss += mi.swapPss;
             totalMemtrack += mi.memtrack;
-            totalMemtrackGraphics += memtrackTmp[1];
-            totalMemtrackGl += memtrackTmp[2];
         }
         Collections.sort(memInfos, new Comparator<ProcessMemInfo>() {
             @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) {
diff --git a/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java
index 3ce2471..262e795 100644
--- a/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java
+++ b/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java
@@ -61,6 +61,11 @@
     public void onStop() {
     }
 
+    @Override
+    protected void closeDialog() {
+        /* Do nothing */
+    }
+
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
             switch (msg.what) {
diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java
index aabb587..7b5f2cd 100644
--- a/services/core/java/com/android/server/am/BaseErrorDialog.java
+++ b/services/core/java/com/android/server/am/BaseErrorDialog.java
@@ -16,16 +16,19 @@
 
 package com.android.server.am;
 
-import com.android.internal.R;
-
 import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Handler;
 import android.os.Message;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 import android.widget.Button;
 
+import com.android.internal.R;
+
 public class BaseErrorDialog extends AlertDialog {
     private static final int ENABLE_BUTTONS = 0;
     private static final int DISABLE_BUTTONS = 1;
@@ -44,10 +47,19 @@
         getWindow().setAttributes(attrs);
     }
 
+    @Override
     public void onStart() {
         super.onStart();
         mHandler.sendEmptyMessage(DISABLE_BUTTONS);
         mHandler.sendMessageDelayed(mHandler.obtainMessage(ENABLE_BUTTONS), 1000);
+        getContext().registerReceiver(mReceiver,
+                new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        getContext().unregisterReceiver(mReceiver);
     }
 
     public boolean dispatchKeyEvent(KeyEvent event) {
@@ -84,4 +96,24 @@
             }
         }
     };
+
+    /**
+     * Called when received ACTION_CLOSE_SYSTEM_DIALOGS.
+     */
+    protected void closeDialog() {
+        if (mCancelable) {
+            cancel();
+        } else {
+            dismiss();
+        }
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+                closeDialog();
+            }
+        }
+    };
 }
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 34ff774..2906193 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -22,6 +22,7 @@
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -43,6 +44,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerWhitelistManager;
 import android.os.PowerWhitelistManager.TempAllowListType;
 import android.os.Process;
 import android.os.RemoteException;
@@ -903,8 +905,9 @@
         return false;
     }
 
-    final void scheduleTempWhitelistLocked(int uid, long duration, BroadcastRecord r,
-            @TempAllowListType int type) {
+    final void scheduleTempAllowlistLocked(int uid, long duration, BroadcastRecord r,
+            @TempAllowListType int type, @PowerWhitelistManager.ReasonCode int reasonCode,
+            @Nullable String reason) {
         if (duration > Integer.MAX_VALUE) {
             duration = Integer.MAX_VALUE;
         }
@@ -926,10 +929,11 @@
             b.append(r.intent.getData());
         }
         if (DEBUG_BROADCAST) {
-            Slog.v(TAG, "Broadcast temp whitelist uid=" + uid + " duration=" + duration
+            Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration
                     + " type=" + type + " : " + b.toString());
         }
-        mService.tempAllowlistUidLocked(uid, duration, b.toString(), type);
+        mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type,
+                r.callingUid);
     }
 
     /**
@@ -1332,10 +1336,12 @@
                     // r is guaranteed ordered at this point, so we know finishReceiverLocked()
                     // will get a callback and handle the activity start token lifecycle.
                 }
-                if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
-                    scheduleTempWhitelistLocked(filter.owningUid,
-                            brOptions.getTemporaryAppWhitelistDuration(), r,
-                            brOptions.getTemporaryAppWhitelistType());
+                if (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0) {
+                    scheduleTempAllowlistLocked(filter.owningUid,
+                            brOptions.getTemporaryAppAllowlistDuration(), r,
+                            brOptions.getTemporaryAppAllowlistType(),
+                            brOptions.getTemporaryAppAllowlistReasonCode(),
+                            brOptions.getTemporaryAppAllowlistReason());
                 }
             }
             return;
@@ -1619,11 +1625,13 @@
         }
 
         final boolean isActivityCapable =
-                (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0);
+                (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0);
         if (isActivityCapable) {
-            scheduleTempWhitelistLocked(receiverUid,
-                    brOptions.getTemporaryAppWhitelistDuration(), r,
-                    brOptions.getTemporaryAppWhitelistType());
+            scheduleTempAllowlistLocked(receiverUid,
+                    brOptions.getTemporaryAppAllowlistDuration(), r,
+                    brOptions.getTemporaryAppAllowlistType(),
+                    brOptions.getTemporaryAppAllowlistReasonCode(),
+                    brOptions.getTemporaryAppAllowlistReason());
         }
 
         // Broadcast is being executed, its package can't be stopped.
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index a34163c..c5f082a 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -536,18 +536,6 @@
     }
 
     /**
-     * Enable or disable the freezer. When enable == false all frozen processes are unfrozen,
-     * but aren't removed from the freezer. While in this state, processes can be added or removed
-     * by using Process.setProcessFrozen(), but they wouldn't be actually frozen until the freezer
-     * is enabled. If enable == true all processes in the freezer are frozen.
-     *
-     * @param enable Specify whether to enable (true) or disable (false) the freezer.
-     *
-     * @hide
-     */
-    private static native void enableFreezerInternal(boolean enable);
-
-    /**
      * Informs binder that a process is about to be frozen. If freezer is enabled on a process via
      * this method, this method will synchronously dispatch all pending transactions to the
      * specified pid. This method will not add significant latencies when unfreezing.
@@ -590,10 +578,6 @@
 
             if (state == '1' || state == '0') {
                 supported = true;
-                // This is a workaround after reverting the cgroup v2 uid/pid hierarchy due to
-                // http://b/179006802.
-                // TODO: remove once the uid/pid hierarchy is restored
-                enableFreezerInternal(true);
             } else {
                 Slog.e(TAG_AM, "unexpected value in cgroup.freeze");
             }
diff --git a/services/core/java/com/android/server/am/ErrorDialogController.java b/services/core/java/com/android/server/am/ErrorDialogController.java
index ef135d5..f23d309 100644
--- a/services/core/java/com/android/server/am/ErrorDialogController.java
+++ b/services/core/java/com/android/server/am/ErrorDialogController.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import android.annotation.Nullable;
+import android.app.AnrController;
 import android.app.Dialog;
 import android.content.Context;
 
@@ -57,6 +59,13 @@
     @GuardedBy("mProcLock")
     private AppWaitingForDebuggerDialog mWaitDialog;
 
+    /**
+     * ANR dialog controller
+     */
+    @GuardedBy("mProcLock")
+    @Nullable
+    private AnrController mAnrController;
+
     @GuardedBy("mProcLock")
     boolean hasCrashDialogs() {
         return mCrashDialogs != null;
@@ -118,6 +127,7 @@
         }
         forAllDialogs(mAnrDialogs, Dialog::dismiss);
         mAnrDialogs = null;
+        mAnrController = null;
     }
 
     @GuardedBy("mProcLock")
@@ -220,6 +230,17 @@
         });
     }
 
+    @GuardedBy("mProcLock")
+    @Nullable
+    AnrController getAnrController() {
+        return mAnrController;
+    }
+
+    @GuardedBy("mProcLock")
+    void setAnrController(AnrController controller) {
+        mAnrController = controller;
+    }
+
     /**
      * Helper function to collect contexts from crashed app located displays.
      *
diff --git a/services/core/java/com/android/server/am/FgsStartTempAllowList.java b/services/core/java/com/android/server/am/FgsStartTempAllowList.java
index 4d8749c..1f897b5 100644
--- a/services/core/java/com/android/server/am/FgsStartTempAllowList.java
+++ b/services/core/java/com/android/server/am/FgsStartTempAllowList.java
@@ -18,24 +18,53 @@
 
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 
+import android.annotation.Nullable;
+import android.os.PowerWhitelistManager.ReasonCode;
 import android.os.SystemClock;
 import android.util.Slog;
-import android.util.SparseLongArray;
+import android.util.SparseArray;
 
 /**
  * List of uids that are temporarily allowed to start FGS from background.
  */
 final class FgsStartTempAllowList {
     private static final int MAX_SIZE = 100;
+
+    public static final class TempFgsAllowListEntry {
+        final long mExpirationTime;
+        final long mDuration;
+        final @ReasonCode int mReasonCode;
+        final String mReason;
+        final int mCallingUid;
+
+        TempFgsAllowListEntry(long expirationTime, long duration, @ReasonCode int reasonCode,
+                String reason, int callingUid) {
+            mExpirationTime = expirationTime;
+            mDuration = duration;
+            mReasonCode = reasonCode;
+            mReason = reason;
+            mCallingUid = callingUid;
+        }
+    }
+
     /**
-     * The key is the uid, the value is expiration elapse time in ms of this temp-allowed uid.
+     * The key is the uid, the value is a TempAllowListEntry.
      */
-    private final SparseLongArray mTempAllowListFgs = new SparseLongArray();
+    private final SparseArray<TempFgsAllowListEntry> mTempAllowListFgs = new SparseArray<>();
 
     FgsStartTempAllowList() {
     }
 
-    void add(int uid, long duration) {
+    /**
+     * Add a uid and its duration with reason into the FGS temp-allowlist.
+     * @param uid
+     * @param duration temp-allowlisted duration in milliseconds.
+     * @param reason A human-readable reason for logging purposes.
+     * @param callingUid the callingUid that setup this temp allowlist, only valid when param adding
+     *                   is true.
+     */
+    void add(int uid, long duration, @ReasonCode int reasonCode, @Nullable String reason,
+            int callingUid) {
         if (duration <= 0) {
             Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: "
                     + uid);
@@ -48,26 +77,36 @@
         }
         final long now = SystemClock.elapsedRealtime();
         for (int index = mTempAllowListFgs.size() - 1; index >= 0; index--) {
-            if (mTempAllowListFgs.valueAt(index) < now) {
+            if (mTempAllowListFgs.valueAt(index).mExpirationTime < now) {
                 mTempAllowListFgs.removeAt(index);
             }
         }
-        final long existingExpirationTime = mTempAllowListFgs.get(uid, -1);
+        final TempFgsAllowListEntry existing = mTempAllowListFgs.get(uid);
         final long expirationTime = now + duration;
-        if (existingExpirationTime == -1 || existingExpirationTime < expirationTime) {
-            mTempAllowListFgs.put(uid, expirationTime);
+        if (existing == null || existing.mExpirationTime < expirationTime) {
+            mTempAllowListFgs.put(uid,
+                    new TempFgsAllowListEntry(expirationTime, duration, reasonCode,
+                            reason == null ? "" : reason, callingUid));
         }
     }
 
-    boolean isAllowed(int uid) {
+    /**
+     * Is this uid temp-allowlisted to start FGS.
+     * @param uid
+     * @return If uid is in the temp-allowlist, return the {@link TempFgsAllowListEntry}; If not in
+     *         temp-allowlist, return null.
+     */
+    @Nullable
+    TempFgsAllowListEntry getAllowedDurationAndReason(int uid) {
         final int index = mTempAllowListFgs.indexOfKey(uid);
         if (index < 0) {
-            return false;
-        } else if (mTempAllowListFgs.valueAt(index) < SystemClock.elapsedRealtime()) {
+            return null;
+        } else if (mTempAllowListFgs.valueAt(index).mExpirationTime
+                < SystemClock.elapsedRealtime()) {
             mTempAllowListFgs.removeAt(index);
-            return false;
+            return null;
         } else {
-            return true;
+            return mTempAllowListFgs.valueAt(index);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 87cba54..24953fc 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -21,6 +21,7 @@
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NETWORK;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
@@ -41,6 +42,7 @@
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
+import static android.os.PowerWhitelistManager.REASON_DENIED;
 import static android.os.Process.SCHED_OTHER;
 import static android.os.Process.THREAD_GROUP_BACKGROUND;
 import static android.os.Process.THREAD_GROUP_DEFAULT;
@@ -51,7 +53,6 @@
 import static android.os.Process.setThreadPriority;
 import static android.os.Process.setThreadScheduler;
 
-import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
@@ -1967,7 +1968,7 @@
                     int clientProcState = cstate.getCurRawProcState();
 
                     // pass client's mAllowStartFgs to the app if client is not persistent process.
-                    if (cstate.getAllowedStartFgs() != FGS_FEATURE_DENIED
+                    if (cstate.getAllowedStartFgs() != REASON_DENIED
                             && cstate.getMaxAdj() >= ProcessList.FOREGROUND_APP_ADJ) {
                         state.setAllowStartFgs(cstate.getAllowedStartFgs());
                     }
@@ -1981,6 +1982,21 @@
                             capability |= cstate.getCurCapability();
                         }
 
+                        // If an app has network capability by default
+                        // (by having procstate <= BFGS), then the apps it binds to will get
+                        // elevated to a high enough procstate anyway to get network unless they
+                        // request otherwise, so don't propagate the network capability by default
+                        // in this case unless they explicitly request it.
+                        if ((cstate.getCurCapability() & PROCESS_CAPABILITY_NETWORK) != 0) {
+                            if (clientProcState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+                                if ((cr.flags & Context.BIND_ALLOW_NETWORK_ACCESS) != 0) {
+                                    capability |= PROCESS_CAPABILITY_NETWORK;
+                                }
+                            } else {
+                                capability |= PROCESS_CAPABILITY_NETWORK;
+                            }
+                        }
+
                         if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                             // If the other app is cached for any reason, for purposes here
                             // we are going to consider it empty.  The specific cached state
@@ -2048,6 +2064,10 @@
                                         && clientAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
                                         && adj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
                                     newAdj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
+                                } else if ((cr.flags & Context.BIND_ALMOST_PERCEPTIBLE) != 0
+                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
+                                        && adj >= ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ) {
+                                    newAdj = ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ;
                                 } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
                                         && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
                                         && adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -2117,13 +2137,13 @@
                                 if (enabled) {
                                     if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
                                         // TOP process passes all capabilities to the service.
-                                        capability |= PROCESS_CAPABILITY_ALL;
+                                        capability |= cstate.getCurCapability();
                                     } else {
                                         // TOP process passes no capability to the service.
                                     }
                                 } else {
                                     // TOP process passes all capabilities to the service.
-                                    capability |= PROCESS_CAPABILITY_ALL;
+                                    capability |= cstate.getCurCapability();
                                 }
                             }
                         } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) {
@@ -2448,20 +2468,20 @@
             case PROCESS_STATE_TOP:
                 return PROCESS_CAPABILITY_ALL;
             case PROCESS_STATE_BOUND_TOP:
-                return PROCESS_CAPABILITY_NONE;
+                return PROCESS_CAPABILITY_NETWORK;
             case PROCESS_STATE_FOREGROUND_SERVICE:
                 if (psr.hasForegroundServices()) {
                     // Capability from FGS are conditional depending on foreground service type in
                     // manifest file and the mAllowWhileInUsePermissionInFgs flag.
-                    return PROCESS_CAPABILITY_NONE;
+                    return PROCESS_CAPABILITY_NETWORK;
                 } else {
                     // process has no FGS, the PROCESS_STATE_FOREGROUND_SERVICE is from client.
                     // the implicit capability could be removed in the future, client should use
                     // BIND_INCLUDE_CAPABILITY flag.
-                    return PROCESS_CAPABILITY_ALL_IMPLICIT;
+                    return PROCESS_CAPABILITY_ALL_IMPLICIT | PROCESS_CAPABILITY_NETWORK;
                 }
             case PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
-                return PROCESS_CAPABILITY_NONE;
+                return PROCESS_CAPABILITY_NETWORK;
             default:
                 return PROCESS_CAPABILITY_NONE;
         }
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index 42172bf..534bd84 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -36,6 +36,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerWhitelistManager;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -300,15 +301,16 @@
         }
     }
 
-    void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken,
-            long duration, int type) {
+    void setPendingIntentAllowlistDuration(IIntentSender target, IBinder allowlistToken,
+            long duration, int type, @PowerWhitelistManager.ReasonCode int reasonCode,
+            @Nullable String reason) {
         if (!(target instanceof PendingIntentRecord)) {
             Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target);
             return;
         }
         synchronized (mLock) {
-            ((PendingIntentRecord) target).setWhitelistDurationLocked(whitelistToken, duration,
-                    type);
+            ((PendingIntentRecord) target).setAllowlistDurationLocked(allowlistToken, duration,
+                    type, reasonCode, reason);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 0eb48f6..51666ac 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -31,13 +31,14 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.PowerWhitelistManager;
+import android.os.PowerWhitelistManager.ReasonCode;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.TransactionTooLargeException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.TimeUtils;
 
@@ -67,7 +68,7 @@
      * milliseconds, Integer is allowlist type defined at
      * {@link android.os.PowerWhitelistManager.TempAllowListType}
      */
-    private ArrayMap<IBinder, Pair<Long, Integer>> mWhitelistDuration;
+    private ArrayMap<IBinder, TempAllowListDuration> mAllowlistDuration;
     private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
     private ArraySet<IBinder> mAllowBgActivityStartsForActivitySender = new ArraySet<>();
     private ArraySet<IBinder> mAllowBgActivityStartsForBroadcastSender = new ArraySet<>();
@@ -214,6 +215,21 @@
         }
     }
 
+    static final class TempAllowListDuration {
+        long duration;
+        int type;
+        @ReasonCode int reasonCode;
+        @Nullable String reason;
+
+        TempAllowListDuration(long _duration, int _type, @ReasonCode int _reasonCode,
+                String _reason) {
+            duration = _duration;
+            type = _type;
+            reasonCode = _reasonCode;
+            reason = _reason;
+        }
+    }
+
     PendingIntentRecord(PendingIntentController _controller, Key _k, int _u) {
         controller = _controller;
         key = _k;
@@ -221,18 +237,19 @@
         ref = new WeakReference<>(this);
     }
 
-    void setWhitelistDurationLocked(IBinder whitelistToken, long duration, int type) {
+    void setAllowlistDurationLocked(IBinder allowlistToken, long duration, int type,
+            @ReasonCode int reasonCode, @Nullable String reason) {
         if (duration > 0) {
-            if (mWhitelistDuration == null) {
-                mWhitelistDuration = new ArrayMap<>();
+            if (mAllowlistDuration == null) {
+                mAllowlistDuration = new ArrayMap<>();
             }
-            mWhitelistDuration.put(whitelistToken, new Pair(duration, type));
-        } else if (mWhitelistDuration != null) {
-            mWhitelistDuration.remove(whitelistToken);
-            if (mWhitelistDuration.size() <= 0) {
-                mWhitelistDuration = null;
+            mAllowlistDuration.put(allowlistToken,
+                    new TempAllowListDuration(duration, type, reasonCode, reason));
+        } else if (mAllowlistDuration != null) {
+            mAllowlistDuration.remove(allowlistToken);
+            if (mAllowlistDuration.size() <= 0) {
+                mAllowlistDuration = null;
             }
-
         }
         this.stringName = null;
     }
@@ -280,25 +297,25 @@
         return listeners;
     }
 
-    public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+    public void send(int code, Intent intent, String resolvedType, IBinder allowlistToken,
             IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
-        sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver,
+        sendInner(code, intent, resolvedType, allowlistToken, finishedReceiver,
                 requiredPermission, null, null, 0, 0, 0, options);
     }
 
-    public int sendWithResult(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+    public int sendWithResult(int code, Intent intent, String resolvedType, IBinder allowlistToken,
             IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
-        return sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver,
+        return sendInner(code, intent, resolvedType, allowlistToken, finishedReceiver,
                 requiredPermission, null, null, 0, 0, 0, options);
     }
 
-    public int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+    public int sendInner(int code, Intent intent, String resolvedType, IBinder allowlistToken,
             IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
             String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) {
         if (intent != null) intent.setDefusable(true);
         if (options != null) options.setDefusable(true);
 
-        Pair<Long, Integer> duration = null;
+        TempAllowListDuration duration = null;
         Intent finalIntent = null;
         Intent[] allIntents = null;
         String[] allResolvedTypes = null;
@@ -347,8 +364,8 @@
                 mergedOptions.setCallerOptions(opts);
             }
 
-            if (mWhitelistDuration != null) {
-                duration = mWhitelistDuration.get(whitelistToken);
+            if (mAllowlistDuration != null) {
+                duration = mAllowlistDuration.get(allowlistToken);
             }
 
             if (key.type == ActivityManager.INTENT_SENDER_ACTIVITY
@@ -377,7 +394,9 @@
         try {
             if (duration != null) {
                 StringBuilder tag = new StringBuilder(64);
-                tag.append("pendingintent:");
+                tag.append("setPendingIntentAllowlistDuration,reason:");
+                tag.append(duration.reason == null ? "" : duration.reason);
+                tag.append(",pendingintent:");
                 UserHandle.formatUid(tag, callingUid);
                 tag.append(":");
                 if (finalIntent.getAction() != null) {
@@ -387,8 +406,8 @@
                 } else if (finalIntent.getData() != null) {
                     tag.append(finalIntent.getData().toSafeString());
                 }
-                controller.mAmInternal.tempWhitelistForPendingIntent(callingPid, callingUid,
-                        uid, duration.first, duration.second, tag.toString());
+                controller.mAmInternal.tempAllowlistForPendingIntent(callingPid, callingUid,
+                        uid, duration.duration, duration.type, duration.reasonCode, tag.toString());
             }
 
             boolean sendFinish = finishedReceiver != null;
@@ -417,7 +436,8 @@
                                     allIntents, allResolvedTypes, resultTo, mergedOptions, userId,
                                     false /* validateIncomingUser */,
                                     this /* originatingPendingIntent */,
-                                    mAllowBgActivityStartsForActivitySender.contains(whitelistToken));
+                                    mAllowBgActivityStartsForActivitySender.contains(
+                                            allowlistToken));
                         } else {
                             res = controller.mAtmInternal.startActivityInPackage(uid, callingPid,
                                     callingUid, key.packageName, key.featureId, finalIntent,
@@ -426,7 +446,7 @@
                                     false /* validateIncomingUser */,
                                     this /* originatingPendingIntent */,
                                     mAllowBgActivityStartsForActivitySender.contains(
-                                            whitelistToken));
+                                            allowlistToken));
                         }
                     } catch (RuntimeException e) {
                         Slog.w(TAG, "Unable to send startActivity intent", e);
@@ -439,8 +459,8 @@
                 case ActivityManager.INTENT_SENDER_BROADCAST:
                     try {
                         final boolean allowedByToken =
-                                mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken);
-                        final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null;
+                                mAllowBgActivityStartsForBroadcastSender.contains(allowlistToken);
+                        final IBinder bgStartsToken = (allowedByToken) ? allowlistToken : null;
 
                         // If a completion callback has been requested, require
                         // that the broadcast be delivered synchronously
@@ -460,8 +480,8 @@
                 case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
                     try {
                         final boolean allowedByToken =
-                                mAllowBgActivityStartsForServiceSender.contains(whitelistToken);
-                        final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null;
+                                mAllowBgActivityStartsForServiceSender.contains(allowlistToken);
+                        final IBinder bgStartsToken = (allowedByToken) ? allowlistToken : null;
 
                         controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
                                 key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
@@ -533,18 +553,23 @@
             pw.print(prefix); pw.print("sent="); pw.print(sent);
                     pw.print(" canceled="); pw.println(canceled);
         }
-        if (mWhitelistDuration != null) {
+        if (mAllowlistDuration != null) {
             pw.print(prefix);
-            pw.print("whitelistDuration=");
-            for (int i = 0; i < mWhitelistDuration.size(); i++) {
+            pw.print("allowlistDuration=");
+            for (int i = 0; i < mAllowlistDuration.size(); i++) {
                 if (i != 0) {
                     pw.print(", ");
                 }
-                pw.print(Integer.toHexString(System.identityHashCode(mWhitelistDuration.keyAt(i))));
+                TempAllowListDuration entry = mAllowlistDuration.valueAt(i);
+                pw.print(Integer.toHexString(System.identityHashCode(mAllowlistDuration.keyAt(i))));
                 pw.print(":");
-                TimeUtils.formatDuration(mWhitelistDuration.valueAt(i).first, pw);
+                TimeUtils.formatDuration(entry.duration, pw);
                 pw.print("/");
-                pw.print(mWhitelistDuration.valueAt(i).second);
+                pw.print(entry.type);
+                pw.print("/");
+                pw.print(PowerWhitelistManager.reasonCodeToString(entry.reasonCode));
+                pw.print("/");
+                pw.print(entry.reason);
             }
             pw.println();
         }
@@ -572,18 +597,23 @@
         }
         sb.append(' ');
         sb.append(key.typeName());
-        if (mWhitelistDuration != null) {
-            sb.append( " (whitelist: ");
-            for (int i = 0; i < mWhitelistDuration.size(); i++) {
+        if (mAllowlistDuration != null) {
+            sb.append(" (allowlist: ");
+            for (int i = 0; i < mAllowlistDuration.size(); i++) {
                 if (i != 0) {
                     sb.append(",");
                 }
+                TempAllowListDuration entry = mAllowlistDuration.valueAt(i);
                 sb.append(Integer.toHexString(System.identityHashCode(
-                        mWhitelistDuration.keyAt(i))));
+                        mAllowlistDuration.keyAt(i))));
                 sb.append(":");
-                TimeUtils.formatDuration(mWhitelistDuration.valueAt(i).first, sb);
+                TimeUtils.formatDuration(entry.duration, sb);
                 sb.append("/");
-                sb.append(mWhitelistDuration.valueAt(i).second);
+                sb.append(entry.type);
+                sb.append("/");
+                sb.append(PowerWhitelistManager.reasonCodeToString(entry.reasonCode));
+                sb.append("/");
+                sb.append(entry.reason);
             }
             sb.append(")");
         }
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 1653123..3258f8a 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -22,6 +22,7 @@
 import static com.android.server.am.ProcessRecord.TAG;
 
 import android.app.ActivityManager;
+import android.app.AnrController;
 import android.app.ApplicationErrorReport;
 import android.app.ApplicationExitInfo;
 import android.content.ComponentName;
@@ -418,10 +419,16 @@
 
         // Retrieve max ANR delay from AnrControllers without the mService lock since the
         // controllers might in turn call into apps
-        long anrDialogDelayMs = mService.mActivityTaskManager.getMaxAnrDelayMillis(aInfo);
-        if (aInfo != null && aInfo.packageName != null && anrDialogDelayMs > 0) {
-            Slog.i(TAG, "Delaying ANR dialog for " + aInfo.packageName + " for " + anrDialogDelayMs
-                    + "ms");
+        AnrController anrController = mService.mActivityTaskManager.getAnrController(aInfo);
+        long anrDialogDelayMs = 0;
+        if (anrController != null) {
+            String packageName = aInfo.packageName;
+            int uid = aInfo.uid;
+            anrDialogDelayMs = anrController.getAnrDelayMillis(packageName, uid);
+            // Might execute an async binder call to a system app to show an interim
+            // ANR progress UI
+            anrController.onAnrDelayStarted(packageName, uid);
+            Slog.i(TAG, "ANR delay of " + anrDialogDelayMs + "ms started for " + packageName);
         }
 
         synchronized (mService) {
@@ -440,6 +447,7 @@
                 // Set the app's notResponding state, and look up the errorReportReceiver
                 makeAppNotRespondingLSP(activityShortComponentName,
                         annotation != null ? "ANR " + annotation : "ANR", info.toString());
+                mDialogController.setAnrController(anrController);
             }
 
             // Notify package manager service to possibly update package state
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index cb07a06..38330fe 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
@@ -57,6 +58,7 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessCapability;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.AppProtoEnums;
@@ -228,6 +230,11 @@
     // not so perceptible that it affects the user immediately if killed.
     static final int PERCEPTIBLE_LOW_APP_ADJ = 250;
 
+    // This is a process hosting services that are not perceptible to the user but the
+    // client (system) binding to it requested to treat it as if it is perceptible and avoid killing
+    // it if possible.
+    static final int PERCEPTIBLE_MEDIUM_APP_ADJ = 225;
+
     // This is a process only hosting components that are perceptible to the
     // user, and we really want to avoid killing them, but they are not
     // immediately visible. An example is background music playback.
@@ -1027,6 +1034,9 @@
         } else if (setAdj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
             return buildOomTag("prcl  ", "prcl", null, setAdj,
                     ProcessList.PERCEPTIBLE_LOW_APP_ADJ, compact);
+        } else if (setAdj >= ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ) {
+            return buildOomTag("prcm  ", "prcm", null, setAdj,
+                    ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ, compact);
         } else if (setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
             return buildOomTag("prcp  ", "prcp", null, setAdj,
                     ProcessList.PERCEPTIBLE_APP_ADJ, compact);
@@ -4395,6 +4405,7 @@
             printOomLevel(pw, "FOREGROUND_APP_ADJ", FOREGROUND_APP_ADJ);
             printOomLevel(pw, "VISIBLE_APP_ADJ", VISIBLE_APP_ADJ);
             printOomLevel(pw, "PERCEPTIBLE_APP_ADJ", PERCEPTIBLE_APP_ADJ);
+            printOomLevel(pw, "PERCEPTIBLE_MEDIUM_APP_ADJ", PERCEPTIBLE_MEDIUM_APP_ADJ);
             printOomLevel(pw, "PERCEPTIBLE_LOW_APP_ADJ", PERCEPTIBLE_LOW_APP_ADJ);
             printOomLevel(pw, "BACKUP_APP_ADJ", BACKUP_APP_ADJ);
             printOomLevel(pw, "HEAVY_WEIGHT_APP_ADJ", HEAVY_WEIGHT_APP_ADJ);
@@ -4658,13 +4669,26 @@
         }
     }
 
-    /** Returns the uid's process state or PROCESS_STATE_NONEXISTENT if not running */
+    /**
+     * Returns the uid's process state or {@link ActivityManager#PROCESS_STATE_NONEXISTENT}
+     * if not running
+     */
     @GuardedBy(anyOf = {"mService", "mProcLock"})
     int getUidProcStateLOSP(int uid) {
         UidRecord uidRec = mActiveUids.get(uid);
         return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState();
     }
 
+    /**
+     * Returns the uid's process capability or {@link ActivityManager#PROCESS_CAPABILITY_NONE}
+     * if not running
+     */
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    @ProcessCapability int getUidProcessCapabilityLOSP(int uid) {
+        UidRecord uidRec = mActiveUids.get(uid);
+        return uidRec == null ? PROCESS_CAPABILITY_NONE : uidRec.getCurCapability();
+    }
+
     /** Returns the UidRecord for the given uid, if it exists. */
     @GuardedBy(anyOf = {"mService", "mProcLock"})
     UidRecord getUidRecordLOSP(int uid) {
@@ -4749,8 +4773,9 @@
             if (!UserHandle.isApp(uidRec.getUid()) || !uidRec.hasInternetPermission) {
                 continue;
             }
-            // If process state is not changed, then there's nothing to do.
-            if (uidRec.getSetProcState() == uidRec.getCurProcState()) {
+            // If process state and capabilities are not changed, then there's nothing to do.
+            if (uidRec.getSetProcState() == uidRec.getCurProcState()
+                    && uidRec.getSetCapability() == uidRec.getCurCapability()) {
                 continue;
             }
             final int blockState = getBlockStateForUid(uidRec);
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 499fbcb..6d783fc 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -23,22 +23,23 @@
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
+import static android.os.PowerWhitelistManager.REASON_BACKGROUND_FGS_PERMISSION;
+import static android.os.PowerWhitelistManager.REASON_COMPANION_DEVICE_MANAGER;
+import static android.os.PowerWhitelistManager.REASON_DENIED;
+import static android.os.PowerWhitelistManager.REASON_DEVICE_OWNER;
+import static android.os.PowerWhitelistManager.REASON_PROFILE_OWNER;
+import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
+import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED;
+import static android.os.PowerWhitelistManager.REASON_SYSTEM_UID;
+import static android.os.PowerWhitelistManager.ReasonCode;
+import static android.os.PowerWhitelistManager.getReasonCodeFromProcState;
+import static android.os.PowerWhitelistManager.reasonCodeToString;
 import static android.os.Process.NFC_UID;
 import static android.os.Process.ROOT_UID;
 import static android.os.Process.SHELL_UID;
 import static android.os.Process.SYSTEM_UID;
 
-import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION;
-import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION;
-import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_COMPANION_APP;
-import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
-import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER;
-import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROC_STATE;
-import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER;
-import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION;
-import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_UID;
-import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED;
-import static com.android.server.am.ActiveServices.fgsCodeToString;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
 import static com.android.server.am.ProcessRecord.TAG;
 
@@ -329,7 +330,7 @@
      * Does the process has permission to start FGS from background.
      */
     @GuardedBy("mService")
-    private @ActiveServices.FgsFeatureRetCode int mAllowStartFgsByPermission;
+    private @ReasonCode int mAllowStartFgsByPermission = REASON_DENIED;
 
     /**
      * Can this process start FGS from background?
@@ -337,7 +338,7 @@
      * another process through service binding.
      */
     @GuardedBy("mService")
-    private @ActiveServices.FgsFeatureRetCode int mAllowStartFgs;
+    private @ReasonCode int mAllowStartFgs = REASON_DENIED;
 
     /**
      * Debugging: primary thing impacting oom_adj.
@@ -1152,44 +1153,47 @@
     }
 
     @GuardedBy("mService")
+    int getAllowStartFgsState() {
+        return mAllowStartFgsState;
+    }
+
+    @GuardedBy("mService")
     boolean isAllowedStartFgsState() {
         return mAllowStartFgsState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
     }
 
     @GuardedBy("mService")
     void setAllowStartFgsByPermission() {
-        int ret = FGS_FEATURE_DENIED;
-        if (ret == FGS_FEATURE_DENIED) {
-            boolean isSystem = false;
-            final int uid = UserHandle.getAppId(mApp.info.uid);
-            switch (uid) {
-                case ROOT_UID:
-                case SYSTEM_UID:
-                case NFC_UID:
-                case SHELL_UID:
-                    isSystem = true;
-                    break;
-                default:
-                    isSystem = false;
-                    break;
-            }
-
-            if (isSystem) {
-                ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_UID;
-            }
+        int ret = REASON_DENIED;
+        boolean isSystem = false;
+        final int uid = UserHandle.getAppId(mApp.info.uid);
+        switch (uid) {
+            case ROOT_UID:
+            case SYSTEM_UID:
+            case NFC_UID:
+            case SHELL_UID:
+                isSystem = true;
+                break;
+            default:
+                isSystem = false;
+                break;
         }
 
-        if (ret == FGS_FEATURE_DENIED) {
+        if (isSystem) {
+            ret = REASON_SYSTEM_UID;
+        }
+
+        if (ret == REASON_DENIED) {
             if (ActivityManager.checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND,
                     mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
-                ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION;
+                ret = REASON_BACKGROUND_ACTIVITY_PERMISSION;
             } else if (ActivityManager.checkComponentPermission(
                     START_FOREGROUND_SERVICES_FROM_BACKGROUND,
                     mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
-                ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION;
+                ret = REASON_BACKGROUND_FGS_PERMISSION;
             } else if (ActivityManager.checkComponentPermission(SYSTEM_ALERT_WINDOW,
                     mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
-                ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION;
+                ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
             }
         }
         mAllowStartFgs = mAllowStartFgsByPermission = ret;
@@ -1197,59 +1201,65 @@
 
     @GuardedBy("mService")
     void setAllowStartFgs() {
-        if (mAllowStartFgs != FGS_FEATURE_DENIED) {
+        if (mAllowStartFgs != REASON_DENIED) {
             return;
         }
-        if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+        if (mAllowStartFgs == REASON_DENIED) {
             if (isAllowedStartFgsState()) {
-                mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+                mAllowStartFgs = getReasonCodeFromProcState(mAllowStartFgsState);
             }
         }
 
-        if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+        if (mAllowStartFgs == REASON_DENIED) {
             // Is the calling UID a device owner app?
             if (mService.mInternal != null) {
                 if (mService.mInternal.isDeviceOwner(mApp.info.uid)) {
-                    mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER;
+                    mAllowStartFgs = REASON_DEVICE_OWNER;
                 }
             }
         }
 
-        if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+        if (mAllowStartFgs == REASON_DENIED) {
             if (mService.mInternal != null) {
                 final boolean isCompanionApp = mService.mInternal.isAssociatedCompanionApp(
                         UserHandle.getUserId(mApp.info.uid), mApp.info.uid);
                 if (isCompanionApp) {
-                    mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_COMPANION_APP;
+                    mAllowStartFgs = REASON_COMPANION_DEVICE_MANAGER;
                 }
             }
         }
 
-        if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+        if (mAllowStartFgs == REASON_DENIED) {
             // Is the calling UID a profile owner app?
             if (mService.mInternal != null) {
                 if (mService.mInternal.isProfileOwner(mApp.info.uid)) {
-                    mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER;
+                    mAllowStartFgs = REASON_PROFILE_OWNER;
                 }
             }
         }
 
-        if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+        if (mAllowStartFgs == REASON_DENIED) {
             // uid is on DeviceIdleController's user/system allowlist
             // or AMS's FgsStartTempAllowList.
-            if (mService.isAllowlistedForFgsStartLOSP(mApp.info.uid)) {
-                mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
+            FgsStartTempAllowList.TempFgsAllowListEntry entry =
+                    mService.isAllowlistedForFgsStartLOSP(mApp.info.uid);
+            if (entry != null) {
+                if (entry == ActivityManagerService.FAKE_TEMP_ALLOWLIST_ENTRY) {
+                    mAllowStartFgs = REASON_SYSTEM_ALLOW_LISTED;
+                } else {
+                    mAllowStartFgs = entry.mReasonCode;
+                }
             }
         }
     }
 
     @GuardedBy("mService")
-    void setAllowStartFgs(@ActiveServices.FgsFeatureRetCode int allowStartFgs) {
+    void setAllowStartFgs(@ReasonCode int allowStartFgs) {
         mAllowStartFgs = allowStartFgs;
     }
 
     @GuardedBy("mService")
-    @ActiveServices.FgsFeatureRetCode int getAllowedStartFgs() {
+    @ReasonCode int getAllowedStartFgs() {
         return mAllowStartFgs;
     }
 
@@ -1291,9 +1301,9 @@
         pw.println();
         pw.print(prefix); pw.print("allowStartFgsState=");
         pw.println(mAllowStartFgsState);
-        if (mAllowStartFgs != FGS_FEATURE_DENIED) {
+        if (mAllowStartFgs != REASON_DENIED) {
             pw.print(prefix); pw.print("allowStartFgs=");
-            pw.println(fgsCodeToString(mAllowStartFgs));
+            pw.println(reasonCodeToString(mAllowStartFgs));
         }
         if (mHasShownUi || mApp.mProfile.hasPendingUiClean()) {
             pw.print(prefix); pw.print("hasShownUi="); pw.print(mHasShownUi);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 485087d..3ab95d1 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -18,6 +18,7 @@
 
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
+import static android.os.PowerWhitelistManager.REASON_DENIED;
 
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -36,6 +37,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
+import android.os.PowerWhitelistManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -101,7 +103,7 @@
     ProcessRecord isolatedProc; // keep track of isolated process, if requested
     ServiceState tracker; // tracking service execution, may be null
     ServiceState restartTracker; // tracking service restart
-    boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
+    boolean allowlistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
     boolean delayed;        // are we waiting to start this service in the background?
     boolean fgRequired;     // is the service required to go foreground after starting?
     boolean fgWaiting;      // is a timeout for going foreground already scheduled?
@@ -156,11 +158,14 @@
 
     // the most recent package that start/bind this service.
     String mRecentCallingPackage;
+    // the most recent uid that start/bind this service.
+    int mRecentCallingUid;
 
     // allow the service becomes foreground service? Service started from background may not be
     // allowed to become a foreground service.
-    @ActiveServices.FgsFeatureRetCode int mAllowStartForeground;
+    @PowerWhitelistManager.ReasonCode int mAllowStartForeground = REASON_DENIED;
     String mInfoAllowStartForeground;
+    FgsStartTempAllowList.TempFgsAllowListEntry mInfoTempFgsAllowListReason;
     boolean mLoggedInfoAllowStartForeground;
 
     String stringName;      // caching of toString
@@ -309,7 +314,7 @@
         if (isolatedProc != null) {
             isolatedProc.dumpDebug(proto, ServiceRecordProto.ISOLATED_PROC);
         }
-        proto.write(ServiceRecordProto.WHITELIST_MANAGER, whitelistManager);
+        proto.write(ServiceRecordProto.WHITELIST_MANAGER, allowlistManager);
         proto.write(ServiceRecordProto.DELAYED, delayed);
         if (isForeground || foregroundId != 0) {
             long fgToken = proto.start(ServiceRecordProto.FOREGROUND);
@@ -410,8 +415,8 @@
         if (isolatedProc != null) {
             pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
         }
-        if (whitelistManager) {
-            pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
+        if (allowlistManager) {
+            pw.print(prefix); pw.print("allowlistManager="); pw.println(allowlistManager);
         }
         if (mIsAllowedBgActivityStartsByBinding) {
             pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByBinding=");
@@ -429,6 +434,8 @@
                 pw.println(mAllowWhileInUsePermissionInFgs);
         pw.print(prefix); pw.print("recentCallingPackage=");
                 pw.println(mRecentCallingPackage);
+        pw.print(prefix); pw.print("recentCallingUid=");
+        pw.println(mRecentCallingUid);
         pw.print(prefix); pw.print("allowStartForeground=");
         pw.println(mAllowStartForeground);
         pw.print(prefix); pw.print("infoAllowStartForeground=");
@@ -894,13 +901,13 @@
         return false;
     }
 
-    public void updateWhitelistManager() {
-        whitelistManager = false;
+    public void updateAllowlistManager() {
+        allowlistManager = false;
         for (int conni=connections.size()-1; conni>=0; conni--) {
             ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
             for (int i=0; i<cr.size(); i++) {
                 if ((cr.get(i).flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
-                    whitelistManager = true;
+                    allowlistManager = true;
                     return;
                 }
             }
diff --git a/services/core/java/com/android/server/am/StrictModeViolationDialog.java b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
index 4cc1fc1..9dddd65 100644
--- a/services/core/java/com/android/server/am/StrictModeViolationDialog.java
+++ b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
@@ -82,6 +82,11 @@
                 DISMISS_TIMEOUT);
     }
 
+    @Override
+    protected void closeDialog() {
+        mHandler.obtainMessage(ACTION_OK).sendToTarget();
+    }
+
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
             synchronized (mService.mProcLock) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index a776458..44dcc20 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -339,7 +339,10 @@
     SparseIntArray mProfileOwners;
 
     @GuardedBy("this")
-    private CheckOpsDelegate mCheckOpsDelegate;
+    private CheckOpsDelegate mAppOpsPolicy;
+
+    @GuardedBy("this")
+    private CheckOpsDelegateDispatcher mCheckOpsDelegateDispatcher;
 
     /**
       * Reverse lookup for {@link AppOpsManager#opToSwitch(int)}. Initialized once and never
@@ -1770,6 +1773,17 @@
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
     }
 
+    /**
+     * Sets a policy for handling app ops.
+     *
+     * @param appOpsPolicy The policy.
+     */
+    public void setAppOpsPolicy(@Nullable CheckOpsDelegate appOpsPolicy) {
+        synchronized (AppOpsService.this) {
+            mAppOpsPolicy = appOpsPolicy;
+        }
+    }
+
     public void packageRemoved(int uid, String packageName) {
         synchronized (this) {
             UidState uidState = mUidStates.get(uid);
@@ -2868,13 +2882,19 @@
 
     public CheckOpsDelegate getAppOpsServiceDelegate() {
         synchronized (this) {
-            return mCheckOpsDelegate;
+            return (mCheckOpsDelegateDispatcher != null)
+                    ? mCheckOpsDelegateDispatcher.getCheckOpsDelegate()
+                    : null;
         }
     }
 
     public void setAppOpsServiceDelegate(CheckOpsDelegate delegate) {
         synchronized (this) {
-            mCheckOpsDelegate = delegate;
+            if (delegate != null) {
+                mCheckOpsDelegateDispatcher = new CheckOpsDelegateDispatcher(delegate);
+            } else {
+                mCheckOpsDelegateDispatcher = null;
+            }
         }
     }
 
@@ -2889,19 +2909,28 @@
     }
 
     private int checkOperationInternal(int code, int uid, String packageName, boolean raw) {
-        final CheckOpsDelegate delegate;
-        synchronized (this) {
-            delegate = mCheckOpsDelegate;
+        final CheckOpsDelegate policy;
+        final CheckOpsDelegateDispatcher delegateDispatcher;
+        synchronized (AppOpsService.this) {
+            policy = mAppOpsPolicy;
+            delegateDispatcher = mCheckOpsDelegateDispatcher;
         }
-        if (delegate == null) {
-            return checkOperationImpl(code, uid, packageName, raw);
+        if (policy != null) {
+            if (delegateDispatcher != null) {
+                return policy.checkOperation(code, uid, packageName, raw,
+                        delegateDispatcher::checkOperationImpl);
+            } else {
+                return policy.checkOperation(code, uid, packageName, raw,
+                        AppOpsService.this::checkOperationImpl);
+            }
+        } else if (delegateDispatcher != null) {
+            delegateDispatcher.getCheckOpsDelegate().checkOperation(code, uid,
+                    packageName, raw, AppOpsService.this::checkOperationImpl);
         }
-        return delegate.checkOperation(code, uid, packageName, raw,
-                    AppOpsService.this::checkOperationImpl);
+        return checkOperationImpl(code, uid, packageName, raw);
     }
 
-    private int checkOperationImpl(int code, int uid, String packageName,
-                boolean raw) {
+    private int checkOperationImpl(int code, int uid, String packageName, boolean raw) {
         verifyIncomingOp(code);
         verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
 
@@ -2956,15 +2985,25 @@
 
     @Override
     public int checkAudioOperation(int code, int usage, int uid, String packageName) {
-        final CheckOpsDelegate delegate;
-        synchronized (this) {
-            delegate = mCheckOpsDelegate;
+        final CheckOpsDelegate policy;
+        final CheckOpsDelegateDispatcher delegateDispatcher;
+        synchronized (AppOpsService.this) {
+            policy = mAppOpsPolicy;
+            delegateDispatcher = mCheckOpsDelegateDispatcher;
         }
-        if (delegate == null) {
-            return checkAudioOperationImpl(code, usage, uid, packageName);
+        if (policy != null) {
+            if (delegateDispatcher != null) {
+                return policy.checkAudioOperation(code, usage, uid, packageName,
+                        delegateDispatcher::checkAudioOperationImpl);
+            } else {
+                return policy.checkAudioOperation(code, usage, uid, packageName,
+                        AppOpsService.this::checkAudioOperationImpl);
+            }
+        } else if (delegateDispatcher != null) {
+            delegateDispatcher.getCheckOpsDelegate().checkAudioOperation(code, usage,
+                    uid, packageName, AppOpsService.this::checkAudioOperationImpl);
         }
-        return delegate.checkAudioOperation(code, usage, uid, packageName,
-                AppOpsService.this::checkAudioOperationImpl);
+        return checkAudioOperationImpl(code, usage, uid, packageName);
     }
 
     private int checkAudioOperationImpl(int code, int usage, int uid, String packageName) {
@@ -3078,17 +3117,29 @@
     @Override
     public int noteOperation(int code, int uid, String packageName, String attributionTag,
             boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage) {
-        final CheckOpsDelegate delegate;
-        synchronized (this) {
-            delegate = mCheckOpsDelegate;
+        final CheckOpsDelegate policy;
+        final CheckOpsDelegateDispatcher delegateDispatcher;
+        synchronized (AppOpsService.this) {
+            policy = mAppOpsPolicy;
+            delegateDispatcher = mCheckOpsDelegateDispatcher;
         }
-        if (delegate == null) {
-            return noteOperationImpl(code, uid, packageName, attributionTag,
-                    shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+        if (policy != null) {
+            if (delegateDispatcher != null) {
+                return policy.noteOperation(code, uid, packageName, attributionTag,
+                        shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+                        delegateDispatcher::noteOperationImpl);
+            } else {
+                return policy.noteOperation(code, uid, packageName, attributionTag,
+                        shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+                        AppOpsService.this::noteOperationImpl);
+            }
+        } else if (delegateDispatcher != null) {
+            delegateDispatcher.getCheckOpsDelegate().noteOperation(code, uid, packageName,
+                    attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+                    AppOpsService.this::noteOperationImpl);
         }
-        return delegate.noteOperation(code, uid, packageName, attributionTag,
-                shouldCollectAsyncNotedOp, message, shouldCollectMessage,
-                AppOpsService.this::noteOperationImpl);
+        return noteOperationImpl(code, uid, packageName, attributionTag,
+                shouldCollectAsyncNotedOp, message, shouldCollectMessage);
     }
 
     private int noteOperationImpl(int code, int uid, @Nullable String packageName,
@@ -6589,7 +6640,6 @@
         }
     }
 
-
     /**
      * Async task for writing note op stack trace, op code, package name and version to file
      * More specifically, writes all the collected ops from {@link #mNoteOpCallerStacktraces}
@@ -6726,4 +6776,34 @@
             }
         }
     }
+
+    private final class CheckOpsDelegateDispatcher {
+        private final @NonNull CheckOpsDelegate mCheckOpsDelegate;
+
+        CheckOpsDelegateDispatcher(@NonNull CheckOpsDelegate checkOpsDelegate) {
+            mCheckOpsDelegate = checkOpsDelegate;
+        }
+
+        public @NonNull CheckOpsDelegate getCheckOpsDelegate() {
+            return mCheckOpsDelegate;
+        }
+
+        public int checkOperationImpl(int code, int uid, String packageName, boolean raw) {
+            return mCheckOpsDelegate.checkOperation(code, uid, packageName, raw,
+                    AppOpsService.this::checkOperationImpl);
+        }
+
+        public int checkAudioOperationImpl(int code, int usage, int uid, String packageName) {
+            return mCheckOpsDelegate.checkAudioOperation(code, usage, uid, packageName,
+                    AppOpsService.this::checkAudioOperationImpl);
+        }
+
+        public int noteOperationImpl(int code, int uid, @Nullable String packageName,
+                @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
+                @Nullable String message, boolean shouldCollectMessage) {
+            return mCheckOpsDelegate.noteOperation(code, uid, packageName, featureId,
+                    shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+                    AppOpsService.this::noteOperationImpl);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f5b9417..8363c9d2 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6958,7 +6958,10 @@
     private void onSetVolumeIndexOnDevice(@NonNull DeviceVolumeUpdate update) {
         final VolumeStreamState streamState = mStreamStates[update.mStreamType];
         if (update.hasVolumeIndex()) {
-            final int index = update.getVolumeIndex();
+            int index = update.getVolumeIndex();
+            if (!checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) {
+                index = safeMediaVolumeIndex(update.mDevice);
+            }
             streamState.setIndex(index, update.mDevice, update.mCaller,
                     // trusted as index is always validated before message is posted
                     true /*hasModifyAudioSettings*/);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java
new file mode 100644
index 0000000..f35a520
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import com.android.internal.R;
+
+public class ReEnrollNotificationUtils {
+
+    private static final String NOTIFICATION_TAG = "FaceService";
+    private static final int NOTIFICATION_ID = 1;
+
+    public static void showReEnrollmentNotification(@NonNull Context context) {
+        final NotificationManager notificationManager =
+                context.getSystemService(NotificationManager.class);
+
+        final String name =
+                context.getString(R.string.face_recalibrate_notification_name);
+        final String title =
+                context.getString(R.string.face_recalibrate_notification_title);
+        final String content =
+                context.getString(R.string.face_recalibrate_notification_content);
+
+        final Intent intent = new Intent("android.settings.FACE_SETTINGS");
+        intent.setPackage("com.android.settings");
+
+        final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
+                0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
+                null /* options */, UserHandle.CURRENT);
+
+        final String channelName = "FaceEnrollNotificationChannel";
+
+        final NotificationChannel channel = new NotificationChannel(channelName, name,
+                NotificationManager.IMPORTANCE_HIGH);
+        final Notification notification = new Notification.Builder(context, channelName)
+                .setSmallIcon(R.drawable.ic_lock)
+                .setContentTitle(title)
+                .setContentText(content)
+                .setSubText(name)
+                .setOnlyAlertOnce(true)
+                .setLocalOnly(true)
+                .setAutoCancel(true)
+                .setCategory(Notification.CATEGORY_SYSTEM)
+                .setContentIntent(pendingIntent)
+                .setVisibility(Notification.VISIBILITY_SECRET)
+                .build();
+
+        notificationManager.createNotificationChannel(channel);
+        notificationManager.notifyAsUser(NOTIFICATION_TAG,
+                NOTIFICATION_ID, notification,
+                UserHandle.CURRENT);
+    }
+
+    public static void cancelNotification(@NonNull Context context) {
+        final NotificationManager notificationManager =
+                context.getSystemService(NotificationManager.class);
+        notificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID, UserHandle.CURRENT);
+    }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 8f55402..089cf1e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -41,6 +41,7 @@
 import com.android.server.biometrics.sensors.LockoutCache;
 import com.android.server.biometrics.sensors.LockoutConsumer;
 import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
 import com.android.server.biometrics.sensors.face.UsageStats;
 
 import java.util.ArrayList;
@@ -163,6 +164,9 @@
                     vibrateError();
                 }
                 break;
+            case BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL:
+                ReEnrollNotificationUtils.showReEnrollmentNotification(getContext());
+                break;
             default:
                 break;
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 898d81b..0eb51fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -41,6 +41,7 @@
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.face.FaceUtils;
+import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -85,6 +86,13 @@
     }
 
     @Override
+    public void start(@NonNull Callback callback) {
+        super.start(callback);
+
+        ReEnrollNotificationUtils.cancelNotification(getContext());
+    }
+
+    @Override
     public void destroy() {
         try {
             AidlNativeHandleUtils.close(mPreviewSurface);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index ee8823e..1b9bd7f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.NotificationManager;
 import android.app.SynchronousUserSwitchObserver;
 import android.app.UserSwitchObserver;
 import android.content.Context;
@@ -71,6 +70,7 @@
 import com.android.server.biometrics.sensors.RemovalConsumer;
 import com.android.server.biometrics.sensors.face.FaceUtils;
 import com.android.server.biometrics.sensors.face.LockoutHalImpl;
+import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
 import com.android.server.biometrics.sensors.face.ServiceProvider;
 import com.android.server.biometrics.sensors.face.UsageStats;
 
@@ -96,8 +96,6 @@
 
     private static final String TAG = "Face10";
     private static final int ENROLL_TIMEOUT_SEC = 75;
-    static final String NOTIFICATION_TAG = "FaceService";
-    static final int NOTIFICATION_ID = 1;
 
     private boolean mTestHalEnabled;
 
@@ -109,7 +107,6 @@
     @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
     @NonNull private final LockoutHalImpl mLockoutTracker;
     @NonNull private final UsageStats mUsageStats;
-    @NonNull private final NotificationManager mNotificationManager;
     @NonNull private final Map<Integer, Long> mAuthenticatorIds;
     @Nullable private IBiometricsFace mDaemon;
     @NonNull private final HalResultController mHalResultController;
@@ -343,7 +340,6 @@
         mUsageStats = new UsageStats(context);
         mAuthenticatorIds = new HashMap<>();
         mLazyDaemon = Face10.this::getDaemon;
-        mNotificationManager = mContext.getSystemService(NotificationManager.class);
         mLockoutTracker = new LockoutHalImpl();
         mLockoutResetDispatcher = lockoutResetDispatcher;
         mHalResultController = new HalResultController(sensorId, context, mHandler, mScheduler,
@@ -607,8 +603,7 @@
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
-            mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
-                    UserHandle.CURRENT);
+            ReEnrollNotificationUtils.cancelNotification(mContext);
 
             final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
                     new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index a4b3ac5..3ca51d3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -17,12 +17,7 @@
 package com.android.server.biometrics.sensors.face.hidl;
 
 import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Resources;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
@@ -32,7 +27,6 @@
 import android.hardware.face.FaceManager;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -40,6 +34,7 @@
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
 import com.android.server.biometrics.sensors.face.UsageStats;
 
 import java.util.ArrayList;
@@ -52,7 +47,7 @@
 
     private static final String TAG = "FaceAuthenticationClient";
 
-    private final NotificationManager mNotificationManager;
+
     private final UsageStats mUsageStats;
 
     private final int[] mBiometricPromptIgnoreList;
@@ -72,7 +67,6 @@
                 owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
                 BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
                 lockoutTracker, isKeyguard);
-        mNotificationManager = context.getSystemService(NotificationManager.class);
         mUsageStats = usageStats;
 
         final Resources resources = getContext().getResources();
@@ -188,41 +182,7 @@
         mLastAcquire = acquireInfo;
 
         if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) {
-            final String name =
-                    getContext().getString(R.string.face_recalibrate_notification_name);
-            final String title =
-                    getContext().getString(R.string.face_recalibrate_notification_title);
-            final String content =
-                    getContext().getString(R.string.face_recalibrate_notification_content);
-
-            final Intent intent = new Intent("android.settings.FACE_SETTINGS");
-            intent.setPackage("com.android.settings");
-
-            final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(getContext(),
-                    0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
-                    null /* options */, UserHandle.CURRENT);
-
-            final String channelName = "FaceEnrollNotificationChannel";
-
-            NotificationChannel channel = new NotificationChannel(channelName, name,
-                    NotificationManager.IMPORTANCE_HIGH);
-            Notification notification = new Notification.Builder(getContext(), channelName)
-                    .setSmallIcon(R.drawable.ic_lock)
-                    .setContentTitle(title)
-                    .setContentText(content)
-                    .setSubText(name)
-                    .setOnlyAlertOnce(true)
-                    .setLocalOnly(true)
-                    .setAutoCancel(true)
-                    .setCategory(Notification.CATEGORY_SYSTEM)
-                    .setContentIntent(pendingIntent)
-                    .setVisibility(Notification.VISIBILITY_SECRET)
-                    .build();
-
-            mNotificationManager.createNotificationChannel(channel);
-            mNotificationManager.notifyAsUser(Face10.NOTIFICATION_TAG,
-                    Face10.NOTIFICATION_ID, notification,
-                    UserHandle.CURRENT);
+            ReEnrollNotificationUtils.showReEnrollmentNotification(getContext());
         }
 
         final boolean shouldSend = shouldSend(acquireInfo, vendorCode);
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 5076007..2de709e 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -61,7 +61,6 @@
 
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.UiThread;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
@@ -207,7 +206,7 @@
                                          new ClipData.Item(contents));
                         synchronized(mClipboards) {
                             setPrimaryClipInternal(getClipboard(0), clip,
-                                    android.os.Process.SYSTEM_UID);
+                                    android.os.Process.SYSTEM_UID, null);
                         }
                     }
                 });
@@ -247,6 +246,8 @@
         ClipData primaryClip;
         /** UID that set {@link #primaryClip}. */
         int primaryClipUid = android.os.Process.NOBODY_UID;
+        /** Package of the app that set {@link #primaryClip}. */
+        String mPrimaryClipPackage;
 
         final HashSet<String> activePermissionOwners
                 = new HashSet<String>();
@@ -365,7 +366,7 @@
                     return;
                 }
                 checkDataOwnerLocked(clip, intendingUid);
-                setPrimaryClipInternal(clip, intendingUid);
+                setPrimaryClipInternal(clip, intendingUid, callingPackage);
             }
         }
 
@@ -378,7 +379,7 @@
                         intendingUid, intendingUserId)) {
                     return;
                 }
-                setPrimaryClipInternal(null, intendingUid);
+                setPrimaryClipInternal(null, intendingUid, callingPackage);
             }
         }
 
@@ -509,6 +510,11 @@
     }
 
     void setPrimaryClipInternal(@Nullable ClipData clip, int uid) {
+        setPrimaryClipInternal(clip, uid, null);
+    }
+
+    private void setPrimaryClipInternal(
+            @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
         // Push clipboard to host, if any
         if (mHostClipboardMonitor != null) {
             if (clip == null) {
@@ -524,7 +530,7 @@
 
         // Update this user
         final int userId = UserHandle.getUserId(uid);
-        setPrimaryClipInternal(getClipboard(userId), clip, uid);
+        setPrimaryClipInternal(getClipboard(userId), clip, uid, sourcePackage);
 
         // Update related users
         List<UserInfo> related = getRelatedProfiles(userId);
@@ -558,7 +564,8 @@
                         final boolean canCopyIntoProfile = !hasRestriction(
                                 UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
                         if (canCopyIntoProfile) {
-                            setPrimaryClipInternal(getClipboard(id), clip, uid);
+                            setPrimaryClipInternal(
+                                    getClipboard(id), clip, uid, sourcePackage);
                         }
                     }
                 }
@@ -568,6 +575,11 @@
 
     void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,
             int uid) {
+        setPrimaryClipInternal(clipboard, clip, uid, null);
+    }
+
+    private void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,
+            int uid, @Nullable String sourcePackage) {
         revokeUris(clipboard);
         clipboard.activePermissionOwners.clear();
         if (clip == null && clipboard.primaryClip == null) {
@@ -576,8 +588,10 @@
         clipboard.primaryClip = clip;
         if (clip != null) {
             clipboard.primaryClipUid = uid;
+            clipboard.mPrimaryClipPackage = sourcePackage;
         } else {
             clipboard.primaryClipUid = android.os.Process.NOBODY_UID;
+            clipboard.mPrimaryClipPackage = null;
         }
         if (clip != null) {
             final ClipDescription description = clip.getDescription();
@@ -861,29 +875,34 @@
                 && mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) {
             return;
         }
-        // Load the labels for the calling app and the app that set the clipboard content.
-        final long ident = Binder.clearCallingIdentity();
+
+        // Retrieve the app label of the source of the clip data
+        CharSequence sourceAppLabel = null;
+        if (clipboard.mPrimaryClipPackage != null) {
+            try {
+                sourceAppLabel = mPm.getApplicationLabel(
+                        mPm.getApplicationInfo(clipboard.mPrimaryClipPackage, 0));
+            } catch (PackageManager.NameNotFoundException e) {
+                // leave label as null
+            }
+        }
+
         try {
-            final IPackageManager pm = AppGlobals.getPackageManager();
+            CharSequence callingAppLabel = mPm.getApplicationLabel(
+                    mPm.getApplicationInfo(callingPackage, 0));
             String message;
-            final CharSequence callingLabel = mPm.getApplicationLabel(
-                    pm.getApplicationInfo(callingPackage, 0, userId));
-            final String[] packagesForUid = pm.getPackagesForUid(clipboard.primaryClipUid);
-            if (packagesForUid != null && packagesForUid.length > 0) {
-                final CharSequence clipLabel = mPm.getApplicationLabel(
-                        pm.getApplicationInfo(packagesForUid[0], 0,
-                                UserHandle.getUserId(clipboard.primaryClipUid)));
-                message = callingLabel + " pasted from " + clipLabel;
+            if (sourceAppLabel != null) {
+                message = callingAppLabel + " pasted from " + sourceAppLabel;
             } else {
-                message = callingLabel + " pasted from clipboard";
+                message = callingAppLabel + " pasted from clipboard";
             }
             Slog.i(TAG, message);
-            Toast.makeText(getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT)
-                    .show();
-        } catch (RemoteException e) {
-            /* ignore */
-        } finally {
-            Binder.restoreCallingIdentity(ident);
+            Binder.withCleanCallingIdentity(() ->
+                    Toast.makeText(getContext(), UiThread.get().getLooper(), message,
+                            Toast.LENGTH_SHORT)
+                            .show());
+        } catch (PackageManager.NameNotFoundException e) {
+            // do nothing
         }
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 713ac73..01ac81fb 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1510,7 +1510,7 @@
             if (start != -1) ranges.add(new UidRange(start, stop));
         } else if (disallowedApplications != null) {
             // Add all ranges for user skipping UIDs for disallowedApplications.
-            final UidRange userRange = UidRange.createForUser(userId);
+            final UidRange userRange = UidRange.createForUser(UserHandle.of(userId));
             int start = userRange.start;
             for (int uid : getAppsUids(disallowedApplications, userId)) {
                 if (uid == start) {
@@ -1523,7 +1523,7 @@
             if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop));
         } else {
             // Add all UIDs for the user.
-            ranges.add(UidRange.createForUser(userId));
+            ranges.add(UidRange.createForUser(UserHandle.of(userId)));
         }
     }
 
@@ -1532,7 +1532,7 @@
     private static List<UidRange> uidRangesForUser(int userId, Set<UidRange> existingRanges) {
         // UidRange#createForUser returns the entire range of UIDs available to a macro-user.
         // This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE}
-        final UidRange userRange = UidRange.createForUser(userId);
+        final UidRange userRange = UidRange.createForUser(UserHandle.of(userId));
         final List<UidRange> ranges = new ArrayList<>();
         for (UidRange range : existingRanges) {
             if (userRange.containsRange(range)) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index fe89903..7b9ca37 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -264,6 +264,10 @@
 
     private final SyncLogger mLogger;
 
+    // NOTE: this is a temporary allow-list for testing purposes; it will be removed before release.
+    private final String[] mEjSyncAllowedPackages = new String[]{
+            "com.google.android.google", "com.android.frameworks.servicestests"};
+
     private boolean isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs) {
         for (JobInfo job: pendingJobs) {
             if (job.getId() == jobId) {
@@ -983,6 +987,14 @@
             }
         }
 
+        final boolean scheduleAsEj =
+                extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false);
+        // NOTE: this is a temporary check for internal testing - to be removed before release.
+        if (scheduleAsEj && !ArrayUtils.contains(mEjSyncAllowedPackages, callingPackage)) {
+            throw new IllegalArgumentException(
+                    callingPackage + " is not allowed to schedule a sync as an EJ yet.");
+        }
+
         for (AccountAndUser account : accounts) {
             // If userId is specified, do not sync accounts of other users
             if (userId >= UserHandle.USER_SYSTEM && account.userId >= UserHandle.USER_SYSTEM
@@ -1490,6 +1502,12 @@
                         + logSafe(syncOperation.target));
                 backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE,
                         SyncStorageEngine.NOT_IN_BACKOFF_MODE);
+            } else {
+                // if an EJ is being backed-off but doesn't have SYNC_EXTRAS_IGNORE_BACKOFF set,
+                // reschedule it as a regular job
+                if (syncOperation.isScheduledAsExpeditedJob()) {
+                    syncOperation.scheduleEjAsRegularJob = true;
+                }
             }
             long now = SystemClock.elapsedRealtime();
             long backoffDelay = backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE ? 0
@@ -1640,6 +1658,10 @@
             b.setRequiresCharging(true);
         }
 
+        if (syncOperation.isScheduledAsExpeditedJob() && !syncOperation.scheduleEjAsRegularJob) {
+            b.setExpedited(true);
+        }
+
         if (syncOperation.syncExemptionFlag
                 == ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP) {
             DeviceIdleInternal dic =
@@ -3951,6 +3973,9 @@
         if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) {
             return true;
         }
+        if (key.equals(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB)) {
+            return true;
+        }
         if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) {
             return true;
         }
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 4787635..c8654d1 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -103,6 +103,13 @@
     /** Stores the number of times this sync operation failed and had to be retried. */
     int retries;
 
+    /**
+     * Indicates if a sync that was originally scheduled as an EJ is being re-scheduled as a
+     * regular job. Specifically, this will be {@code true} if a sync is being backed-off but
+     * {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF} is not set.
+     */
+    boolean scheduleEjAsRegularJob;
+
     /** jobId of the JobScheduler job corresponding to this sync */
     public int jobId;
 
@@ -408,6 +415,12 @@
         if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
             sb.append(" EXPEDITED");
         }
+        if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false)) {
+            sb.append(" EXPEDITED-JOB");
+            if (scheduleEjAsRegularJob) {
+                sb.append("(scheduled-as-regular)");
+            }
+        }
         switch (syncExemptionFlag) {
             case ContentResolver.SYNC_EXEMPTION_NONE:
                 break;
@@ -537,6 +550,11 @@
         return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, false);
     }
 
+    boolean isScheduledAsExpeditedJob() {
+        return mImmutableExtras.getBoolean(
+                ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false);
+    }
+
     boolean isAppStandbyExempted() {
         return syncExemptionFlag != ContentResolver.SYNC_EXEMPTION_NONE;
     }
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index a62f67a..30cbf27 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -61,7 +61,10 @@
     private static final Plog PLOG = Plog.createSystemPlog(TAG);
 
     @Nullable
-    public static BrightnessMappingStrategy create(Resources resources) {
+    public static BrightnessMappingStrategy create(Resources resources,
+            DisplayDeviceConfig displayDeviceConfig) {
+
+        // Display independent values
         float[] luxLevels = getLuxLevels(resources.getIntArray(
                 com.android.internal.R.array.config_autoBrightnessLevels));
         int[] brightnessLevelsBacklight = resources.getIntArray(
@@ -71,32 +74,22 @@
         float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
                 com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
                 1, 1);
-
-        float[] nitsRange = getFloatArray(resources.obtainTypedArray(
-                com.android.internal.R.array.config_screenBrightnessNits));
-        int[] backlightRange = resources.getIntArray(
-                com.android.internal.R.array.config_screenBrightnessBacklight);
-
         long shortTermModelTimeout = resources.getInteger(
                 com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout);
 
-        if (isValidMapping(nitsRange, backlightRange)
+        // Display dependent values - used for physical mapping strategy nits -> brightness
+        final float[] nitsRange = displayDeviceConfig.getNits();
+        final float[] brightnessRange = displayDeviceConfig.getBrightness();
+
+        if (isValidMapping(nitsRange, brightnessRange)
                 && isValidMapping(luxLevels, brightnessLevelsNits)) {
-            int minimumBacklight = resources.getInteger(
-                    com.android.internal.R.integer.config_screenBrightnessSettingMinimum);
-            int maximumBacklight = resources.getInteger(
-                    com.android.internal.R.integer.config_screenBrightnessSettingMaximum);
-            if (backlightRange[0] > minimumBacklight
-                    || backlightRange[backlightRange.length - 1] < maximumBacklight) {
-                Slog.w(TAG, "Screen brightness mapping does not cover whole range of available " +
-                        "backlight values, autobrightness functionality may be impaired.");
-            }
+
             BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
                     luxLevels, brightnessLevelsNits);
             builder.setShortTermModelTimeoutMillis(shortTermModelTimeout);
             builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
             builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
-            return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange,
+            return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange,
                     autoBrightnessAdjustmentMaxGamma);
         } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) {
             return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight,
@@ -264,11 +257,11 @@
     public abstract boolean setAutoBrightnessAdjustment(float adjustment);
 
     /**
-     * Converts the provided backlight value to nits if possible.
+     * Converts the provided brightness value to nits if possible.
      *
-     * Returns -1.0f if there's no available mapping for the backlight to nits.
+     * Returns -1.0f if there's no available mapping for the brightness to nits.
      */
-    public abstract float convertToNits(int backlight);
+    public abstract float convertToNits(float brightness);
 
     /**
      * Adds a user interaction data point to the brightness mapping.
@@ -603,7 +596,7 @@
         }
 
         @Override
-        public float convertToNits(int backlight) {
+        public float convertToNits(float brightness) {
             return -1.0f;
         }
 
@@ -701,37 +694,39 @@
         // in nits.
         private Spline mBrightnessSpline;
 
-        // A spline mapping from nits to the corresponding backlight value, normalized to the range
+        // A spline mapping from nits to the corresponding brightness value, normalized to the range
         // [0, 1.0].
-        private Spline mNitsToBacklightSpline;
+        private Spline mNitsToBrightnessSpline;
+
+        // A spline mapping from the system brightness value, normalized to the range [0, 1.0], to
+        // a brightness in nits.
+        private Spline mBrightnessToNitsSpline;
 
         // The default brightness configuration.
         private final BrightnessConfiguration mDefaultConfig;
 
-        // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to
-        // a brightness in nits.
-        private Spline mBacklightToNitsSpline;
-
-        private float[] mNits;
-        private int[] mBacklight;
+        private final float[] mNits;
+        private final float[] mBrightness;
 
         private boolean mBrightnessRangeAdjustmentApplied;
 
-        private float mMaxGamma;
+        private final float mMaxGamma;
         private float mAutoBrightnessAdjustment;
         private float mUserLux;
         private float mUserBrightness;
 
         public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits,
-                                       int[] backlight, float maxGamma) {
-            Preconditions.checkArgument(nits.length != 0 && backlight.length != 0,
-                    "Nits and backlight arrays must not be empty!");
-            Preconditions.checkArgument(nits.length == backlight.length,
-                    "Nits and backlight arrays must be the same length!");
+                float[] brightness, float maxGamma) {
+
+            Preconditions.checkArgument(nits.length != 0 && brightness.length != 0,
+                    "Nits and brightness arrays must not be empty!");
+
+            Preconditions.checkArgument(nits.length == brightness.length,
+                    "Nits and brightness arrays must be the same length!");
             Objects.requireNonNull(config);
             Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits");
-            Preconditions.checkArrayElementsInRange(backlight,
-                    PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON, "backlight");
+            Preconditions.checkArrayElementsInRange(brightness,
+                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, "brightness");
 
             mMaxGamma = maxGamma;
             mAutoBrightnessAdjustment = 0;
@@ -739,7 +734,7 @@
             mUserBrightness = -1;
 
             mNits = nits;
-            mBacklight = backlight;
+            mBrightness = brightness;
             computeNitsBrightnessSplines(mNits);
 
             mDefaultConfig = config;
@@ -784,15 +779,15 @@
         public float getBrightness(float lux, String packageName,
                 @ApplicationInfo.Category int category) {
             float nits = mBrightnessSpline.interpolate(lux);
-            float backlight = mNitsToBacklightSpline.interpolate(nits);
+            float brightness = mNitsToBrightnessSpline.interpolate(nits);
             // Correct the brightness according to the current application and its category, but
-            // only if no user data point is set (as this will oevrride the user setting).
+            // only if no user data point is set (as this will override the user setting).
             if (mUserLux == -1) {
-                backlight = correctBrightness(backlight, packageName, category);
+                brightness = correctBrightness(brightness, packageName, category);
             } else if (mLoggingEnabled) {
                 Slog.d(TAG, "user point set, correction not applied");
             }
-            return backlight;
+            return brightness;
         }
 
         @Override
@@ -817,8 +812,8 @@
         }
 
         @Override
-        public float convertToNits(int backlight) {
-            return mBacklightToNitsSpline.interpolate(normalizeAbsoluteBrightness(backlight));
+        public float convertToNits(float brightness) {
+            return mBrightnessToNitsSpline.interpolate(brightness);
         }
 
         @Override
@@ -884,7 +879,8 @@
             pw.println("PhysicalMappingStrategy");
             pw.println("  mConfig=" + mConfig);
             pw.println("  mBrightnessSpline=" + mBrightnessSpline);
-            pw.println("  mNitsToBacklightSpline=" + mNitsToBacklightSpline);
+            pw.println("  mNitsToBrightnessSpline=" + mNitsToBrightnessSpline);
+            pw.println("  mBrightnessToNitsSpline=" + mBrightnessToNitsSpline);
             pw.println("  mMaxGamma=" + mMaxGamma);
             pw.println("  mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
             pw.println("  mUserLux=" + mUserLux);
@@ -894,31 +890,25 @@
         }
 
         private void computeNitsBrightnessSplines(float[] nits) {
-            final int len = nits.length;
-            float[] normalizedBacklight = new float[len];
-            for (int i = 0; i < len; i++) {
-                normalizedBacklight[i] = normalizeAbsoluteBrightness(mBacklight[i]);
-            }
-
-            mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);
-            mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
+            mNitsToBrightnessSpline = Spline.createSpline(nits, mBrightness);
+            mBrightnessToNitsSpline = Spline.createSpline(mBrightness, nits);
         }
 
         private void computeSpline() {
             Pair<float[], float[]> defaultCurve = mConfig.getCurve();
             float[] defaultLux = defaultCurve.first;
             float[] defaultNits = defaultCurve.second;
-            float[] defaultBacklight = new float[defaultNits.length];
-            for (int i = 0; i < defaultBacklight.length; i++) {
-                defaultBacklight[i] = mNitsToBacklightSpline.interpolate(defaultNits[i]);
+            float[] defaultBrightness = new float[defaultNits.length];
+            for (int i = 0; i < defaultBrightness.length; i++) {
+                defaultBrightness[i] = mNitsToBrightnessSpline.interpolate(defaultNits[i]);
             }
-            Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBacklight, mUserLux,
+            Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBrightness, mUserLux,
                     mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma);
             float[] lux = curve.first;
-            float[] backlight = curve.second;
-            float[] nits = new float[backlight.length];
+            float[] brightness = curve.second;
+            float[] nits = new float[brightness.length];
             for (int i = 0; i < nits.length; i++) {
-                nits[i] = mBacklightToNitsSpline.interpolate(backlight[i]);
+                nits[i] = mBrightnessToNitsSpline.interpolate(brightness[i]);
             }
             mBrightnessSpline = Spline.createSpline(lux, nits);
         }
@@ -926,7 +916,7 @@
         private float getUnadjustedBrightness(float lux) {
             Pair<float[], float[]> curve = mConfig.getCurve();
             Spline spline = Spline.createSpline(curve.first, curve.second);
-            return mNitsToBacklightSpline.interpolate(spline.interpolate(lux));
+            return mNitsToBrightnessSpline.interpolate(spline.interpolate(lux));
         }
 
         private float correctBrightness(float brightness, String packageName, int category) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 49328f1..0071b2f 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -18,9 +18,12 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.Environment;
 import android.os.PowerManager;
+import android.util.MathUtils;
 import android.util.Slog;
+import android.util.Spline;
 import android.view.DisplayAddress;
 
 import com.android.internal.R;
@@ -72,15 +75,31 @@
 
     private final Context mContext;
 
+    // Nits and backlight values that are loaded from either the display device config file, or
+    // config.xml. These are the raw values and just used for the dumpsys
+    private float[] mRawNits;
+    private float[] mRawBacklight;
+
+    // These arrays are calculated from the raw arrays, but clamped to contain values equal to and
+    // between mBacklightMinimum and mBacklightMaximum. These three arrays should all be the same
+    // length
+    // Nits array that is used to store the entire range of nits values that the device supports
     private float[] mNits;
+    // Backlight array holds the values that the HAL uses to display the corresponding nits values
+    private float[] mBacklight;
+    // Purely an array that covers the ranges of values 0.0 - 1.0, indicating the system brightness
+    // for the corresponding values above
     private float[] mBrightness;
-    private float mBrightnessMinimum = Float.NaN;
-    private float mBrightnessMaximum = Float.NaN;
+
+    private float mBacklightMinimum = Float.NaN;
+    private float mBacklightMaximum = Float.NaN;
     private float mBrightnessDefault = Float.NaN;
     private float mBrightnessRampFastDecrease = Float.NaN;
     private float mBrightnessRampFastIncrease = Float.NaN;
     private float mBrightnessRampSlowDecrease = Float.NaN;
     private float mBrightnessRampSlowIncrease = Float.NaN;
+    private Spline mBrightnessToBacklightSpline;
+    private Spline mBacklightToBrightnessSpline;
     private List<String> mQuirks;
     private boolean mIsHighBrightnessModeEnabled = false;
     private HighBrightnessModeData mHbmData;
@@ -167,7 +186,7 @@
     }
 
     /**
-     * Return the brightness mapping nits array if one is defined in the configuration file.
+     * Return the brightness mapping nits array.
      *
      * @return The brightness mapping nits array.
      */
@@ -176,22 +195,40 @@
     }
 
     /**
-     * Return the brightness mapping value array if one is defined in the configuration file.
+     * Return the brightness mapping backlight array.
      *
-     * @return The brightness mapping value array.
+     * @return The backlight mapping value array.
+     */
+    public float[] getBacklight() {
+        return mBacklight;
+    }
+
+    /**
+     * Calculates the backlight value, as recognised by the HAL, from the brightness value
+     * given that the rest of the system deals with.
+     *
+     * @param brightness value on the framework scale of 0-1
+     * @return backlight value on the HAL scale of 0-1
+     */
+    public float getBacklightFromBrightness(float brightness) {
+        return mBrightnessToBacklightSpline.interpolate(brightness);
+    }
+
+    /**
+     * Return an array of equal length to backlight and nits, that covers the entire system
+     * brightness range of 0.0-1.0.
+     *
+     * @return brightness array
      */
     public float[] getBrightness() {
         return mBrightness;
     }
 
-    public float getBrightnessMinimum() {
-        return mBrightnessMinimum;
-    }
-
-    public float getBrightnessMaximum() {
-        return mBrightnessMaximum;
-    }
-
+    /**
+     * Return the default brightness on a scale of 0.0f - 1.0f
+     *
+     * @return default brightness
+     */
     public float getBrightnessDefault() {
         return mBrightnessDefault;
     }
@@ -237,10 +274,15 @@
     @Override
     public String toString() {
         String str = "DisplayDeviceConfig{"
-                + "mBrightness=" + Arrays.toString(mBrightness)
+                + "mBacklight=" + Arrays.toString(mBacklight)
                 + ", mNits=" + Arrays.toString(mNits)
-                + ", mBrightnessMinimum=" + mBrightnessMinimum
-                + ", mBrightnessMaximum=" + mBrightnessMaximum
+                + ", mRawBacklight=" + Arrays.toString(mRawBacklight)
+                + ", mRawNits=" + Arrays.toString(mRawNits)
+                + ", mBrightness=" + Arrays.toString(mBrightness)
+                + ", mBrightnessToBacklightSpline=" + mBrightnessToBacklightSpline
+                + ", mBacklightToBrightnessSpline=" + mBacklightToBrightnessSpline
+                + ", mBacklightMinimum=" + mBacklightMinimum
+                + ", mBacklightMaximum=" + mBacklightMaximum
                 + ", mBrightnessDefault=" + mBrightnessDefault
                 + ", mQuirks=" + mQuirks
                 + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
@@ -253,10 +295,6 @@
         return str;
     }
 
-    private float getMaxBrightness() {
-        return mBrightness[mBrightness.length - 1];
-    }
-
     private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
             String suffixFormat, long idNumber) {
 
@@ -264,7 +302,6 @@
         final String filename = String.format(CONFIG_FILE_FORMAT, suffix);
         final File filePath = Environment.buildPath(
                 baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
-
         if (filePath.exists()) {
             final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
             config.initFromFile(filePath);
@@ -299,9 +336,9 @@
         try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
             final DisplayConfiguration config = XmlParser.read(in);
             if (config != null) {
-                loadBrightnessMap(config);
                 loadBrightnessDefaultFromDdcXml(config);
                 loadBrightnessConstraintsFromConfigXml();
+                loadBrightnessMap(config);
                 loadHighBrightnessModeData(config);
                 loadQuirks(config);
                 loadBrightnessRamps(config);
@@ -318,13 +355,20 @@
         // If no ddc exists, use config.xml
         loadBrightnessDefaultFromConfigXml();
         loadBrightnessConstraintsFromConfigXml();
+        loadBrightnessMapFromConfigXml();
         loadBrightnessRampsFromConfigXml();
     }
 
     private void initFromPmValues() {
-        mBrightnessMinimum = PowerManager.BRIGHTNESS_MIN;
-        mBrightnessMaximum = PowerManager.BRIGHTNESS_MAX;
+        // Set all to basic values
+        mBacklightMinimum = PowerManager.BRIGHTNESS_MIN;
+        mBacklightMaximum = PowerManager.BRIGHTNESS_MAX;
         mBrightnessDefault = BRIGHTNESS_DEFAULT;
+        mBrightnessRampFastDecrease = PowerManager.BRIGHTNESS_MAX;
+        mBrightnessRampFastIncrease = PowerManager.BRIGHTNESS_MAX;
+        mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX;
+        mBrightnessRampSlowIncrease = PowerManager.BRIGHTNESS_MAX;
+        setSimpleMappingStrategyValues();
     }
 
     private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) {
@@ -364,24 +408,27 @@
         final float max = mContext.getResources().getFloat(com.android.internal.R.dimen
                 .config_screenBrightnessSettingMaximumFloat);
         if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG) {
-            mBrightnessMinimum = BrightnessSynchronizer.brightnessIntToFloat(
+            mBacklightMinimum = BrightnessSynchronizer.brightnessIntToFloat(
                     mContext.getResources().getInteger(com.android.internal.R.integer
                             .config_screenBrightnessSettingMinimum));
-            mBrightnessMaximum = BrightnessSynchronizer.brightnessIntToFloat(
+            mBacklightMaximum = BrightnessSynchronizer.brightnessIntToFloat(
                     mContext.getResources().getInteger(com.android.internal.R.integer
                             .config_screenBrightnessSettingMaximum));
         } else {
-            mBrightnessMinimum = min;
-            mBrightnessMaximum = max;
+            mBacklightMinimum = min;
+            mBacklightMaximum = max;
         }
     }
 
     private void loadBrightnessMap(DisplayConfiguration config) {
         final NitsMap map = config.getScreenBrightnessMap();
-        // Map may not exist in config file
+        // Map may not exist in display device config
         if (map == null) {
+            loadBrightnessMapFromConfigXml();
             return;
         }
+
+        // Use the (preferred) display device config mapping
         final List<Point> points = map.getPoint();
         final int size = points.size();
 
@@ -408,8 +455,123 @@
             }
             ++i;
         }
-        mNits = nits;
-        mBrightness = backlight;
+        mRawNits = nits;
+        mRawBacklight = backlight;
+        constrainNitsAndBacklightArrays();
+    }
+
+    private void loadBrightnessMapFromConfigXml() {
+        // Use the config.xml mapping
+        final Resources res = mContext.getResources();
+        final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray(
+                com.android.internal.R.array.config_screenBrightnessNits));
+        final int[] sysBrightness = res.getIntArray(
+                com.android.internal.R.array.config_screenBrightnessBacklight);
+        final float[] sysBrightnessFloat = new float[sysBrightness.length];
+
+        for (int i = 0; i < sysBrightness.length; i++) {
+            sysBrightnessFloat[i] = BrightnessSynchronizer.brightnessIntToFloat(
+                    sysBrightness[i]);
+        }
+
+        // These arrays are allowed to be empty, we set null values so that
+        // BrightnessMappingStrategy will create a SimpleMappingStrategy instead.
+        if (sysBrightnessFloat.length == 0 || sysNits.length == 0) {
+            setSimpleMappingStrategyValues();
+            return;
+        }
+
+        mRawNits = sysNits;
+        mRawBacklight = sysBrightnessFloat;
+        constrainNitsAndBacklightArrays();
+    }
+
+    private void setSimpleMappingStrategyValues() {
+        // No translation from backlight to brightness should occur if we are using a
+        // SimpleMappingStrategy (ie they should be the same) so the splines are
+        // set to be linear, between 0.0 and 1.0
+        mNits = null;
+        mBacklight = null;
+        float[] simpleMappingStrategyArray = new float[]{0.0f, 1.0f};
+        mBrightnessToBacklightSpline = Spline.createSpline(simpleMappingStrategyArray,
+                simpleMappingStrategyArray);
+        mBacklightToBrightnessSpline = Spline.createSpline(simpleMappingStrategyArray,
+                simpleMappingStrategyArray);
+    }
+
+    /**
+     * Change the nits and backlight arrays, so that they cover only the allowed backlight values
+     * Use the brightness minimum and maximum values to clamp these arrays.
+     */
+    private void constrainNitsAndBacklightArrays() {
+        if (mRawBacklight[0] > mBacklightMinimum
+                || mRawBacklight[mRawBacklight.length - 1] < mBacklightMaximum
+                || mBacklightMinimum > mBacklightMaximum) {
+            throw new IllegalStateException("Min or max values are invalid"
+                    + "; raw min=" + mRawBacklight[0]
+                    + "; raw max=" + mRawBacklight[mRawBacklight.length - 1]
+                    + "; backlight min=" + mBacklightMinimum
+                    + "; backlight max=" + mBacklightMaximum);
+        }
+
+        float[] newNits = new float[mRawBacklight.length];
+        float[] newBacklight = new float[mRawBacklight.length];
+        // Find the starting index of the clamped arrays. This may be less than the min so
+        // we'll need to clamp this value still when actually doing the remapping.
+        int newStart = 0;
+        for (int i = 0; i < mRawBacklight.length - 1; i++) {
+            if (mRawBacklight[i + 1] > mBacklightMinimum) {
+                newStart = i;
+                break;
+            }
+        }
+
+        boolean isLastValue = false;
+        int newIndex = 0;
+        for (int i = newStart; i < mRawBacklight.length && !isLastValue; i++) {
+            newIndex = i - newStart;
+            final float newBacklightVal;
+            final float newNitsVal;
+            isLastValue = mRawBacklight[i] > mBacklightMaximum
+                    || i >= mRawBacklight.length - 1;
+            // Clamp beginning and end to valid backlight values.
+            if (newIndex == 0) {
+                newBacklightVal = MathUtils.max(mRawBacklight[i], mBacklightMinimum);
+                newNitsVal = rawBacklightToNits(i, newBacklightVal);
+            } else if (isLastValue) {
+                newBacklightVal = MathUtils.min(mRawBacklight[i], mBacklightMaximum);
+                newNitsVal = rawBacklightToNits(i - 1, newBacklightVal);
+            } else {
+                newBacklightVal = mRawBacklight[i];
+                newNitsVal = mRawNits[i];
+            }
+            newBacklight[newIndex] = newBacklightVal;
+            newNits[newIndex] = newNitsVal;
+        }
+        mBacklight = Arrays.copyOf(newBacklight, newIndex + 1);
+        mNits = Arrays.copyOf(newNits, newIndex + 1);
+        createBacklightConversionSplines();
+    }
+
+    private float rawBacklightToNits(int i, float backlight) {
+        return MathUtils.map(mRawBacklight[i], mRawBacklight[i + 1],
+                mRawNits[i], mRawNits[i + 1], backlight);
+    }
+
+    // This method creates a brightness spline that is of equal length with proportional increments
+    // to the backlight spline. The values of this array range from 0.0f to 1.0f instead of the
+    // potential constrained range that the backlight array covers
+    // These splines are used to convert from the system brightness value to the HAL backlight
+    // value
+    private void createBacklightConversionSplines() {
+        mBrightness = new float[mBacklight.length];
+        for (int i = 0; i < mBrightness.length; i++) {
+            mBrightness[i] = MathUtils.map(mBacklight[0],
+                    mBacklight[mBacklight.length - 1],
+                    PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, mBacklight[i]);
+        }
+        mBrightnessToBacklightSpline = Spline.createSpline(mBrightness, mBacklight);
+        mBacklightToBrightnessSpline = Spline.createSpline(mBacklight, mBrightness);
     }
 
     private void loadQuirks(DisplayConfiguration config) {
@@ -425,12 +587,14 @@
             mIsHighBrightnessModeEnabled = hbm.getEnabled();
             mHbmData = new HighBrightnessModeData();
             mHbmData.minimumLux = hbm.getMinimumLux_all().floatValue();
-            mHbmData.transitionPoint = hbm.getTransitionPoint_all().floatValue();
-            if (mHbmData.transitionPoint >= getMaxBrightness()) {
+            float transitionPointBacklightScale = hbm.getTransitionPoint_all().floatValue();
+            if (transitionPointBacklightScale >= mBacklightMaximum) {
                 throw new IllegalArgumentException("HBM transition point invalid. "
                         + mHbmData.transitionPoint + " is not less than "
-                        + getMaxBrightness());
+                        + mBacklightMaximum);
             }
+            mHbmData.transitionPoint =
+                    mBacklightToBrightnessSpline.interpolate(transitionPointBacklightScale);
             final HbmTiming hbmTiming = hbm.getTiming_all();
             mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000;
             mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 62cf86b..86142bc 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -190,12 +190,6 @@
     // The dim screen brightness.
     private final float mScreenBrightnessDimConfig;
 
-    // The minimum allowed brightness.
-    private final float mScreenBrightnessRangeMinimum;
-
-    // The maximum allowed brightness.
-    private final float mScreenBrightnessRangeMaximum;
-
     private final float mScreenBrightnessDefault;
 
     // The minimum allowed brightness while in VR.
@@ -443,8 +437,6 @@
 
         final Resources resources = context.getResources();
 
-        final float screenBrightnessSettingMinimumFloat = clampAbsoluteBrightness(
-                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM));
 
         // DOZE AND DIM SETTINGS
         mScreenBrightnessDozeConfig = clampAbsoluteBrightness(
@@ -453,10 +445,6 @@
                 pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
 
         // NORMAL SCREEN SETTINGS
-        mScreenBrightnessRangeMinimum =
-                Math.min(screenBrightnessSettingMinimumFloat, mScreenBrightnessDimConfig);
-        mScreenBrightnessRangeMaximum = clampAbsoluteBrightness(
-                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM));
         mScreenBrightnessDefault = clampAbsoluteBrightness(
                 mLogicalDisplay.getDisplayInfoLocked().brightnessDefault);
 
@@ -481,18 +469,10 @@
 
         DisplayDeviceConfig displayDeviceConfig = logicalDisplay
                 .getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
-        // TODO: (b/178183143) Ensure that the ddc is not null
-        if (displayDeviceConfig != null) {
-            mBrightnessRampRateFastDecrease = displayDeviceConfig.getBrightnessRampFastDecrease();
-            mBrightnessRampRateFastIncrease = displayDeviceConfig.getBrightnessRampFastIncrease();
-            mBrightnessRampRateSlowDecrease = displayDeviceConfig.getBrightnessRampSlowDecrease();
-            mBrightnessRampRateSlowIncrease = displayDeviceConfig.getBrightnessRampSlowIncrease();
-        } else {
-            mBrightnessRampRateFastDecrease = 1.0f;
-            mBrightnessRampRateFastIncrease = 1.0f;
-            mBrightnessRampRateSlowDecrease = 1.0f;
-            mBrightnessRampRateSlowIncrease = 1.0f;
-        }
+        mBrightnessRampRateFastDecrease = displayDeviceConfig.getBrightnessRampFastDecrease();
+        mBrightnessRampRateFastIncrease = displayDeviceConfig.getBrightnessRampFastIncrease();
+        mBrightnessRampRateSlowDecrease = displayDeviceConfig.getBrightnessRampSlowDecrease();
+        mBrightnessRampRateSlowIncrease = displayDeviceConfig.getBrightnessRampSlowIncrease();
         mSkipScreenOnBrightnessRamp = resources.getBoolean(
                 com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
 
@@ -545,12 +525,14 @@
                     com.android.internal.R.string.config_displayLightSensorType);
             Sensor lightSensor = findDisplayLightSensor(lightSensorType);
 
-            mBrightnessMapper = BrightnessMappingStrategy.create(resources);
+            final DisplayDeviceConfig ddc =
+                    logicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
+            mBrightnessMapper = BrightnessMappingStrategy.create(resources, ddc);
             if (mBrightnessMapper != null) {
                 mAutomaticBrightnessController = new AutomaticBrightnessController(this,
                         handler.getLooper(), sensorManager, lightSensor, mBrightnessMapper,
-                        lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum,
-                        mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
+                        lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN,
+                        PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate,
                         initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
                         autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
                         screenBrightnessThresholds, logicalDisplay, context);
@@ -838,9 +820,8 @@
         noteScreenBrightness(mPowerState.getScreenBrightness());
 
         // Initialize all of the brightness tracking state
-        final float brightness = convertToNits(BrightnessSynchronizer.brightnessFloatToInt(
-                mPowerState.getScreenBrightness()));
-        if (brightness >= 0.0f) {
+        final float brightness = convertToNits(mPowerState.getScreenBrightness());
+        if (brightness >= PowerManager.BRIGHTNESS_MIN) {
             mBrightnessTracker.start(brightness);
         }
 
@@ -1151,10 +1132,10 @@
         // Apply dimming by at least some minimum amount when user activity
         // timeout is about to expire.
         if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
-            if (brightnessState > mScreenBrightnessRangeMinimum) {
+            if (brightnessState > PowerManager.BRIGHTNESS_MIN) {
                 brightnessState = Math.max(Math.min(brightnessState
                                 - SCREEN_DIM_MINIMUM_REDUCTION_FLOAT,
-                        mScreenBrightnessDimConfig), mScreenBrightnessRangeMinimum);
+                        mScreenBrightnessDimConfig), PowerManager.BRIGHTNESS_MIN);
                 mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED);
             }
             if (!mAppliedDimming) {
@@ -1168,12 +1149,11 @@
         // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor
         // as long as it is above the minimum threshold.
         if (mPowerRequest.lowPowerMode) {
-            if (brightnessState > mScreenBrightnessRangeMinimum) {
+            if (brightnessState > PowerManager.BRIGHTNESS_MIN) {
                 final float brightnessFactor =
                         Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1);
                 final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor);
-                brightnessState = Math.max(lowPowerBrightnessFloat,
-                        mScreenBrightnessRangeMinimum);
+                brightnessState = Math.max(lowPowerBrightnessFloat, PowerManager.BRIGHTNESS_MIN);
                 mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER);
             }
             if (!mAppliedLowPower) {
@@ -1258,9 +1238,8 @@
                     // slider event so notify as if the system changed the brightness.
                     userInitiatedChange = false;
                 }
-                notifyBrightnessChanged(
-                        BrightnessSynchronizer.brightnessFloatToInt(brightnessState),
-                        userInitiatedChange, hadUserBrightnessPoint);
+                notifyBrightnessChanged(brightnessState, userInitiatedChange,
+                        hadUserBrightnessPoint);
             }
 
         }
@@ -1487,17 +1466,17 @@
 
     private float clampScreenBrightness(float value) {
         if (Float.isNaN(value)) {
-            return mScreenBrightnessRangeMinimum;
+            return PowerManager.BRIGHTNESS_MIN;
         }
         return MathUtils.constrain(
-                value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
+                value, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
     }
 
     // Checks whether the brightness is within the valid brightness range, not including the off or
     // invalid states.
     private boolean isValidBrightnessValue(float brightnessState) {
-        return brightnessState >= mScreenBrightnessRangeMinimum
-                && brightnessState <= mScreenBrightnessRangeMaximum;
+        return brightnessState >= PowerManager.BRIGHTNESS_MIN
+                && brightnessState <= PowerManager.BRIGHTNESS_MAX;
     }
 
     private void animateScreenBrightness(float target, float rate) {
@@ -1874,7 +1853,7 @@
         return true;
     }
 
-    private void notifyBrightnessChanged(int brightness, boolean userInitiated,
+    private void notifyBrightnessChanged(float brightness, boolean userInitiated,
             boolean hadUserDataPoint) {
         final float brightnessInNits = convertToNits(brightness);
         if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
@@ -1891,9 +1870,9 @@
         }
     }
 
-    private float convertToNits(int backlight) {
+    private float convertToNits(float brightness) {
         if (mBrightnessMapper != null) {
-            return mBrightnessMapper.convertToNits(backlight);
+            return mBrightnessMapper.convertToNits(brightness);
         } else {
             return -1.0f;
         }
@@ -1972,12 +1951,9 @@
 
         pw.println();
         pw.println("Display Power Controller Configuration:");
-        pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
-        pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
         pw.println("  mScreenBrightnessRangeDefault=" + mScreenBrightnessDefault);
         pw.println("  mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
         pw.println("  mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
-        pw.println("  mScreenBrightnessDefault=" + mScreenBrightnessDefault);
         pw.println("  mScreenBrightnessForVrRangeMinimum=" + mScreenBrightnessForVrRangeMinimum);
         pw.println("  mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum);
         pw.println("  mScreenBrightnessForVrDefault=" + mScreenBrightnessForVrDefault);
@@ -2109,10 +2085,6 @@
         }
     }
 
-    private static int clampAbsoluteBrightness(int value) {
-        return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
-    }
-
     private static float clampAbsoluteBrightness(float value) {
         return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN,
                 PowerManager.BRIGHTNESS_MAX);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4bbf227..3709963 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -30,7 +30,6 @@
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.Spline;
 import android.view.Display;
 import android.view.DisplayAddress;
 import android.view.DisplayCutout;
@@ -210,8 +209,7 @@
         private SurfaceControl.DisplayMode[] mSfDisplayModes;
         // The active display mode in SurfaceFlinger
         private SurfaceControl.DisplayMode mActiveSfDisplayMode;
-        private Spline mSystemBrightnessToNits;
-        private Spline mNitsToHalBrightness;
+        private DisplayDeviceConfig mDisplayDeviceConfig;
 
         private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides =
                 new DisplayEventReceiver.FrameRateOverride[0];
@@ -410,16 +408,13 @@
         @Override
         public DisplayDeviceConfig getDisplayDeviceConfig() {
             if (mDisplayDeviceConfig == null) {
-                loadDisplayConfiguration();
+                loadDisplayDeviceConfig();
             }
             return mDisplayDeviceConfig;
         }
 
-        private void loadDisplayConfiguration() {
-            Spline nitsToHal = null;
-            Spline sysToNits = null;
-
-            // Load the mapping from nits to HAL brightness range (display-device-config.xml)
+        private void loadDisplayDeviceConfig() {
+            // Load display device config
             final Context context = getOverlayContext();
             mDisplayDeviceConfig = DisplayDeviceConfig.create(context, mPhysicalDisplayId,
                     mIsDefaultDisplay);
@@ -427,33 +422,9 @@
                 return;
             }
 
+            // Load brightness HWC quirk
             mBacklightAdapter.setForceSurfaceControl(mDisplayDeviceConfig.hasQuirk(
                     DisplayDeviceConfig.QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC));
-
-            final float[] halNits = mDisplayDeviceConfig.getNits();
-            final float[] halBrightness = mDisplayDeviceConfig.getBrightness();
-            if (halNits == null || halBrightness == null) {
-                return;
-            }
-            nitsToHal = Spline.createSpline(halNits, halBrightness);
-
-            // Load the mapping from system brightness range to nits (config.xml)
-            final Resources res = context.getResources();
-            final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray(
-                    com.android.internal.R.array.config_screenBrightnessNits));
-            final int[] sysBrightness = res.getIntArray(
-                    com.android.internal.R.array.config_screenBrightnessBacklight);
-            if (sysNits.length == 0 || sysBrightness.length != sysNits.length) {
-                return;
-            }
-            final float[] sysBrightnessFloat = new float[sysBrightness.length];
-            for (int i = 0; i < sysBrightness.length; i++) {
-                sysBrightnessFloat[i] = sysBrightness[i];
-            }
-            sysToNits = Spline.createSpline(sysBrightnessFloat, sysNits);
-
-            mNitsToHalBrightness = nitsToHal;
-            mSystemBrightnessToNits = sysToNits;
         }
 
         private boolean updateStaticInfo(SurfaceControl.StaticDisplayInfo info) {
@@ -665,15 +636,9 @@
 
                 // The display is trusted since it is created by system.
                 mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
-                if (mDisplayDeviceConfig != null) {
-                    mInfo.brightnessMinimum = mDisplayDeviceConfig.getBrightnessMinimum();
-                    mInfo.brightnessMaximum = mDisplayDeviceConfig.getBrightnessMaximum();
-                    mInfo.brightnessDefault = mDisplayDeviceConfig.getBrightnessDefault();
-                } else {
-                    mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN;
-                    mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX;
-                    mInfo.brightnessDefault = 0.5f;
-                }
+                mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN;
+                mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX;
+                mInfo.brightnessDefault = getDisplayDeviceConfig().getBrightnessDefault();
             }
             return mInfo;
         }
@@ -718,7 +683,12 @@
                                 setDisplayState(Display.STATE_ON);
                                 currentState = Display.STATE_ON;
                             } else {
-                                return; // old state and new state is off
+                                if (oldState == Display.STATE_UNKNOWN) {
+                                    // There's no guarantee about what the initial state is
+                                    // at startup, so we have to set it if previous was UNKNOWN.
+                                    setDisplayState(state);
+                                }
+                                return;
                             }
                         }
 
@@ -806,8 +776,8 @@
                         Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
                                 + "id=" + physicalDisplayId + ", brightness=" + brightness + ")");
                         try {
-                            brightness = displayBrightnessToHalBrightness(brightness);
-                            mBacklightAdapter.setBrightness(brightness);
+                            float backlight = brightnessToBacklight(brightness);
+                            mBacklightAdapter.setBacklight(backlight);
                             Trace.traceCounter(Trace.TRACE_TAG_POWER,
                                     "ScreenBrightness",
                                     BrightnessSynchronizer.brightnessFloatToInt(brightness));
@@ -816,35 +786,8 @@
                         }
                     }
 
-                    /**
-                     * Converts brightness range from the framework's brightness space to the
-                     * Hal brightness space if the HAL brightness space has been provided via
-                     * a display device configuration file.
-                     */
-                    private float displayBrightnessToHalBrightness(float brightness) {
-                        // TODO: b/171380847 - This needs to be deprecated. The nits-to-brightness
-                        // relationship should be specified in display-config OR config.xml, but not
-                        // both, and no nits-space conversion should be necessary.
-                        //
-                        // Only do a conversion if there exists a unique system brightness and a
-                        // unique HAL brightness-to-nits range defined.
-                        if (mSystemBrightnessToNits == null || mNitsToHalBrightness == null) {
-                            return brightness;
-                        }
-
-                        // Sys brightness in this conversion is always specified in the old 1-255
-                        // range, so convert that here before the translation.
-                        final float brightnessInt =
-                                BrightnessSynchronizer.brightnessFloatToIntRange(brightness);
-
-                        if (BrightnessSynchronizer.floatEquals(
-                                brightnessInt, PowerManager.BRIGHTNESS_OFF)) {
-                            return PowerManager.BRIGHTNESS_OFF_FLOAT;
-                        }
-
-                        final float nits = mSystemBrightnessToNits.interpolate(brightnessInt);
-                        final float halBrightness = mNitsToHalBrightness.interpolate(nits);
-                        return halBrightness;
+                    private float brightnessToBacklight(float brightness) {
+                        return getDisplayDeviceConfig().getBacklightFromBrightness(brightness);
                     }
                 };
             }
@@ -1333,11 +1276,12 @@
             }
         }
 
-        void setBrightness(float brightness) {
+        // Set backlight within min and max backlight values
+        void setBacklight(float backlight) {
             if (mUseSurfaceControlBrightness || mForceSurfaceControl) {
-                mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, brightness);
+                mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, backlight);
             } else if (mBacklight != null) {
-                mBacklight.setBrightness(brightness);
+                mBacklight.setBrightness(backlight);
             }
         }
 
diff --git a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
index d514aab..62337c7 100644
--- a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
+++ b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
@@ -46,7 +46,7 @@
     private static final String ATTR_VALUE = "value";
 
     /* package */ static class Config {
-        public long lastModifiedDate;
+        public long lastModifiedMillis;
         public final Set<String> updatedFontDirs = new ArraySet<>();
         public final List<FontConfig.FontFamily> fontFamilies = new ArrayList<>();
     }
@@ -73,7 +73,7 @@
             } else if (depth == 2) {
                 switch (tag) {
                     case TAG_LAST_MODIFIED_DATE:
-                        out.lastModifiedDate = parseLongAttribute(parser, ATTR_VALUE, 0);
+                        out.lastModifiedMillis = parseLongAttribute(parser, ATTR_VALUE, 0);
                         break;
                     case TAG_UPDATED_FONT_DIR:
                         out.updatedFontDirs.add(getAttribute(parser, ATTR_VALUE));
@@ -101,7 +101,7 @@
 
         out.startTag(null, TAG_ROOT);
         out.startTag(null, TAG_LAST_MODIFIED_DATE);
-        out.attribute(null, ATTR_VALUE, Long.toString(config.lastModifiedDate));
+        out.attribute(null, ATTR_VALUE, Long.toString(config.lastModifiedMillis));
         out.endTag(null, TAG_LAST_MODIFIED_DATE);
         for (String dir : config.updatedFontDirs) {
             out.startTag(null, TAG_UPDATED_FONT_DIR);
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 08ddc6d..86dbe86 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -112,7 +112,7 @@
     private final File mConfigFile;
     private final File mTmpConfigFile;
 
-    private long mLastModifiedDate;
+    private long mLastModifiedMillis;
     private int mConfigVersion = 1;
 
     /**
@@ -147,7 +147,7 @@
     /* package */ void loadFontFileMap() {
         mFontFileInfoMap.clear();
         mFontFamilyMap.clear();
-        mLastModifiedDate = 0;
+        mLastModifiedMillis = 0;
         boolean success = false;
         try {
             PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
@@ -157,7 +157,7 @@
                 Slog.e(TAG, "Failed to load config xml file", e);
                 return;
             }
-            mLastModifiedDate = config.lastModifiedDate;
+            mLastModifiedMillis = config.lastModifiedMillis;
 
             File[] dirs = mFilesDir.listFiles();
             if (dirs == null) return;
@@ -200,7 +200,7 @@
             if (!success) {
                 mFontFileInfoMap.clear();
                 mFontFamilyMap.clear();
-                mLastModifiedDate = 0;
+                mLastModifiedMillis = 0;
                 FileUtils.deleteContents(mFilesDir);
             }
         }
@@ -211,7 +211,7 @@
         FileUtils.deleteContents(mFilesDir);
         mFontFamilyMap.clear();
 
-        mLastModifiedDate = Instant.now().getEpochSecond();
+        mLastModifiedMillis = System.currentTimeMillis();
         try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
             PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig());
         } catch (Exception e) {
@@ -231,7 +231,7 @@
         // Backup the mapping for rollback.
         ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap);
         ArrayMap<String, FontConfig.FontFamily> backupFamilies = new ArrayMap<>(mFontFamilyMap);
-        long backupLastModifiedDate = mLastModifiedDate;
+        long backupLastModifiedDate = mLastModifiedMillis;
         boolean success = false;
         try {
             for (FontUpdateRequest request : requests) {
@@ -247,7 +247,7 @@
             }
 
             // Write config file.
-            mLastModifiedDate = Instant.now().getEpochSecond();
+            mLastModifiedMillis = Instant.now().getEpochSecond();
             try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
                 PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig());
             } catch (Exception e) {
@@ -269,7 +269,7 @@
                 mFontFileInfoMap.putAll(backupMap);
                 mFontFamilyMap.clear();
                 mFontFamilyMap.putAll(backupFamilies);
-                mLastModifiedDate = backupLastModifiedDate;
+                mLastModifiedMillis = backupLastModifiedDate;
             }
         }
     }
@@ -527,7 +527,7 @@
 
     private PersistentSystemFontConfig.Config createPersistentConfig() {
         PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
-        config.lastModifiedDate = mLastModifiedDate;
+        config.lastModifiedMillis = mLastModifiedMillis;
         for (FontFileInfo info : mFontFileInfoMap.values()) {
             config.updatedFontDirs.add(info.getRandomizedFontDir().getName());
         }
@@ -560,7 +560,7 @@
         // which will be used as a fallback font without being overridden.
         mergedFamilies.addAll(mFontFamilyMap.values());
         return new FontConfig(
-                mergedFamilies, config.getAliases(), mLastModifiedDate, mConfigVersion);
+                mergedFamilies, config.getAliases(), mLastModifiedMillis, mConfigVersion);
     }
 
     /* package */ int getConfigVersion() {
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index a7f34ed..4c4c978 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -259,7 +259,7 @@
     }
 
     private void mayDisableSystemAudioAndARC(int address) {
-        if (HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, address)) {
+        if (!HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, address)) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
index d08980c..720be82 100644
--- a/services/core/java/com/android/server/input/InputShellCommand.java
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -29,6 +29,7 @@
 import android.view.ViewConfiguration;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -199,6 +200,8 @@
                 runRoll(inputSource, displayId);
             }  else if ("motionevent".equals(arg)) {
                 runMotionEvent(inputSource, displayId);
+            } else if ("keycombination".equals(arg)) {
+                runKeyCombination(inputSource, displayId);
             } else {
                 handleDefaultCommands(arg);
             }
@@ -224,7 +227,7 @@
             out.println();
             out.println("The commands and default sources are:");
             out.println("      text <string> (Default: touchscreen)");
-            out.println("      keyevent [--longpress] <key code number or name> ..."
+            out.println("      keyevent [--longpress|--doubletap] <key code number or name> ..."
                     + " (Default: keyboard)");
             out.println("      tap <x> <y> (Default: touchscreen)");
             out.println("      swipe <x1> <y1> <x2> <y2> [duration(ms)]"
@@ -234,6 +237,8 @@
             out.println("      press (Default: trackball)");
             out.println("      roll <dx> <dy> (Default: trackball)");
             out.println("      motionevent <DOWN|UP|MOVE|CANCEL> <x> <y> (Default: touchscreen)");
+            out.println("      keycombination <key code 1> <key code 2> ..."
+                    + " (Default: keyboard)");
         }
     }
 
@@ -282,6 +287,14 @@
         final boolean longpress = "--longpress".equals(arg);
         if (longpress) {
             arg = getNextArgRequired();
+        } else {
+            final boolean doubleTap = "--doubletap".equals(arg);
+            if (doubleTap) {
+                arg = getNextArgRequired();
+                final int keycode = KeyEvent.keyCodeFromString(arg);
+                sendKeyDoubleTap(inputSource, keycode, displayId);
+                return;
+            }
         }
 
         do {
@@ -292,22 +305,32 @@
 
     private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) {
         final long now = SystemClock.uptimeMillis();
-        int repeatCount = 0;
 
-        KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount,
+        KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0 /* repeatCount */,
                 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
                 inputSource);
         event.setDisplayId(displayId);
 
         injectKeyEvent(event);
         if (longpress) {
-            repeatCount++;
-            injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount,
+            // Some long press behavior would check the event time, we set a new event time here.
+            final long nextEventTime = now + ViewConfiguration.getGlobalActionKeyTimeout();
+            injectKeyEvent(KeyEvent.changeTimeRepeat(event, nextEventTime, 1 /* repeatCount */,
                     KeyEvent.FLAG_LONG_PRESS));
         }
         injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
     }
 
+    private void sendKeyDoubleTap(int inputSource, int keyCode, int displayId) {
+        sendKeyEvent(inputSource, keyCode, false, displayId);
+        try {
+            Thread.sleep(ViewConfiguration.getDoubleTapMinTime());
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        sendKeyEvent(inputSource, keyCode, false, displayId);
+    }
+
     private void runTap(int inputSource, int displayId) {
         inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
         sendTap(inputSource, Float.parseFloat(getNextArgRequired()),
@@ -440,4 +463,59 @@
         final long now = SystemClock.uptimeMillis();
         injectMotionEvent(inputSource, action, now, now, x, y, pressure, displayId);
     }
+
+    private void runKeyCombination(int inputSource, int displayId) {
+        String arg = getNextArgRequired();
+        ArrayList<Integer> keyCodes = new ArrayList<>();
+
+        while (arg != null) {
+            final int keyCode = KeyEvent.keyCodeFromString(arg);
+            if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
+                throw new IllegalArgumentException("Unknown keycode: " + arg);
+            }
+            keyCodes.add(keyCode);
+            arg = getNextArg();
+        }
+
+        // At least 2 keys.
+        if (keyCodes.size() < 2) {
+            throw new IllegalArgumentException("keycombination requires at least 2 keycodes");
+        }
+
+        sendKeyCombination(inputSource, keyCodes, displayId);
+    }
+
+    private void injectKeyEventAsync(KeyEvent event) {
+        InputManager.getInstance().injectInputEvent(event,
+                InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+    }
+
+    private void sendKeyCombination(int inputSource, ArrayList<Integer> keyCodes, int displayId) {
+        final long now = SystemClock.uptimeMillis();
+        final int count = keyCodes.size();
+        final KeyEvent[] events = new KeyEvent[count];
+        for (int i = 0; i < count; i++) {
+            final KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCodes.get(i), 0,
+                    0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
+                    inputSource);
+            event.setDisplayId(displayId);
+            events[i] = event;
+        }
+
+        for (KeyEvent event: events) {
+            // Use async inject so interceptKeyBeforeQueueing or interceptKeyBeforeDispatching could
+            // handle keys.
+            injectKeyEventAsync(event);
+        }
+
+        try {
+            Thread.sleep(ViewConfiguration.getTapTimeout());
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        for (KeyEvent event: events) {
+            injectKeyEventAsync(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1e66589..87e63eb 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5414,37 +5414,36 @@
         @BinderThread
         @ShellCommandResult
         private int onCommandWithSystemIdentity(@Nullable String cmd) {
-            if ("get-last-switch-user-id".equals(cmd)) {
-                return mService.getLastSwitchUserId(this);
-            }
-
-            // For existing "adb shell ime <command>".
-            if ("ime".equals(cmd)) {
-                final String imeCommand = getNextArg();
-                if (imeCommand == null || "help".equals(imeCommand) || "-h".equals(imeCommand)) {
-                    onImeCommandHelp();
-                    return ShellCommandResult.SUCCESS;
+            switch (TextUtils.emptyIfNull(cmd)) {
+                case "get-last-switch-user-id":
+                    return mService.getLastSwitchUserId(this);
+                case "ime": {  // For "adb shell ime <command>".
+                    final String imeCommand = TextUtils.emptyIfNull(getNextArg());
+                    switch (imeCommand) {
+                        case "":
+                        case "-h":
+                        case "help":
+                            return onImeCommandHelp();
+                        case "list":
+                            return mService.handleShellCommandListInputMethods(this);
+                        case "enable":
+                            return mService.handleShellCommandEnableDisableInputMethod(this, true);
+                        case "disable":
+                            return mService.handleShellCommandEnableDisableInputMethod(this, false);
+                        case "set":
+                            return mService.handleShellCommandSetInputMethod(this);
+                        case "reset":
+                            return mService.handleShellCommandResetInputMethod(this);
+                        case "tracing":
+                            return mService.handleShellCommandTraceInputMethod(this);
+                        default:
+                            getOutPrintWriter().println("Unknown command: " + imeCommand);
+                            return ShellCommandResult.FAILURE;
+                    }
                 }
-                switch (imeCommand) {
-                    case "list":
-                        return mService.handleShellCommandListInputMethods(this);
-                    case "enable":
-                        return mService.handleShellCommandEnableDisableInputMethod(this, true);
-                    case "disable":
-                        return mService.handleShellCommandEnableDisableInputMethod(this, false);
-                    case "set":
-                        return mService.handleShellCommandSetInputMethod(this);
-                    case "reset":
-                        return mService.handleShellCommandResetInputMethod(this);
-                    case "tracing":
-                        return mService.handleShellCommandTraceInputMethod(this);
-                    default:
-                        getOutPrintWriter().println("Unknown command: " + imeCommand);
-                        return ShellCommandResult.FAILURE;
-                }
+                default:
+                    return handleDefaultCommands(cmd);
             }
-
-            return handleDefaultCommands(cmd);
         }
 
         @BinderThread
@@ -5461,7 +5460,9 @@
             }
         }
 
-        private void onImeCommandHelp() {
+        @BinderThread
+        @ShellCommandResult
+        private int onImeCommandHelp() {
             try (IndentingPrintWriter pw =
                          new IndentingPrintWriter(getOutPrintWriter(), "  ", 100)) {
                 pw.println("ime <command>:");
@@ -5516,6 +5517,7 @@
 
                 pw.decreaseIndent();
             }
+            return ShellCommandResult.SUCCESS;
         }
     }
 
diff --git a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
index 7b400b6..6ea4bd2 100644
--- a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
@@ -17,7 +17,6 @@
 package com.android.server.location;
 
 import android.annotation.Nullable;
-import android.content.ComponentName;
 import android.content.Context;
 import android.hardware.location.ActivityRecognitionHardware;
 import android.hardware.location.IActivityRecognitionHardwareClient;
@@ -27,6 +26,7 @@
 import android.util.Log;
 
 import com.android.server.ServiceWatcher;
+import com.android.server.ServiceWatcher.BoundService;
 
 /**
  * Proxy class to bind GmsCore to the ActivityRecognitionHardware.
@@ -82,7 +82,7 @@
         return resolves;
     }
 
-    private void onBind(IBinder binder, ComponentName service) throws RemoteException {
+    private void onBind(IBinder binder, BoundService service) throws RemoteException {
         String descriptor = binder.getInterfaceDescriptor();
 
         if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 1bf9da7..57e9fc9 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -63,6 +63,7 @@
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationManagerInternal;
+import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener;
 import android.location.LocationProvider;
 import android.location.LocationRequest;
 import android.location.LocationTime;
@@ -82,6 +83,7 @@
 import android.provider.Settings;
 import android.stats.location.LocationStatsEnums;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 
@@ -252,9 +254,12 @@
 
     // @GuardedBy("mProviderManagers")
     // hold lock for writes, no lock necessary for simple reads
-    private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
+    final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
             new CopyOnWriteArrayList<>();
 
+    @GuardedBy("mLock")
+    private @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener;
+
     LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) {
         mContext = context.createAttributionContext(ATTRIBUTION_TAG);
         mInjector = injector;
@@ -284,7 +289,7 @@
     }
 
     @Nullable
-    private LocationProviderManager getLocationProviderManager(String providerName) {
+    LocationProviderManager getLocationProviderManager(String providerName) {
         if (providerName == null) {
             return null;
         }
@@ -319,8 +324,9 @@
             Preconditions.checkState(getLocationProviderManager(manager.getName()) == null);
 
             manager.startManager();
+            manager.setOnProviderLocationTagsChangeListener(
+                    mOnProviderLocationTagsChangeListener);
             if (realProvider != null) {
-
                 // custom logic wrapping all non-passive providers
                 if (manager != mPassiveManager) {
                     boolean enableStationaryThrottling = Settings.Global.getInt(
@@ -331,7 +337,6 @@
                                 mInjector, realProvider, mEventLog);
                     }
                 }
-
                 manager.setRealProvider(realProvider);
             }
             mProviderManagers.add(manager);
@@ -456,8 +461,9 @@
                     .setPowerUsage(Integer.parseInt(fragments[8]))
                     .setAccuracy(Integer.parseInt(fragments[9]))
                     .build();
-            getOrAddLocationProviderManager(name).setMockProvider(
-                    new MockLocationProvider(properties, CallerIdentity.fromContext(mContext)));
+            final LocationProviderManager manager = getOrAddLocationProviderManager(name);
+            manager.setMockProvider(new MockLocationProvider(properties,
+                    CallerIdentity.fromContext(mContext), /*locationTags*/ null));
         }
     }
 
@@ -1147,15 +1153,16 @@
 
     @Override
     public void addTestProvider(String provider, ProviderProperties properties,
-            String packageName, String attributionTag) {
+            List<String> locationTags, String packageName, String attributionTag) {
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag);
         if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) {
             return;
         }
 
-        getOrAddLocationProviderManager(provider).setMockProvider(
-                new MockLocationProvider(properties, identity));
+        final LocationProviderManager manager = getOrAddLocationProviderManager(provider);
+        manager.setMockProvider(new MockLocationProvider(properties, identity,
+                (locationTags != null) ? new ArraySet<>(locationTags) : null));
     }
 
     @Override
@@ -1288,13 +1295,13 @@
 
         ipw.println("Historical Aggregate Location Provider Data:");
         ipw.increaseIndent();
-        ArrayMap<String, ArrayMap<String, LocationEventLog.AggregateStats>> aggregateStats =
+        ArrayMap<String, ArrayMap<CallerIdentity, LocationEventLog.AggregateStats>> aggregateStats =
                 mEventLog.copyAggregateStats();
         for (int i = 0; i < aggregateStats.size(); i++) {
             ipw.print(aggregateStats.keyAt(i));
             ipw.println(":");
             ipw.increaseIndent();
-            ArrayMap<String, LocationEventLog.AggregateStats> providerStats =
+            ArrayMap<CallerIdentity, LocationEventLog.AggregateStats> providerStats =
                     aggregateStats.valueAt(i);
             for (int j = 0; j < providerStats.size(); j++) {
                 ipw.print(providerStats.keyAt(j));
@@ -1362,7 +1369,7 @@
                 if (provider != null && !provider.equals(manager.getName())) {
                     continue;
                 }
-                if (identity.equalsIgnoringListenerId(manager.getIdentity())) {
+                if (identity.equals(manager.getIdentity())) {
                     return true;
                 }
             }
@@ -1392,6 +1399,19 @@
 
             return new LocationTime(location.getTime(), location.getElapsedRealtimeNanos());
         }
+
+        @Override
+        public void setOnProviderLocationTagsChangeListener(
+                @Nullable OnProviderLocationTagsChangeListener listener) {
+            synchronized (mLock) {
+                mOnProviderLocationTagsChangeListener = listener;
+                final int providerCount = mProviderManagers.size();
+                for (int i = 0; i < providerCount; i++) {
+                    final LocationProviderManager manager = mProviderManagers.get(i);
+                    manager.setOnProviderLocationTagsChangeListener(listener);
+                }
+            }
+        }
     }
 
     private static class SystemInjector implements Injector {
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index f0dd8b5..21a9b04 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -75,8 +75,8 @@
             case "add-test-provider": {
                 String provider = getNextArgRequired();
                 ProviderProperties properties = parseTestProviderProviderProperties();
-                mService.addTestProvider(provider, properties, mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                mService.addTestProvider(provider, properties, /*locationTags*/ null,
+                        mContext.getOpPackageName(), mContext.getFeatureId());
                 return 0;
             }
             case "remove-test-provider": {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 81c1e45..dde45c4 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -295,7 +295,7 @@
                 };
             SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
             manager.addSensorPrivacyListener(
-                    SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE, listener);
+                    SensorPrivacyManager.Sensors.MICROPHONE, listener);
         }
     }
 
@@ -1079,8 +1079,8 @@
      */
     private void sendMicrophoneDisableSettingUpdate() {
         SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
-        boolean disabled = manager.isIndividualSensorPrivacyEnabled(
-                SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE);
+        boolean disabled = manager.isSensorPrivacyEnabled(
+                SensorPrivacyManager.Sensors.MICROPHONE);
         Log.d(TAG, "Mic Disabled Setting: " + disabled);
         mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled);
     }
diff --git a/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java b/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java
index af3907e..dda502b 100644
--- a/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java
+++ b/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java
@@ -320,13 +320,15 @@
                         "(source: " + detectedCountry.getSource()
                         + ", countryISO: " + detectedCountry.getCountryIso() + ")")
                     + " isAirplaneModeOff()=" + isAirplaneModeOff()
+                    + " isWifiOn()=" + isWifiOn()
                     + " mListener=" + mListener
                     + " isGeoCoderImplemnted()=" + isGeoCoderImplemented());
         }
 
         if (startLocationBasedDetection && (detectedCountry == null
                 || detectedCountry.getSource() > Country.COUNTRY_SOURCE_LOCATION)
-                && isAirplaneModeOff() && mListener != null && isGeoCoderImplemented()) {
+                && (isAirplaneModeOff() || isWifiOn()) && mListener != null
+                && isGeoCoderImplemented()) {
             if (DEBUG) Slog.d(TAG, "run startLocationBasedDetector()");
             // Start finding location when the source is less reliable than the
             // location and the airplane mode is off (as geocoder will not
@@ -387,6 +389,11 @@
                 mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 0;
     }
 
+    protected boolean isWifiOn() {
+        return Settings.Global.getInt(
+                mContext.getContentResolver(), Settings.Global.WIFI_ON, 0) != 0;
+    }
+
     /**
      * Notify the country change.
      */
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index dbfd0a5..29ce378 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -64,16 +64,17 @@
     private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 10;
 
     @GuardedBy("mAggregateStats")
-    private final ArrayMap<String, ArrayMap<String, AggregateStats>> mAggregateStats;
+    private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
 
     public LocationEventLog() {
         super(getLogSize());
         mAggregateStats = new ArrayMap<>(4);
     }
 
-    public ArrayMap<String, ArrayMap<String, AggregateStats>> copyAggregateStats() {
+    /** Copies out all aggregated stats. */
+    public ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copyAggregateStats() {
         synchronized (mAggregateStats) {
-            ArrayMap<String, ArrayMap<String, AggregateStats>> copy = new ArrayMap<>(
+            ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copy = new ArrayMap<>(
                     mAggregateStats);
             for (int i = 0; i < copy.size(); i++) {
                 copy.setValueAt(i, new ArrayMap<>(copy.valueAt(i)));
@@ -82,17 +83,18 @@
         }
     }
 
-    private AggregateStats getAggregateStats(String provider, String packageName) {
+    private AggregateStats getAggregateStats(String provider, CallerIdentity identity) {
         synchronized (mAggregateStats) {
-            ArrayMap<String, AggregateStats> packageMap = mAggregateStats.get(provider);
+            ArrayMap<CallerIdentity, AggregateStats> packageMap = mAggregateStats.get(provider);
             if (packageMap == null) {
                 packageMap = new ArrayMap<>(2);
                 mAggregateStats.put(provider, packageMap);
             }
-            AggregateStats stats = packageMap.get(packageName);
+            CallerIdentity stripped = identity.stripListenerId();
+            AggregateStats stats = packageMap.get(stripped);
             if (stats == null) {
                 stats = new AggregateStats();
-                packageMap.put(packageName, stats);
+                packageMap.put(stripped, stats);
             }
             return stats;
         }
@@ -117,34 +119,33 @@
     public void logProviderClientRegistered(String provider, CallerIdentity identity,
             LocationRequest request) {
         addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request);
-        getAggregateStats(provider, identity.getPackageName())
-                .markRequestAdded(request.getIntervalMillis());
+        getAggregateStats(provider, identity).markRequestAdded(request.getIntervalMillis());
     }
 
     /** Logs a client unregistration for a location provider. */
     public void logProviderClientUnregistered(String provider, CallerIdentity identity) {
         addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity);
-        getAggregateStats(provider, identity.getPackageName()).markRequestRemoved();
+        getAggregateStats(provider, identity).markRequestRemoved();
     }
 
     /** Logs a client for a location provider entering the active state. */
     public void logProviderClientActive(String provider, CallerIdentity identity) {
-        getAggregateStats(provider, identity.getPackageName()).markRequestActive();
+        getAggregateStats(provider, identity).markRequestActive();
     }
 
     /** Logs a client for a location provider leaving the active state. */
     public void logProviderClientInactive(String provider, CallerIdentity identity) {
-        getAggregateStats(provider, identity.getPackageName()).markRequestInactive();
+        getAggregateStats(provider, identity).markRequestInactive();
     }
 
     /** Logs a client for a location provider entering the foreground state. */
     public void logProviderClientForeground(String provider, CallerIdentity identity) {
-        getAggregateStats(provider, identity.getPackageName()).markRequestForeground();
+        getAggregateStats(provider, identity).markRequestForeground();
     }
 
     /** Logs a client for a location provider leaving the foreground state. */
     public void logProviderClientBackground(String provider, CallerIdentity identity) {
-        getAggregateStats(provider, identity.getPackageName()).markRequestBackground();
+        getAggregateStats(provider, identity).markRequestBackground();
     }
 
     /** Logs a change to the provider request for a location provider. */
@@ -165,7 +166,7 @@
         if (Build.IS_DEBUGGABLE || D) {
             addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity);
         }
-        getAggregateStats(provider, identity.getPackageName()).markLocationDelivered();
+        getAggregateStats(provider, identity).markLocationDelivered();
     }
 
     /** Logs that a provider has entered or exited stationary throttling. */
@@ -218,7 +219,7 @@
 
         protected final String mProvider;
 
-        protected ProviderEvent(long timeDelta, String provider) {
+        ProviderEvent(long timeDelta, String provider) {
             super(timeDelta);
             mProvider = provider;
         }
@@ -234,7 +235,7 @@
         private final int mUserId;
         private final boolean mEnabled;
 
-        protected ProviderEnabledEvent(long timeDelta, String provider, int userId,
+        ProviderEnabledEvent(long timeDelta, String provider, int userId,
                 boolean enabled) {
             super(timeDelta, provider);
             mUserId = userId;
@@ -252,7 +253,7 @@
 
         private final boolean mMocked;
 
-        protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
+        ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
             super(timeDelta, provider);
             mMocked = mocked;
         }
@@ -273,7 +274,7 @@
         private final CallerIdentity mIdentity;
         @Nullable private final LocationRequest mLocationRequest;
 
-        private ProviderRegisterEvent(long timeDelta, String provider, boolean registered,
+        ProviderRegisterEvent(long timeDelta, String provider, boolean registered,
                 CallerIdentity identity, @Nullable LocationRequest locationRequest) {
             super(timeDelta, provider);
             mRegistered = registered;
@@ -296,7 +297,7 @@
 
         private final ProviderRequest mRequest;
 
-        private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
+        ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
             super(timeDelta, provider);
             mRequest = request;
         }
@@ -311,7 +312,7 @@
 
         private final int mNumLocations;
 
-        private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
+        ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
             super(timeDelta, provider);
             mNumLocations = numLocations;
         }
@@ -327,7 +328,7 @@
         private final int mNumLocations;
         @Nullable private final CallerIdentity mIdentity;
 
-        private ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations,
+        ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations,
                 @Nullable CallerIdentity identity) {
             super(timeDelta, provider);
             mNumLocations = numLocations;
@@ -345,7 +346,7 @@
 
         private final boolean mStationaryThrottled;
 
-        private ProviderStationaryThrottledEvent(long timeDelta, String provider,
+        ProviderStationaryThrottledEvent(long timeDelta, String provider,
                 boolean stationaryThrottled) {
             super(timeDelta, provider);
             mStationaryThrottled = stationaryThrottled;
@@ -363,7 +364,7 @@
         @LocationPowerSaveMode
         private final int mLocationPowerSaveMode;
 
-        private LocationPowerSaveModeEvent(long timeDelta,
+        LocationPowerSaveModeEvent(long timeDelta,
                 @LocationPowerSaveMode int locationPowerSaveMode) {
             super(timeDelta);
             mLocationPowerSaveMode = locationPowerSaveMode;
@@ -401,7 +402,7 @@
         private final int mUserId;
         private final boolean mEnabled;
 
-        private LocationEnabledEvent(long timeDelta, int userId, boolean enabled) {
+        LocationEnabledEvent(long timeDelta, int userId, boolean enabled) {
             super(timeDelta);
             mUserId = userId;
             mEnabled = enabled;
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 9216a6b..29da177 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -371,7 +371,8 @@
 
     public GnssLocationProvider(Context context, Injector injector, GnssNative gnssNative,
             GnssMetrics gnssMetrics) {
-        super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES);
+        super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES,
+                /*locationTags*/ null);
 
         mContext = context;
         mGnssNative = gnssNative;
diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
index 4e0a0b8..9ff6e6b 100644
--- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
@@ -29,6 +29,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.UnaryOperator;
@@ -67,7 +68,7 @@
          * Default state value for a location provider that is disabled with no properties and an
          * empty provider package list.
          */
-        public static final State EMPTY_STATE = new State(false, null, null);
+        public static final State EMPTY_STATE = new State(false, null, null, null);
 
         /**
          * The provider's allowed state.
@@ -84,10 +85,14 @@
          */
         @Nullable public final CallerIdentity identity;
 
-        private State(boolean allowed, ProviderProperties properties, CallerIdentity identity) {
+        @Nullable public final Set<String> locationTags;
+
+        private State(boolean allowed, ProviderProperties properties, CallerIdentity identity,
+                Set<String> locationTags) {
             this.allowed = allowed;
             this.properties = properties;
             this.identity = identity;
+            this.locationTags = locationTags;
         }
 
         /**
@@ -97,7 +102,7 @@
             if (allowed == this.allowed) {
                 return this;
             } else {
-                return new State(allowed, properties, identity);
+                return new State(allowed, properties, identity, locationTags);
             }
         }
 
@@ -108,7 +113,7 @@
             if (Objects.equals(properties, this.properties)) {
                 return this;
             } else {
-                return new State(allowed, properties, identity);
+                return new State(allowed, properties, identity, locationTags);
             }
         }
 
@@ -119,10 +124,22 @@
             if (Objects.equals(identity, this.identity)) {
                 return this;
             } else {
-                return new State(allowed, properties, identity);
+                return new State(allowed, properties, identity, locationTags);
             }
         }
 
+        /**
+         * Returns a state the same as the current but with location tags set as specified.
+         */
+        public State withLocationTags(@Nullable Set<String> locationTags) {
+            if (Objects.equals(locationTags, this.locationTags)) {
+                return this;
+            } else {
+                return new State(allowed, properties, identity, locationTags);
+            }
+        }
+
+
         @Override
         public boolean equals(Object o) {
             if (this == o) {
@@ -133,12 +150,13 @@
             }
             State state = (State) o;
             return allowed == state.allowed && properties == state.properties
-                    && Objects.equals(identity, state.identity);
+                    && Objects.equals(identity, state.identity)
+                    && Objects.equals(locationTags, state.locationTags);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(allowed, properties, identity);
+            return Objects.hash(allowed, properties, identity, locationTags);
         }
     }
 
@@ -195,10 +213,14 @@
      * An optional identity and properties may be provided to initialize the location provider.
      */
     protected AbstractLocationProvider(Executor executor, @Nullable CallerIdentity identity,
-            @Nullable ProviderProperties properties) {
+            @Nullable ProviderProperties properties, @Nullable Set<String> locationTags) {
+        Preconditions.checkArgument(identity == null || identity.getListenerId() == null);
         mExecutor = executor;
         mInternalState = new AtomicReference<>(new InternalState(null,
-                State.EMPTY_STATE.withIdentity(identity).withProperties(properties)));
+                State.EMPTY_STATE
+                        .withIdentity(identity)
+                        .withProperties(properties).withLocationTags(locationTags))
+        );
         mController = new Controller();
     }
 
@@ -273,10 +295,18 @@
      * Call this method to report a change in provider packages.
      */
     protected void setIdentity(@Nullable CallerIdentity identity) {
+        Preconditions.checkArgument(identity == null || identity.getListenerId() == null);
         setState(state -> state.withIdentity(identity));
     }
 
     /**
+     * Call this method to report a change in provider location tags.
+     */
+    protected void setLocationTags(@Nullable Set<String> locationTags) {
+        setState(state -> state.withLocationTags(locationTags));
+    }
+
+    /**
      * Call this method to report a new location.
      */
     protected void reportLocation(LocationResult locationResult) {
@@ -335,6 +365,8 @@
 
         private boolean mStarted = false;
 
+        Controller() {}
+
         @Override
         public State setListener(@Nullable Listener listener) {
             InternalState oldInternalState = mInternalState.getAndUpdate(
diff --git a/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java
index a3ec867..49f6e64 100644
--- a/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java
@@ -40,7 +40,7 @@
     private boolean mInitialized = false;
 
     DelegateLocationProvider(Executor executor, AbstractLocationProvider delegate) {
-        super(executor, null, null);
+        super(executor, null, null, null);
 
         mDelegate = delegate;
     }
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 388b5a4..1ecf3ee 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -52,6 +52,8 @@
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationManagerInternal;
+import android.location.LocationManagerInternal.LocationTagInfo;
+import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener;
 import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.LocationRequest;
 import android.location.LocationResult;
@@ -85,6 +87,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.location.LocationPermissions;
@@ -117,6 +120,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Objects;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Predicate;
@@ -304,6 +308,7 @@
                 LocationTransport transport, @PermissionLevel int permissionLevel) {
             super(Objects.requireNonNull(request), identity, transport);
 
+            Preconditions.checkArgument(identity.getListenerId() != null);
             Preconditions.checkArgument(permissionLevel > PERMISSION_NONE);
             Preconditions.checkArgument(!request.getWorkSource().isEmpty());
 
@@ -1287,6 +1292,9 @@
     @GuardedBy("mLock")
     private @Nullable OnAlarmListener mDelayedRegister;
 
+    @GuardedBy("mLock")
+    private @Nullable OnProviderLocationTagsChangeListener mOnLocationTagsChangeListener;
+
     public LocationProviderManager(Context context, Injector injector, LocationEventLog eventLog,
             String name, @Nullable PassiveLocationProviderManager passiveManager) {
         mContext = context;
@@ -1447,6 +1455,19 @@
         }
     }
 
+    /**
+     * Registers a listener for the location tags of the provider.
+     *
+     * @param listener The listener
+     */
+    public void setOnProviderLocationTagsChangeListener(
+            @Nullable OnProviderLocationTagsChangeListener listener) {
+        Preconditions.checkArgument(mOnLocationTagsChangeListener == null || listener == null);
+        synchronized (mLock) {
+            mOnLocationTagsChangeListener = listener;
+        }
+    }
+
     public void setMockProvider(@Nullable MockLocationProvider provider) {
         synchronized (mLock) {
             Preconditions.checkState(mState != STATE_STOPPED);
@@ -2243,6 +2264,27 @@
         if (oldState.allowed != newState.allowed) {
             onEnabledChanged(UserHandle.USER_ALL);
         }
+
+        if (!Objects.equals(oldState.locationTags, newState.locationTags)) {
+            if (mOnLocationTagsChangeListener != null) {
+                if (oldState.identity != null) {
+                    FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
+                            OnProviderLocationTagsChangeListener::onLocationTagsChanged,
+                            mOnLocationTagsChangeListener, new LocationTagInfo(
+                                    oldState.identity.getUid(), oldState.identity.getPackageName(),
+                                    Collections.emptySet())
+                            ));
+                }
+                if (newState.identity != null) {
+                    FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
+                            OnProviderLocationTagsChangeListener::onLocationTagsChanged,
+                            mOnLocationTagsChangeListener, new LocationTagInfo(
+                                    newState.identity.getUid(), newState.identity.getPackageName(),
+                                    newState.locationTags)
+                            ));
+                }
+            }
+        }
     }
 
     @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
index 0d8f643..7660f56 100644
--- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
@@ -28,6 +28,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Set;
 
 /**
  * A mock location provider used by LocationManagerService to implement test providers.
@@ -38,9 +39,10 @@
 
     @Nullable private Location mLocation;
 
-    public MockLocationProvider(ProviderProperties properties, CallerIdentity identity) {
+    public MockLocationProvider(ProviderProperties properties, CallerIdentity identity,
+            @Nullable Set<String> locationTags) {
         // using a direct executor is ok because this class has no locks that could deadlock
-        super(DIRECT_EXECUTOR, identity, properties);
+        super(DIRECT_EXECUTOR, identity, properties, locationTags);
     }
 
     /** Sets the allowed state of this mock provider. */
diff --git a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
index cb7264e..4ffa9a5 100644
--- a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
@@ -75,7 +75,7 @@
     public MockableLocationProvider(Object ownerLock) {
         // using a direct executor is acceptable because all inbound calls are delegated to the
         // actual provider implementations which will use their own executors
-        super(DIRECT_EXECUTOR, null, null);
+        super(DIRECT_EXECUTOR, null, null, null);
         mOwnerLock = ownerLock;
         mRequest = ProviderRequest.EMPTY_REQUEST;
     }
diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
index a5758a3..ee9d35d 100644
--- a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
@@ -47,7 +47,8 @@
 
     public PassiveLocationProvider(Context context) {
         // using a direct executor is ok because this class has no locks that could deadlock
-        super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context), PROPERTIES);
+        super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context), PROPERTIES,
+                /*locationTags*/ null);
         setAllowed(true);
     }
 
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index 4c97f64..f00478a 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
@@ -32,10 +33,12 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.ArraySet;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.ServiceWatcher;
+import com.android.server.ServiceWatcher.BoundService;
 import com.android.server.location.provider.AbstractLocationProvider;
 
 import java.io.FileDescriptor;
@@ -49,6 +52,9 @@
  */
 public class ProxyLocationProvider extends AbstractLocationProvider {
 
+    private static final String KEY_LOCATION_TAGS = "android:location_allow_listed_tags";
+    private static final String LOCATION_TAGS_SEPARATOR = ";";
+
     /**
      * Creates and registers this proxy. If no suitable service is available for the proxy, returns
      * null.
@@ -84,7 +90,7 @@
             int nonOverlayPackageResId) {
         // safe to use direct executor since our locks are not acquired in a code path invoked by
         // our owning provider
-        super(DIRECT_EXECUTOR, null, null);
+        super(DIRECT_EXECUTOR, null, null, null);
 
         mContext = context;
         mServiceWatcher = new ServiceWatcher(context, action, this::onBind,
@@ -94,22 +100,34 @@
         mRequest = ProviderRequest.EMPTY_REQUEST;
     }
 
+    private void updateLocationTagInfo(@NonNull BoundService boundService) {
+        if (boundService.metadata != null) {
+            final String tagsList = boundService.metadata.getString(KEY_LOCATION_TAGS);
+            if (tagsList != null) {
+                final String[] tags = tagsList.split(LOCATION_TAGS_SEPARATOR);
+                setLocationTags(new ArraySet<>(tags));
+            }
+        }
+    }
+
     private boolean checkServiceResolves() {
         return mServiceWatcher.checkServiceResolves();
     }
 
-    private void onBind(IBinder binder, ComponentName service) throws RemoteException {
+    private void onBind(IBinder binder, BoundService boundService) throws RemoteException {
         ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
 
         synchronized (mLock) {
             mProxy = new Proxy();
-            mService = service;
+            mService = boundService.component;
             provider.setLocationProviderManager(mProxy);
 
             ProviderRequest request = mRequest;
             if (!request.equals(ProviderRequest.EMPTY_REQUEST)) {
                 provider.setRequest(request);
             }
+
+            updateLocationTagInfo(boundService);
         }
     }
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ecf4438..2b9dd2d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -882,9 +882,10 @@
 
             mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
             try {
+                // TODO: There shouldn't be a need to receive callback for all changes.
                 mActivityManager.registerUidObserver(mUidObserver,
                         ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE,
-                        NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android");
+                        ActivityManager.PROCESS_STATE_UNKNOWN, "android");
                 mNetworkManager.registerObserver(mAlertObserver);
             } catch (RemoteException e) {
                 // ignored; both services live in system_server
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 6aefe41..557fa89 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -54,6 +54,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastDataInput;
+import com.android.internal.util.FastDataOutput;
 import com.android.internal.util.FileRotator;
 import com.android.internal.util.IndentingPrintWriter;
 
@@ -89,6 +91,9 @@
     /** File header magic number: "ANET" */
     private static final int FILE_MAGIC = 0x414E4554;
 
+    /** Default buffer size from BufferedInputStream */
+    private static final int BUFFER_SIZE = 8192;
+
     private static final int VERSION_NETWORK_INIT = 1;
 
     private static final int VERSION_UID_INIT = 1;
@@ -434,7 +439,8 @@
 
     @Override
     public void read(InputStream in) throws IOException {
-        read((DataInput) new DataInputStream(in));
+        final FastDataInput dataIn = new FastDataInput(in, BUFFER_SIZE);
+        read(dataIn);
     }
 
     private void read(DataInput in) throws IOException {
@@ -473,8 +479,9 @@
 
     @Override
     public void write(OutputStream out) throws IOException {
-        write((DataOutput) new DataOutputStream(out));
-        out.flush();
+        final FastDataOutput dataOut = new FastDataOutput(out, BUFFER_SIZE);
+        write(dataOut);
+        dataOut.flush();
     }
 
     private void write(DataOutput out) throws IOException {
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 160d2da..afb47e8 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -32,7 +32,7 @@
     void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex,
             Notification.Action action, NotificationVisibility nv, boolean generatedByAssistant);
     void onNotificationClear(int callingUid, int callingPid,
-            String pkg, String tag, int id, int userId, String key,
+            String pkg, int userId, String key,
             @NotificationStats.DismissalSurface int dismissalSurface,
             @NotificationStats.DismissalSentiment int dismissalSentiment,
             NotificationVisibility nv);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8b4c639..f8cb2e4 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1044,15 +1044,19 @@
 
         @Override
         public void onNotificationClear(int callingUid, int callingPid,
-                String pkg, String tag, int id, int userId, String key,
+                String pkg, int userId, String key,
                 @NotificationStats.DismissalSurface int dismissalSurface,
                 @NotificationStats.DismissalSentiment int dismissalSentiment,
                 NotificationVisibility nv) {
+            String tag = null;
+            int id = 0;
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
                     r.recordDismissalSurface(dismissalSurface);
                     r.recordDismissalSentiment(dismissalSentiment);
+                    tag = r.getSbn().getTag();
+                    id = r.getSbn().getId();
                 }
             }
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index 308e815..9a9b14c 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -171,7 +171,7 @@
         }
     }
 
-    private class DataLoaderServiceConnection implements ServiceConnection {
+    private class DataLoaderServiceConnection implements ServiceConnection, IBinder.DeathRecipient {
         final int mId;
         final IDataLoaderStatusListener mListener;
         IDataLoader mDataLoader;
@@ -194,6 +194,13 @@
                 mContext.unbindService(this);
                 return;
             }
+            try {
+                service.linkToDeath(this, /*flags=*/0);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to link to DataLoader's death: " + mId, e);
+                onBindingDied(className);
+                return;
+            }
             callListener(IDataLoaderStatusListener.DATA_LOADER_BOUND);
         }
 
@@ -218,6 +225,13 @@
             destroy();
         }
 
+        @Override
+        public void binderDied() {
+            Slog.i(TAG, "DataLoader " + mId + " died");
+            callListener(IDataLoaderStatusListener.DATA_LOADER_DESTROYED);
+            destroy();
+        }
+
         IDataLoader getDataLoader() {
             return mDataLoader;
         }
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index 380cdb1..6875b8a 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -56,6 +56,7 @@
 
     private boolean mTitlePrinted;
     private boolean mFullPreferred;
+    private boolean mCheckIn;
 
     private String mTargetPackageName;
 
@@ -118,4 +119,12 @@
     public void setFullPreferred(boolean fullPreferred) {
         mFullPreferred = fullPreferred;
     }
+
+    public boolean isCheckIn() {
+        return mCheckIn;
+    }
+
+    public void setCheckIn(boolean checkIn) {
+        mCheckIn = checkIn;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2812830..a5e28f1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -530,14 +530,6 @@
             throw new SecurityException("User restriction prevents installing");
         }
 
-        if (params.dataLoaderParams != null
-                && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2)
-                        != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("You need the "
-                    + "com.android.permission.USE_INSTALLER_V2 permission "
-                    + "to use a data loader");
-        }
-
         // INSTALL_REASON_ROLLBACK allows an app to be rolled back without requiring the ROLLBACK
         // capability; ensure if this is set as the install reason the app has one of the necessary
         // signature permissions to perform the rollback.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7bf3c5c..460b2f2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3547,14 +3547,12 @@
 
     @Override
     public DataLoaderParamsParcel getDataLoaderParams() {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
         return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null;
     }
 
     @Override
     public void addFile(int location, String name, long lengthBytes, byte[] metadata,
             byte[] signature) {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
         if (!isDataLoaderInstallation()) {
             throw new IllegalStateException(
                     "Cannot add files to non-data loader installation session.");
@@ -3587,7 +3585,6 @@
 
     @Override
     public void removeFile(int location, String name) {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
         if (!isDataLoaderInstallation()) {
             throw new IllegalStateException(
                     "Cannot add files to non-data loader installation session.");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 16966d4..8123951 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1961,9 +1961,10 @@
         boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid);
         boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId, int callingUid);
         boolean isInstantAppResolutionAllowed(Intent intent, List<ResolveInfo> resolvedActivities,
-                int userId, boolean skipPackageCheck);
+                int userId, boolean skipPackageCheck, int flags);
         boolean isInstantAppResolutionAllowedBody(Intent intent,
-                List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck);
+                List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck,
+                int flags);
         boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
                 String resolvedType, int flags);
         boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId);
@@ -2322,7 +2323,7 @@
                 result = filterIfNotSystemUser(mComponentResolver.queryActivities(
                         intent, resolvedType, flags, userId), userId);
                 addInstant = isInstantAppResolutionAllowed(intent, result, userId,
-                        false /*skipPackageCheck*/);
+                        false /*skipPackageCheck*/, flags);
                 // Check for cross profile results.
                 boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
                 xpResolveInfo = queryCrossProfileIntents(
@@ -2387,8 +2388,8 @@
                 if (result == null || result.size() == 0) {
                     // the caller wants to resolve for a particular package; however, there
                     // were no installed results, so, try to find an ephemeral result
-                    addInstant = isInstantAppResolutionAllowed(
-                                    intent, null /*result*/, userId, true /*skipPackageCheck*/);
+                    addInstant = isInstantAppResolutionAllowed(intent, null /*result*/, userId,
+                            true /*skipPackageCheck*/, flags);
                     if (result == null) {
                         result = new ArrayList<>();
                     }
@@ -2618,7 +2619,7 @@
             // We'll want to include browser possibilities in a few cases
             boolean includeBrowser = false;
 
-            if (!DomainVerificationUtils.isDomainVerificationIntent(intent)) {
+            if (!DomainVerificationUtils.isDomainVerificationIntent(intent, matchFlags)) {
                 result.addAll(undefinedList);
                 // Maybe add one for the other profile.
                 if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel
@@ -2802,7 +2803,7 @@
                 }
 
                 result.highestApprovalLevel = Math.max(mDomainVerificationManager
-                        .approvalLevelForDomain(ps, intent, riTargetUser.targetUserId),
+                        .approvalLevelForDomain(ps, intent, flags, riTargetUser.targetUserId),
                         result.highestApprovalLevel);
             }
             if (result != null && result.highestApprovalLevel
@@ -3049,7 +3050,7 @@
                     final String packageName = info.activityInfo.packageName;
                     final PackageSetting ps = mSettings.getPackageLPr(packageName);
                     if (ps.getInstantApp(userId)) {
-                        if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent,
+                        if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags,
                                 userId)) {
                             if (DEBUG_INSTANT) {
                                 Slog.v(TAG, "Instant app approved for intent; pkg: "
@@ -3928,7 +3929,7 @@
 
         public boolean isInstantAppResolutionAllowed(
                 Intent intent, List<ResolveInfo> resolvedActivities, int userId,
-                boolean skipPackageCheck) {
+                boolean skipPackageCheck, int flags) {
             if (mInstantAppResolverConnection == null) {
                 return false;
             }
@@ -3961,14 +3962,14 @@
             // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
             // Or if there's already an ephemeral app installed that handles the action
             return isInstantAppResolutionAllowedBody(intent, resolvedActivities, userId,
-                                                       skipPackageCheck);
+                                                       skipPackageCheck, flags);
         }
 
         // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
         // Or if there's already an ephemeral app installed that handles the action
         public boolean isInstantAppResolutionAllowedBody(
                 Intent intent, List<ResolveInfo> resolvedActivities, int userId,
-                boolean skipPackageCheck) {
+                boolean skipPackageCheck, int flags) {
             final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
             for (int n = 0; n < count; n++) {
                 final ResolveInfo info = resolvedActivities.get(n);
@@ -3977,7 +3978,7 @@
                 if (ps != null) {
                     // only check domain verification status if the app is not a browser
                     if (!info.handleAllWebDataURI) {
-                        if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent,
+                        if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags,
                                 userId)) {
                             if (DEBUG_INSTANT) {
                                 Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName
@@ -4403,6 +4404,7 @@
 
         public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
             final String packageName = dumpState.getTargetPackageName();
+            final boolean checkin = dumpState.isCheckIn();
 
             switch (type) {
                 case DumpState.DUMP_VERSION:
@@ -4415,6 +4417,56 @@
                     break;
                 }
 
+                case DumpState.DUMP_LIBS:
+                {
+                    boolean printedHeader = false;
+                    final int numSharedLibraries = mSharedLibraries.size();
+                    for (int index = 0; index < numSharedLibraries; index++) {
+                        final String libName = mSharedLibraries.keyAt(index);
+                        final WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
+                                mSharedLibraries.get(libName);
+                        if (versionedLib == null) {
+                            continue;
+                        }
+                        final int versionCount = versionedLib.size();
+                        for (int i = 0; i < versionCount; i++) {
+                            SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
+                            if (!checkin) {
+                                if (!printedHeader) {
+                                    if (dumpState.onTitlePrinted()) {
+                                        pw.println();
+                                    }
+                                    pw.println("Libraries:");
+                                    printedHeader = true;
+                                }
+                                pw.print("  ");
+                            } else {
+                                pw.print("lib,");
+                            }
+                            pw.print(libraryInfo.getName());
+                            if (libraryInfo.isStatic()) {
+                                pw.print(" version=" + libraryInfo.getLongVersion());
+                            }
+                            if (!checkin) {
+                                pw.print(" -> ");
+                            }
+                            if (libraryInfo.getPath() != null) {
+                                if (libraryInfo.isNative()) {
+                                    pw.print(" (so) ");
+                                } else {
+                                    pw.print(" (jar) ");
+                                }
+                                pw.print(libraryInfo.getPath());
+                            } else {
+                                pw.print(" (apk) ");
+                                pw.print(libraryInfo.getPackageName());
+                            }
+                            pw.println();
+                        }
+                    }
+                    break;
+                }
+
                 case DumpState.DUMP_PREFERRED_XML:
                 {
                     pw.flush();
@@ -4676,10 +4728,11 @@
             }
         }
         public boolean isInstantAppResolutionAllowedBody(Intent intent,
-                List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck) {
+                List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck,
+                int flags) {
             synchronized (mLock) {
                 return super.isInstantAppResolutionAllowedBody(intent, resolvedActivities, userId,
-                        skipPackageCheck);
+                        skipPackageCheck, flags);
             }
         }
         public int getPackageUidInternal(String packageName, int flags, int userId,
@@ -9167,6 +9220,11 @@
      */
     @Override
     public String[] getPackagesForUid(int uid) {
+        final int callingUid = Binder.getCallingUid();
+        final int userId = UserHandle.getUserId(uid);
+        enforceCrossUserOrProfilePermission(callingUid, userId,
+                /* requireFullPermission */ false,
+                /* checkShell */ false, "getPackagesForUid");
         return snapshotComputer().getPackagesForUid(uid);
     }
 
@@ -9470,20 +9528,20 @@
 
     private boolean isInstantAppResolutionAllowed(
             Intent intent, List<ResolveInfo> resolvedActivities, int userId,
-            boolean skipPackageCheck) {
+            boolean skipPackageCheck, int flags) {
         return liveComputer().isInstantAppResolutionAllowed(
             intent, resolvedActivities, userId,
-            skipPackageCheck);
+            skipPackageCheck, flags);
     }
 
     // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
     // Or if there's already an ephemeral app installed that handles the action
     private boolean isInstantAppResolutionAllowedBody(
             Intent intent, List<ResolveInfo> resolvedActivities, int userId,
-            boolean skipPackageCheck) {
+            boolean skipPackageCheck, int flags) {
         return liveComputer().isInstantAppResolutionAllowedBody(
             intent, resolvedActivities, userId,
-            skipPackageCheck);
+            skipPackageCheck, flags);
     }
 
     private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
@@ -9540,7 +9598,7 @@
                         final String packageName = ri.activityInfo.packageName;
                         final PackageSetting ps = mSettings.getPackageLPr(packageName);
                         if (ps != null && hasAnyDomainApproval(mDomainVerificationManager, ps,
-                                intent, userId)) {
+                                intent, flags, userId)) {
                             return ri;
                         }
                     }
@@ -9597,8 +9655,9 @@
      */
     private static boolean hasAnyDomainApproval(
             @NonNull DomainVerificationManagerInternal manager, @NonNull PackageSetting pkgSetting,
-            @NonNull Intent intent, @UserIdInt int userId) {
-        return manager.approvalLevelForDomain(pkgSetting, intent, userId)
+            @NonNull Intent intent, @PackageManager.ResolveInfoFlags int resolveInfoFlags,
+            @UserIdInt int userId) {
+        return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId)
                 > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
     }
 
@@ -23699,8 +23758,6 @@
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
 
         DumpState dumpState = new DumpState();
-        boolean checkin = false;
-
         ArraySet<String> permissionNames = null;
 
         int opti = 0;
@@ -23750,7 +23807,7 @@
                 pw.println("    <package.name>: info about given package");
                 return;
             } else if ("--checkin".equals(opt)) {
-                checkin = true;
+                dumpState.setCheckIn(true);
             } else if ("--all-components".equals(opt)) {
                 dumpState.setOptionEnabled(DumpState.OPTION_DUMP_ALL_COMPONENTS);
             } else if ("-f".equals(opt)) {
@@ -23904,6 +23961,7 @@
         }
 
         final String packageName = dumpState.getTargetPackageName();
+        final boolean checkin = dumpState.isCheckIn();
         if (checkin) {
             pw.println("vers,1");
         }
@@ -23992,11 +24050,7 @@
         }
 
         if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
-            // TODO: Move it to ComputerEngine once LongSparseArray<SharedLibraryInfo> is copied
-            //  in snapshot.
-            synchronized (mLock) {
-                dumpSharedLibrariesLPr(pw, dumpState, checkin);
-            }
+            dump(DumpState.DUMP_LIBS, fd, pw, dumpState);
         }
 
         if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
@@ -24337,53 +24391,6 @@
         }
     }
 
-    private void dumpSharedLibrariesLPr(PrintWriter pw, DumpState dumpState, boolean checkin) {
-        boolean printedHeader = false;
-        final int numSharedLibraries = mSharedLibraries.size();
-        for (int index = 0; index < numSharedLibraries; index++) {
-            final String libName = mSharedLibraries.keyAt(index);
-            WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName);
-            if (versionedLib == null) {
-                continue;
-            }
-            final int versionCount = versionedLib.size();
-            for (int i = 0; i < versionCount; i++) {
-                SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
-                if (!checkin) {
-                    if (!printedHeader) {
-                        if (dumpState.onTitlePrinted()) {
-                            pw.println();
-                        }
-                        pw.println("Libraries:");
-                        printedHeader = true;
-                    }
-                    pw.print("  ");
-                } else {
-                    pw.print("lib,");
-                }
-                pw.print(libraryInfo.getName());
-                if (libraryInfo.isStatic()) {
-                    pw.print(" version=" + libraryInfo.getLongVersion());
-                }
-                if (!checkin) {
-                    pw.print(" -> ");
-                }
-                if (libraryInfo.getPath() != null) {
-                    if (libraryInfo.isNative()) {
-                        pw.print(" (so) ");
-                    } else {
-                        pw.print(" (jar) ");
-                    }
-                    pw.print(libraryInfo.getPath());
-                } else {
-                    pw.print(" (apk) ");
-                    pw.print(libraryInfo.getPackageName());
-                }
-                pw.println();
-            }
-        }
-    }
-
     // ------- apps on sdcard specific code -------
     static final boolean DEBUG_SD_INSTALL = false;
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 24c27be..c75dd27 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -983,7 +983,11 @@
 
     @Override
     public UserInfo getProfileParent(@UserIdInt int userId) {
-        checkManageUsersPermission("get the profile parent");
+        if (!hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
+            throw new SecurityException(
+                    "You need MANAGE_USERS or INTERACT_ACROSS_USERS permission to get the "
+                            + "profile parent");
+        }
         synchronized (mUsersLock) {
             return getProfileParentLU(userId);
         }
@@ -1531,11 +1535,14 @@
 
     @Override
     public String getUserName() {
-        if (!hasManageUsersOrPermission(android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED)) {
-            throw new SecurityException("You need MANAGE_USERS or GET_ACCOUNTS_PRIVILEGED "
-                    + "permissions to: get user name");
+        final int callingUid = Binder.getCallingUid();
+        if (!hasManageOrCreateUsersPermission()
+                || hasPermissionGranted(
+                        android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, callingUid)) {
+            throw new SecurityException("You need MANAGE_USERS or CREATE_USERS or "
+                    + "GET_ACCOUNTS_PRIVILEGED permissions to: get user name");
         }
-        final int userId = UserHandle.getUserId(Binder.getCallingUid());
+        final int userId = UserHandle.getUserId(callingUid);
         synchronized (mUsersLock) {
             UserInfo userInfo = userWithName(getUserInfoLU(userId));
             return userInfo == null ? "" : userInfo.name;
@@ -3287,7 +3294,7 @@
      * as well as for {@link UserManager#USER_TYPE_FULL_RESTRICTED}.
      */
     @Override
-    public UserInfo createProfileForUserWithThrow(String name, @NonNull String userType,
+    public UserInfo createProfileForUserWithThrow(@Nullable String name, @NonNull String userType,
             @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages)
             throws ServiceSpecificException {
         checkManageOrCreateUsersPermission(flags);
@@ -3868,7 +3875,7 @@
      * @hide
      */
     @Override
-    public UserInfo createRestrictedProfileWithThrow(String name, int parentUserId) {
+    public UserInfo createRestrictedProfileWithThrow(@Nullable String name, int parentUserId) {
         checkManageOrCreateUsersPermission("setupRestrictedProfile");
         final UserInfo user = createProfileForUserWithThrow(
                 name, UserManager.USER_TYPE_FULL_RESTRICTED, 0, parentUserId, null);
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index b6ea901..9e22d82 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -310,7 +310,7 @@
      */
     @ApprovalLevel
     int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
-            @UserIdInt int userId);
+            @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId);
 
     /**
      * @return the domain verification set ID for the given package, or null if the ID is
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 8e5aead..8dcb45f 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -1346,9 +1346,9 @@
 
     @Override
     public int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
-            @UserIdInt int userId) {
+            @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
         String packageName = pkgSetting.name;
-        if (!DomainVerificationUtils.isDomainVerificationIntent(intent)) {
+        if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
             if (DEBUG_APPROVAL) {
                 debugApproval(packageName, intent, userId, false, "not valid intent");
             }
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
index 475d3a8..783aff6 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -40,10 +40,13 @@
         throw new NameNotFoundException("Package " + packageName + " unavailable");
     }
 
-    public static boolean isDomainVerificationIntent(Intent intent) {
-        return intent.isWebIntent()
-                && intent.hasCategory(Intent.CATEGORY_BROWSABLE)
-                && intent.hasCategory(Intent.CATEGORY_DEFAULT);
+    public static boolean isDomainVerificationIntent(Intent intent, int resolveInfoFlags) {
+        if (!intent.isWebIntent() || !intent.hasCategory(Intent.CATEGORY_BROWSABLE)) {
+            return false;
+        }
+
+        return ((resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0)
+                || intent.hasCategory(Intent.CATEGORY_DEFAULT);
     }
 
     static boolean isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg,
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
new file mode 100644
index 0000000..c965390
--- /dev/null
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal;
+import android.location.LocationManagerInternal;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.QuadFunction;
+import com.android.server.LocalServices;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class defines policy for special behaviors around app ops.
+ */
+public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegate {
+    @NonNull
+    private final Object mLock = new Object();
+
+    /**
+     * The locking policy around the location tags is a bit special. Since we want to
+     * avoid grabbing the lock on every op note we are taking the approach where the
+     * read and write are being done via a thread-safe data structure such that the
+     * lookup/insert are single thread-safe calls. When we update the cached state we
+     * use a lock to ensure the update's lookup and store calls are done atomically,
+     * so multiple writers would not interleave. The tradeoff is we make is that the
+     * concurrent data structure would use boxing/unboxing of integers but this is
+     * preferred to locking.
+     */
+    @GuardedBy("mLock - writes only - see above")
+    @NonNull
+    private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags =
+            new ConcurrentHashMap();
+
+    public AppOpsPolicy() {
+        final LocationManagerInternal locationManagerInternal = LocalServices.getService(
+                LocationManagerInternal.class);
+        locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> {
+            synchronized (mLock) {
+                final int uid = providerTagInfo.getUid();
+                // We make a copy of the per UID state to limit our mutation to one
+                // operation in the underlying concurrent data structure.
+                ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
+                if (uidTags != null) {
+                    uidTags = new ArrayMap<>(uidTags);
+                }
+
+                final String packageName = providerTagInfo.getPackageName();
+                ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
+                if (packageTags != null) {
+                    packageTags = new ArraySet<>(packageTags);
+                }
+
+                final Set<String> providerTags = providerTagInfo.getTags();
+                if (providerTags != null && !providerTags.isEmpty()) {
+                    if (packageTags != null) {
+                        packageTags.clear();
+                        packageTags.addAll(providerTags);
+                    } else {
+                        packageTags = new ArraySet<>(providerTags);
+                    }
+                    if (uidTags == null) {
+                        uidTags = new ArrayMap<>();
+                    }
+                    uidTags.put(packageName, packageTags);
+                    mLocationTags.put(uid, uidTags);
+                } else if (uidTags != null) {
+                    uidTags.remove(packageName);
+                    if (!uidTags.isEmpty()) {
+                        mLocationTags.put(uid, uidTags);
+                    } else {
+                        mLocationTags.remove(uid);
+                    }
+                }
+            }
+        });
+    }
+
+    @Override
+    public int checkOperation(int code, int uid, String packageName, boolean raw,
+            QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl) {
+        return superImpl.apply(code, uid, packageName, raw);
+    }
+
+    @Override
+    public int checkAudioOperation(int code, int usage, int uid, String packageName,
+            QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) {
+        return superImpl.apply(code, usage, uid, packageName);
+    }
+
+    @Override
+    public int noteOperation(int code, int uid, @Nullable String packageName,
+            @Nullable String featureId, boolean shouldCollectAsyncNotedOp, @Nullable String message,
+            boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer, String, String,
+                    Boolean, String, Boolean, Integer> superImpl) {
+        if (isHandledOp(code)) {
+            // Only a single lookup from the underlying concurrent data structure
+            final ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
+            if (uidTags != null) {
+                final ArraySet<String> packageTags = uidTags.get(packageName);
+                if (packageTags != null && packageTags.contains(featureId)) {
+                    return superImpl.apply(resolveLocationOp(code), uid, packageName, featureId,
+                            shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+                }
+            }
+        }
+        return superImpl.apply(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
+                message, shouldCollectMessage);
+    }
+
+    private static boolean isHandledOp(int code) {
+        switch (code) {
+            case AppOpsManager.OP_FINE_LOCATION:
+            case AppOpsManager.OP_COARSE_LOCATION:
+                return true;
+        }
+        return false;
+    }
+
+    private static int resolveLocationOp(int code) {
+        switch (code) {
+            case AppOpsManager.OP_FINE_LOCATION:
+                return AppOpsManager.OP_FINE_LOCATION_SOURCE;
+            case AppOpsManager.OP_COARSE_LOCATION:
+                return AppOpsManager.OP_COARSE_LOCATION_SOURCE;
+        }
+        return code;
+    }
+}
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 84ac124..7f55723 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -102,9 +102,11 @@
     }
 
     /**
-     * Check if the key event could be triggered by combine key rule before dispatching to a window.
+     * Check if the key event could be intercepted by combination key rule before it is dispatched
+     * to a window.
+     * Return true if any active rule could be triggered by the key event, otherwise false.
      */
-    void interceptKey(KeyEvent event, boolean interactive) {
+    boolean interceptKey(KeyEvent event, boolean interactive) {
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         final int keyCode = event.getKeyCode();
         final int count = mActiveRules.size();
@@ -117,9 +119,9 @@
                     // exceed time from first key down.
                     forAllRules(mActiveRules, (rule)-> rule.cancel());
                     mActiveRules.clear();
-                    return;
+                    return false;
                 } else if (count == 0) { // has some key down but no active rule exist.
-                    return;
+                    return false;
                 }
             }
 
@@ -127,7 +129,7 @@
                 mDownTimes.put(keyCode, eventTime);
             } else {
                 // ignore old key, maybe a repeat key.
-                return;
+                return false;
             }
 
             if (mDownTimes.size() == 1) {
@@ -141,7 +143,7 @@
             } else {
                 // Ignore if rule already triggered.
                 if (mTriggeredRule != null) {
-                    return;
+                    return true;
                 }
 
                 // check if second key can trigger rule, or remove the non-match rule.
@@ -156,6 +158,7 @@
                 mActiveRules.clear();
                 if (mTriggeredRule != null) {
                     mActiveRules.add(mTriggeredRule);
+                    return true;
                 }
             }
         } else {
@@ -168,6 +171,7 @@
                 }
             }
         }
+        return false;
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4ccd57d..1b192e4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -73,6 +73,8 @@
 import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
+import static com.android.server.policy.SingleKeyGestureDetector.KEY_LONGPRESS;
+import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -456,7 +458,6 @@
     volatile boolean mPowerKeyHandled;
     volatile boolean mBackKeyHandled;
     volatile boolean mBeganFromNonInteractive;
-    volatile int mPowerKeyPressCounter;
     volatile boolean mEndCallKeyHandled;
     volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
     volatile boolean mGoingToSleep;
@@ -497,7 +498,6 @@
     boolean mHasSoftInput = false;
     boolean mHapticTextHandleEnabled;
     boolean mUseTvRouting;
-    int mVeryLongPressTimeout;
     boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
     MetricsLogger mLogger;
     boolean mWakeOnDpadKeyPress;
@@ -520,8 +520,6 @@
     boolean mConsumeSearchKeyUp;
     boolean mPendingMetaAction;
     boolean mPendingCapsLockToggle;
-    int mMetaState;
-    int mInitialMetaState;
 
     // support for activating the lock screen while the screen is on
     private HashSet<Integer> mAllowLockscreenWhenOnDisplays = new HashSet<>();
@@ -597,14 +595,13 @@
     private final com.android.internal.policy.LogDecelerateInterpolator mLogDecelerateInterpolator
             = new LogDecelerateInterpolator(100, 0);
 
-    private final MutableBoolean mTmpBoolean = new MutableBoolean(false);
-
     private boolean mPerDisplayFocusEnabled = false;
     private volatile int mTopFocusedDisplayId = INVALID_DISPLAY;
 
     private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS;
 
     private KeyCombinationManager mKeyCombinationManager;
+    private SingleKeyGestureDetector mSingleKeyGestureDetector;
 
     private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
     private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
@@ -615,10 +612,7 @@
     private static final int MSG_DISPATCH_SHOW_GLOBAL_ACTIONS = 10;
     private static final int MSG_HIDE_BOOT_MESSAGE = 11;
     private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12;
-    private static final int MSG_POWER_DELAYED_PRESS = 13;
-    private static final int MSG_POWER_LONG_PRESS = 14;
     private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15;
-    private static final int MSG_BACK_LONG_PRESS = 16;
     private static final int MSG_ACCESSIBILITY_SHORTCUT = 17;
     private static final int MSG_BUGREPORT_TV = 18;
     private static final int MSG_ACCESSIBILITY_TV = 19;
@@ -626,8 +620,7 @@
     private static final int MSG_SYSTEM_KEY_PRESS = 21;
     private static final int MSG_HANDLE_ALL_APPS = 22;
     private static final int MSG_LAUNCH_ASSIST = 23;
-    private static final int MSG_POWER_VERY_LONG_PRESS = 25;
-    private static final int MSG_RINGER_TOGGLE_CHORD = 26;
+    private static final int MSG_RINGER_TOGGLE_CHORD = 24;
 
     private class PolicyHandler extends Handler {
         @Override
@@ -668,22 +661,9 @@
                 case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK:
                     launchVoiceAssistWithWakeLock();
                     break;
-                case MSG_POWER_DELAYED_PRESS:
-                    powerPress((Long) msg.obj, msg.arg1 != 0, msg.arg2);
-                    finishPowerKeyPress();
-                    break;
-                case MSG_POWER_LONG_PRESS:
-                    powerLongPress((Long) msg.obj /* eventTime */);
-                    break;
-                case MSG_POWER_VERY_LONG_PRESS:
-                    powerVeryLongPress();
-                    break;
                 case MSG_SHOW_PICTURE_IN_PICTURE_MENU:
                     showPictureInPictureMenuInternal();
                     break;
-                case MSG_BACK_LONG_PRESS:
-                    backLongPress();
-                    break;
                 case MSG_ACCESSIBILITY_SHORTCUT:
                     accessibilityShortcutActivated();
                     break;
@@ -794,13 +774,6 @@
         }
     };
 
-    private Runnable mPossibleVeryLongPressReboot = new Runnable() {
-        @Override
-        public void run() {
-            mActivityManagerInternal.prepareForPossibleShutdown();
-        }
-    };
-
     private void handleRingerChordGesture() {
         if (mRingerToggleChord == VOLUME_HUSH_OFF) {
             return;
@@ -840,28 +813,13 @@
         }
     }
 
-    private void interceptBackKeyDown() {
-        mLogger.count("key_back_down", 1);
-        // Reset back key state for long press
-        mBackKeyHandled = false;
-
-        if (hasLongPressOnBackBehavior()) {
-            Message msg = mHandler.obtainMessage(MSG_BACK_LONG_PRESS);
-            msg.setAsynchronous(true);
-            mHandler.sendMessageDelayed(msg,
-                    ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
-        }
-    }
 
     // returns true if the key was handled and should not be passed to the user
-    private boolean interceptBackKeyUp(KeyEvent event) {
-        mLogger.count("key_back_up", 1);
+    private boolean backKeyPress() {
+        mLogger.count("key_back_press", 1);
         // Cache handled state
         boolean handled = mBackKeyHandled;
 
-        // Reset back long press state
-        cancelPendingBackKeyAction();
-
         if (mHasFeatureWatch) {
             TelecomManager telecomManager = getTelecommService();
 
@@ -883,10 +841,9 @@
             }
         }
 
-        if (mAutofillManagerInternal != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+        if (mAutofillManagerInternal != null) {
             mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_BACK_KEY_TO_AUTOFILL));
         }
-
         return handled;
     }
 
@@ -896,11 +853,6 @@
             mPowerKeyWakeLock.acquire();
         }
 
-        // Cancel multi-press detection timeout.
-        if (mPowerKeyPressCounter != 0) {
-            mHandler.removeMessages(MSG_POWER_DELAYED_PRESS);
-        }
-
         mWindowManagerFuncs.onPowerKeyDown(interactive);
 
         // Stop ringing or end call if configured to do so when power is pressed.
@@ -922,71 +874,20 @@
 
         final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);
 
-        GestureLauncherService gestureService = LocalServices.getService(
-                GestureLauncherService.class);
-        boolean gesturedServiceIntercepted = false;
-        if (gestureService != null) {
-            gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,
-                    mTmpBoolean);
-            if (mTmpBoolean.value && mRequestedOrGoingToSleep) {
-                mCameraGestureTriggeredDuringGoingToSleep = true;
-            }
-        }
-
         // Inform the StatusBar; but do not allow it to consume the event.
         sendSystemKeyToStatusBarAsync(event.getKeyCode());
 
-        schedulePossibleVeryLongPressReboot();
-
         // If the power key has still not yet been handled, then detect short
         // press, long press, or multi press and decide what to do.
-        mPowerKeyHandled = hungUp || gesturedServiceIntercepted
+        mPowerKeyHandled = mPowerKeyHandled || hungUp
                 || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
         if (!mPowerKeyHandled) {
-            if (interactive) {
-                // When interactive, we're already awake.
-                // Wait for a long press or for the button to be released to decide what to do.
-                if (hasLongPressOnPowerBehavior()) {
-                    if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
-                        powerLongPress(event.getEventTime());
-                    } else {
-                        Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS,
-                                event.getEventTime());
-                        msg.setAsynchronous(true);
-                        mHandler.sendMessageDelayed(msg,
-                                ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
-
-                        if (hasVeryLongPressOnPowerBehavior()) {
-                            Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
-                            longMsg.setAsynchronous(true);
-                            mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
-                        }
-                    }
-                }
-            } else {
+            if (!interactive) {
                 wakeUpFromPowerKey(event.getDownTime());
-
                 if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
-                    if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
-                        powerLongPress(event.getEventTime());
-                    } else {
-                        Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS,
-                                event.getEventTime());
-                        msg.setAsynchronous(true);
-                        mHandler.sendMessageDelayed(msg,
-                                ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
-
-                        if (hasVeryLongPressOnPowerBehavior()) {
-                            Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
-                            longMsg.setAsynchronous(true);
-                            mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
-                        }
-                    }
-
                     mBeganFromNonInteractive = true;
                 } else {
                     final int maxCount = getMaxMultiPressPowerCount();
-
                     if (maxCount <= 1) {
                         mPowerKeyHandled = true;
                     } else {
@@ -994,68 +895,38 @@
                     }
                 }
             }
+        } else {
+            // handled by another power key policy.
+            if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
+                mSingleKeyGestureDetector.reset();
+            }
         }
     }
 
     private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
         final boolean handled = canceled || mPowerKeyHandled;
-        cancelPendingPowerKeyAction();
 
         if (!handled) {
             if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == 0) {
                 // Abort possibly stuck animations only when power key up without long press case.
                 mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
             }
-
-            // Figure out how to handle the key now that it has been released.
-            mPowerKeyPressCounter += 1;
-
-            final int maxCount = getMaxMultiPressPowerCount();
-            final long eventTime = event.getDownTime();
-            if (mPowerKeyPressCounter < maxCount) {
-                // This could be a multi-press.  Wait a little bit longer to confirm.
-                // Continue holding the wake lock.
-                Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS,
-                        interactive ? 1 : 0, mPowerKeyPressCounter, eventTime);
-                msg.setAsynchronous(true);
-                mHandler.sendMessageDelayed(msg, ViewConfiguration.getMultiPressTimeout());
-                return;
-            }
-
-            // No other actions.  Handle it immediately.
-            powerPress(eventTime, interactive, mPowerKeyPressCounter);
+        } else {
+            // handled by single key or another power key policy.
+            mSingleKeyGestureDetector.reset();
+            finishPowerKeyPress();
         }
 
-        // Done.  Reset our state.
-        finishPowerKeyPress();
     }
 
     private void finishPowerKeyPress() {
         mBeganFromNonInteractive = false;
-        mPowerKeyPressCounter = 0;
+        mPowerKeyHandled = false;
         if (mPowerKeyWakeLock.isHeld()) {
             mPowerKeyWakeLock.release();
         }
     }
 
-    private void cancelPendingPowerKeyAction() {
-        if (!mPowerKeyHandled) {
-            mPowerKeyHandled = true;
-            mHandler.removeMessages(MSG_POWER_LONG_PRESS);
-        }
-        if (hasVeryLongPressOnPowerBehavior()) {
-            mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS);
-        }
-        cancelPossibleVeryLongPressReboot();
-    }
-
-    private void cancelPendingBackKeyAction() {
-        if (!mBackKeyHandled) {
-            mBackKeyHandled = true;
-            mHandler.removeMessages(MSG_BACK_LONG_PRESS);
-        }
-    }
-
     private void powerPress(long eventTime, boolean interactive, int count) {
         if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
             Slog.i(TAG, "Suppressed redundant power key press while "
@@ -1206,6 +1077,7 @@
 
     private void powerLongPress(long eventTime) {
         final int behavior = getResolvedLongPressOnPowerBehavior();
+
         switch (behavior) {
             case LONG_PRESS_POWER_NOTHING:
                 break;
@@ -1844,8 +1716,6 @@
                 com.android.internal.R.integer.config_triplePressOnPowerBehavior);
         mShortPressOnSleepBehavior = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_shortPressOnSleepBehavior);
-        mVeryLongPressTimeout = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_veryLongPressTimeout);
         mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);
 
@@ -1939,6 +1809,7 @@
                     }
                 });
         initKeyCombinationRules();
+        initSingleKeyGestureRules();
     }
 
     private void initKeyCombinationRules() {
@@ -1951,7 +1822,7 @@
                     new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
                         @Override
                         void execute() {
-                            cancelPendingPowerKeyAction();
+                            mPowerKeyHandled = true;
                             interceptScreenshotChord();
                         }
                         @Override
@@ -1986,7 +1857,7 @@
                     }
                     @Override
                     void execute() {
-                        cancelPendingPowerKeyAction();
+                        mPowerKeyHandled = true;
                         interceptRingerToggleChord();
                     }
                     @Override
@@ -2000,7 +1871,7 @@
                     new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) {
                         @Override
                         void execute() {
-                            cancelPendingBackKeyAction();
+                            mBackKeyHandled = true;
                             interceptAccessibilityGestureTv();
                         }
 
@@ -2014,7 +1885,7 @@
                     new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) {
                         @Override
                         void execute() {
-                            cancelPendingBackKeyAction();
+                            mBackKeyHandled = true;
                             interceptBugreportGestureTv();
                         }
 
@@ -2027,6 +1898,84 @@
     }
 
     /**
+     * Rule for single power key gesture.
+     */
+    private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
+        PowerKeyRule(int gestures) {
+            super(KEYCODE_POWER, gestures);
+        }
+
+        @Override
+        int getMaxMultiPressCount() {
+            return getMaxMultiPressPowerCount();
+        }
+
+        @Override
+        void onPress(long downTime) {
+            powerPress(downTime, true, 1 /*count*/);
+            finishPowerKeyPress();
+        }
+
+        @Override
+        void onLongPress(long downTime) {
+            powerLongPress(downTime);
+        }
+
+        @Override
+        void onVeryLongPress(long downTime) {
+            mActivityManagerInternal.prepareForPossibleShutdown();
+            powerVeryLongPress();
+        }
+
+        @Override
+        void onMultiPress(long downTime, int count) {
+            powerPress(downTime, true, count);
+            finishPowerKeyPress();
+        }
+    }
+
+    /**
+     * Rule for single back key gesture.
+     */
+    private final class BackKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
+        BackKeyRule(int gestures) {
+            super(KEYCODE_BACK, gestures);
+        }
+
+        @Override
+        int getMaxMultiPressCount() {
+            return 1;
+        }
+
+        @Override
+        void onPress(long downTime) {
+            mBackKeyHandled |= backKeyPress();
+        }
+
+        @Override
+        void onLongPress(long downTime) {
+            backLongPress();
+        }
+    }
+
+    private void initSingleKeyGestureRules() {
+        mSingleKeyGestureDetector = new SingleKeyGestureDetector(mContext);
+
+        int powerKeyGestures = 0;
+        if (hasVeryLongPressOnPowerBehavior()) {
+            powerKeyGestures |= KEY_VERYLONGPRESS;
+        }
+        if (hasLongPressOnPowerBehavior()) {
+            powerKeyGestures |= KEY_LONGPRESS;
+        }
+        mSingleKeyGestureDetector.addRule(new PowerKeyRule(powerKeyGestures));
+
+        if (hasLongPressOnBackBehavior()) {
+            mSingleKeyGestureDetector.addRule(new BackKeyRule(KEY_LONGPRESS));
+        }
+    }
+
+    /**
      * Read values from config.xml that may be overridden depending on
      * the configuration of the device.
      * eg. Disable long press on home goes to recents on sw600dp.
@@ -2547,6 +2496,7 @@
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         final boolean canceled = event.isCanceled();
         final int displayId = event.getDisplayId();
+        final long key_consumed = -1;
 
         if (DEBUG_INPUT) {
             Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
@@ -2554,7 +2504,7 @@
         }
 
         if (mKeyCombinationManager.isKeyConsumed(event)) {
-            return -1;
+            return key_consumed;
         }
 
         if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
@@ -2575,205 +2525,250 @@
             mPendingCapsLockToggle = false;
         }
 
-        // First we always handle the home key here, so applications
-        // can never break it, although if keyguard is on, we do let
-        // it handle it, because that gives us the correct 5 second
-        // timeout.
-        if (keyCode == KEYCODE_HOME) {
-            DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId);
-            if (handler == null) {
-                handler = new DisplayHomeButtonHandler(displayId);
-                mDisplayHomeButtonHandlers.put(displayId, handler);
-            }
-            return handler.handleHomeButton(focusedToken, event);
-        } else if (keyCode == KeyEvent.KEYCODE_MENU) {
-            // Hijack modified menu keys for debugging features
-            final int chordBug = KeyEvent.META_SHIFT_ON;
+        switch(keyCode) {
+            case KeyEvent.KEYCODE_HOME:
+                // First we always handle the home key here, so applications
+                // can never break it, although if keyguard is on, we do let
+                // it handle it, because that gives us the correct 5 second
+                // timeout.
+                DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId);
+                if (handler == null) {
+                    handler = new DisplayHomeButtonHandler(displayId);
+                    mDisplayHomeButtonHandlers.put(displayId, handler);
+                }
+                return handler.handleHomeButton(focusedToken, event);
+            case KeyEvent.KEYCODE_MENU:
+                // Hijack modified menu keys for debugging features
+                final int chordBug = KeyEvent.META_SHIFT_ON;
 
-            if (down && repeatCount == 0) {
-                if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
-                    Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
-                    mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
-                            null, null, null, 0, null, null);
-                    return -1;
-                }
-            }
-        } else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
-            if (down) {
-                if (repeatCount == 0) {
-                    mSearchKeyShortcutPending = true;
-                    mConsumeSearchKeyUp = false;
-                }
-            } else {
-                mSearchKeyShortcutPending = false;
-                if (mConsumeSearchKeyUp) {
-                    mConsumeSearchKeyUp = false;
-                    return -1;
-                }
-            }
-            return 0;
-        } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
-            if (!keyguardOn) {
                 if (down && repeatCount == 0) {
-                    preloadRecentApps();
-                } else if (!down) {
-                    toggleRecentApps();
-                }
-            }
-            return -1;
-        } else if (keyCode == KeyEvent.KEYCODE_N && event.isMetaPressed()) {
-            if (down) {
-                IStatusBarService service = getStatusBarService();
-                if (service != null) {
-                    try {
-                        service.expandNotificationsPanel();
-                    } catch (RemoteException e) {
-                        // do nothing.
+                    if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
+                        Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
+                        mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
+                                null, null, null, 0, null, null);
+                        return key_consumed;
                     }
                 }
-            }
-        } else if (keyCode == KeyEvent.KEYCODE_S && event.isMetaPressed()
-                && event.isCtrlPressed()) {
-            if (down && repeatCount == 0) {
-                int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION
-                        : TAKE_SCREENSHOT_FULLSCREEN;
-                mScreenshotRunnable.setScreenshotType(type);
-                mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER);
-                mHandler.post(mScreenshotRunnable);
-                return -1;
-            }
-        } else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) {
-            if (down && repeatCount == 0 && !isKeyguardLocked()) {
-                toggleKeyboardShortcutsMenu(event.getDeviceId());
-            }
-        } else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
-            Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing");
-            return -1;
-        } else if (keyCode == KeyEvent.KEYCODE_VOICE_ASSIST) {
-            Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in interceptKeyBeforeQueueing");
-            return -1;
-        } else if (keyCode == KeyEvent.KEYCODE_SYSRQ) {
-            if (down && repeatCount == 0) {
-                mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
-                mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER);
-                mHandler.post(mScreenshotRunnable);
-            }
-            return -1;
-        } else if (keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP
-                || keyCode == KeyEvent.KEYCODE_BRIGHTNESS_DOWN) {
-            if (down) {
-                int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1;
+                break;
+            case  KeyEvent.KEYCODE_SEARCH:
+                if (down) {
+                    if (repeatCount == 0) {
+                        mSearchKeyShortcutPending = true;
+                        mConsumeSearchKeyUp = false;
+                    }
+                } else {
+                    mSearchKeyShortcutPending = false;
+                    if (mConsumeSearchKeyUp) {
+                        mConsumeSearchKeyUp = false;
+                        return key_consumed;
+                    }
+                }
+                return 0;
+            case KeyEvent.KEYCODE_APP_SWITCH:
+                if (!keyguardOn) {
+                    if (down && repeatCount == 0) {
+                        preloadRecentApps();
+                    } else if (!down) {
+                        toggleRecentApps();
+                    }
+                }
+                return key_consumed;
+            case KeyEvent.KEYCODE_N:
+                if (down && event.isMetaPressed()) {
+                    IStatusBarService service = getStatusBarService();
+                    if (service != null) {
+                        try {
+                            service.expandNotificationsPanel();
+                        } catch (RemoteException e) {
+                            // do nothing.
+                        }
+                        return key_consumed;
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_S:
+                if (down && event.isMetaPressed() && event.isCtrlPressed() && repeatCount == 0) {
+                    int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION
+                            : TAKE_SCREENSHOT_FULLSCREEN;
+                    mScreenshotRunnable.setScreenshotType(type);
+                    mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER);
+                    mHandler.post(mScreenshotRunnable);
+                    return key_consumed;
+                }
+                break;
+            case KeyEvent.KEYCODE_SLASH:
+                if (down && repeatCount == 0 && event.isMetaPressed() && !keyguardOn) {
+                    toggleKeyboardShortcutsMenu(event.getDeviceId());
+                    return key_consumed;
+                }
+                break;
+            case KeyEvent.KEYCODE_ASSIST:
+                Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing");
+                return key_consumed;
+            case KeyEvent.KEYCODE_VOICE_ASSIST:
+                Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in"
+                        + " interceptKeyBeforeQueueing");
+                return key_consumed;
+            case KeyEvent.KEYCODE_SYSRQ:
+                if (down && repeatCount == 0) {
+                    mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
+                    mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER);
+                    mHandler.post(mScreenshotRunnable);
+                }
+                return key_consumed;
+            case KeyEvent.KEYCODE_BRIGHTNESS_UP:
+            case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
+                if (down) {
+                    int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1;
 
-                // Disable autobrightness if it's on
-                int auto = Settings.System.getIntForUser(
-                        mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS_MODE,
-                        Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
-                        UserHandle.USER_CURRENT_OR_SELF);
-                if (auto != 0) {
-                    Settings.System.putIntForUser(mContext.getContentResolver(),
+                    // Disable autobrightness if it's on
+                    int auto = Settings.System.getIntForUser(
+                            mContext.getContentResolver(),
                             Settings.System.SCREEN_BRIGHTNESS_MODE,
                             Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
                             UserHandle.USER_CURRENT_OR_SELF);
+                    if (auto != 0) {
+                        Settings.System.putIntForUser(mContext.getContentResolver(),
+                                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
+                                UserHandle.USER_CURRENT_OR_SELF);
+                    }
+                    float minFloat = mPowerManager.getBrightnessConstraint(
+                            PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
+                    float maxFloat = mPowerManager.getBrightnessConstraint(
+                            PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
+                    float stepFloat = (maxFloat - minFloat) / BRIGHTNESS_STEPS * direction;
+                    float brightnessFloat = Settings.System.getFloatForUser(
+                            mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FLOAT,
+                            mContext.getDisplay().getBrightnessDefault(),
+                            UserHandle.USER_CURRENT_OR_SELF);
+                    brightnessFloat += stepFloat;
+                    // Make sure we don't go beyond the limits.
+                    brightnessFloat = Math.min(maxFloat, brightnessFloat);
+                    brightnessFloat = Math.max(minFloat, brightnessFloat);
+
+                    Settings.System.putFloatForUser(mContext.getContentResolver(),
+                            Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessFloat,
+                            UserHandle.USER_CURRENT_OR_SELF);
+                    startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG),
+                            UserHandle.CURRENT_OR_SELF);
                 }
-                float minFloat = mPowerManager.getBrightnessConstraint(
-                        PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
-                float maxFloat = mPowerManager.getBrightnessConstraint(
-                        PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
-                float stepFloat = (maxFloat - minFloat) / BRIGHTNESS_STEPS * direction;
-                float brightnessFloat = Settings.System.getFloatForUser(
-                        mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FLOAT,
-                        mContext.getDisplay().getBrightnessDefault(),
-                        UserHandle.USER_CURRENT_OR_SELF);
-                brightnessFloat += stepFloat;
-                // Make sure we don't go beyond the limits.
-                brightnessFloat = Math.min(maxFloat, brightnessFloat);
-                brightnessFloat = Math.max(minFloat, brightnessFloat);
-
-                Settings.System.putFloatForUser(mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessFloat,
-                        UserHandle.USER_CURRENT_OR_SELF);
-                startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG),
-                        UserHandle.CURRENT_OR_SELF);
-            }
-            return -1;
-        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP
-                || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
-                || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
-            if (mUseTvRouting || mHandleVolumeKeysInWM) {
-                // On TVs or when the configuration is enabled, volume keys never
-                // go to the foreground app.
-                dispatchDirectAudioEvent(event);
-                return -1;
-            }
-
-            // If the device is in VR mode and keys are "internal" (e.g. on the side of the
-            // device), then drop the volume keys and don't forward it to the application/dispatch
-            // the audio event.
-            if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) {
-                final InputDevice d = event.getDevice();
-                if (d != null && !d.isExternal()) {
-                    return -1;
+                return key_consumed;
+            case KeyEvent.KEYCODE_VOLUME_UP:
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_VOLUME_MUTE:
+                if (mUseTvRouting || mHandleVolumeKeysInWM) {
+                    // On TVs or when the configuration is enabled, volume keys never
+                    // go to the foreground app.
+                    dispatchDirectAudioEvent(event);
+                    return key_consumed;
                 }
-            }
-        } else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) {
-            // Pass through keyboard navigation keys.
-            return 0;
-        } else if (keyCode == KeyEvent.KEYCODE_ALL_APPS) {
-            if (!down) {
-                mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
-                Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
-                msg.setAsynchronous(true);
-                msg.sendToTarget();
-            }
-            return -1;
-        } else if (keyCode == KeyEvent.KEYCODE_NOTIFICATION) {
-            if (!down) {
-                toggleNotificationPanel();
-            }
-            return -1;
-        }
 
-        // Toggle Caps Lock on META-ALT.
-        boolean actionTriggered = false;
-        if (KeyEvent.isModifierKey(keyCode)) {
-            if (!mPendingCapsLockToggle) {
-                // Start tracking meta state for combo.
-                mInitialMetaState = mMetaState;
-                mPendingCapsLockToggle = true;
-            } else if (event.getAction() == KeyEvent.ACTION_UP) {
-                int altOnMask = mMetaState & KeyEvent.META_ALT_MASK;
-                int metaOnMask = mMetaState & KeyEvent.META_META_MASK;
-
-                // Check for Caps Lock toggle
-                if ((metaOnMask != 0) && (altOnMask != 0)) {
-                    // Check if nothing else is pressed
-                    if (mInitialMetaState == (mMetaState ^ (altOnMask | metaOnMask))) {
-                        // Handle Caps Lock Toggle
-                        mInputManagerInternal.toggleCapsLock(event.getDeviceId());
-                        actionTriggered = true;
+                // If the device is in VR mode and keys are "internal" (e.g. on the side of the
+                // device), then drop the volume keys and don't forward it to the
+                // application/dispatch the audio event.
+                if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) {
+                    final InputDevice d = event.getDevice();
+                    if (d != null && !d.isExternal()) {
+                        return key_consumed;
                     }
                 }
+                break;
+            case KeyEvent.KEYCODE_TAB:
+                if (event.isMetaPressed()) {
+                    // Pass through keyboard navigation keys.
+                    return 0;
+                }
+                // Display task switcher for ALT-TAB.
+                if (down && repeatCount == 0) {
+                    if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) {
+                        final int shiftlessModifiers =
+                                event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
+                        if (KeyEvent.metaStateHasModifiers(
+                                shiftlessModifiers, KeyEvent.META_ALT_ON)) {
+                            mRecentAppsHeldModifiers = shiftlessModifiers;
+                            showRecentApps(true);
+                            return key_consumed;
+                        }
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_ALL_APPS:
+                if (!down) {
+                    mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
+                    Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
+                    msg.setAsynchronous(true);
+                    msg.sendToTarget();
+                }
+                return key_consumed;
+            case KeyEvent.KEYCODE_NOTIFICATION:
+                if (!down) {
+                    toggleNotificationPanel();
+                }
+                return key_consumed;
 
-                // Always stop tracking when key goes up.
-                mPendingCapsLockToggle = false;
-            }
-        }
-        // Store current meta state to be able to evaluate it later.
-        mMetaState = metaState;
+            case KeyEvent.KEYCODE_SPACE:
+                // Handle keyboard layout switching.
+                if ((metaState & (KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK)) == 0) {
+                    return 0;
+                }
+                // Share the same behavior with KEYCODE_LANGUAGE_SWITCH.
+            case KeyEvent.KEYCODE_LANGUAGE_SWITCH:
+                if (down && repeatCount == 0) {
+                    int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
+                    mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
+                    return key_consumed;
+                }
+                break;
+            case KeyEvent.KEYCODE_META_LEFT:
+            case KeyEvent.KEYCODE_META_RIGHT:
+                if (down) {
+                    if (event.isAltPressed()) {
+                        mPendingCapsLockToggle = true;
+                        mPendingMetaAction = false;
+                    } else {
+                        mPendingCapsLockToggle = false;
+                        mPendingMetaAction = true;
+                    }
+                } else {
+                    // Toggle Caps Lock on META-ALT.
+                    if (mPendingCapsLockToggle) {
+                        mInputManagerInternal.toggleCapsLock(event.getDeviceId());
+                        mPendingCapsLockToggle = false;
+                    } else if (mPendingMetaAction) {
+                        launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
+                                event.getDeviceId(),
+                                event.getEventTime());
+                        mPendingMetaAction = false;
+                    }
+                }
+                return key_consumed;
+            case KeyEvent.KEYCODE_ALT_LEFT:
+            case KeyEvent.KEYCODE_ALT_RIGHT:
+                if (down) {
+                    if (event.isMetaPressed()) {
+                        mPendingCapsLockToggle = true;
+                        mPendingMetaAction = false;
+                    } else {
+                        mPendingCapsLockToggle = false;
+                    }
+                } else {
+                    // hide recent if triggered by ALT-TAB.
+                    if (mRecentAppsHeldModifiers != 0
+                            && (metaState & mRecentAppsHeldModifiers) == 0) {
+                        mRecentAppsHeldModifiers = 0;
+                        hideRecentApps(true, false);
+                        return key_consumed;
+                    }
 
-        if (actionTriggered) {
-            return -1;
-        }
-
-        if (KeyEvent.isMetaKey(keyCode)) {
-            if (down) {
-                mPendingMetaAction = true;
-            } else if (mPendingMetaAction) {
-                launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, event.getDeviceId(),
-                        event.getEventTime());
-            }
-            return -1;
+                    // Toggle Caps Lock on META-ALT.
+                    if (mPendingCapsLockToggle) {
+                        mInputManagerInternal.toggleCapsLock(event.getDeviceId());
+                        mPendingCapsLockToggle = false;
+                        return key_consumed;
+                    }
+                }
+                break;
         }
 
         // Shortcuts are invoked through Search+key, so intercept those here
@@ -2803,7 +2798,7 @@
                                 + "SEARCH+" + KeyEvent.keyCodeToString(keyCode));
                     }
                 }
-                return -1;
+                return key_consumed;
             }
         }
 
@@ -2825,7 +2820,7 @@
                                 + "the activity to which it is registered was not found: "
                                 + "META+" + KeyEvent.keyCodeToString(keyCode), ex);
                     }
-                    return -1;
+                    return key_consumed;
                 }
             }
         }
@@ -2844,39 +2839,13 @@
                             + "the activity to which it is registered was not found: "
                             + "keyCode=" + keyCode + ", category=" + category, ex);
                 }
-                return -1;
+                return key_consumed;
             }
         }
 
-        // Display task switcher for ALT-TAB.
-        if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) {
-            if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) {
-                final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
-                if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) {
-                    mRecentAppsHeldModifiers = shiftlessModifiers;
-                    showRecentApps(true);
-                    return -1;
-                }
-            }
-        } else if (!down && mRecentAppsHeldModifiers != 0
-                && (metaState & mRecentAppsHeldModifiers) == 0) {
-            mRecentAppsHeldModifiers = 0;
-            hideRecentApps(true, false);
-        }
-
-        // Handle keyboard language switching.
-        final boolean isCtrlOrMetaSpace = keyCode == KeyEvent.KEYCODE_SPACE
-                && (metaState & (KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK)) != 0;
-        if (down && repeatCount == 0
-                && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH || isCtrlOrMetaSpace)) {
-            int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
-            mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
-            return -1;
-        }
-
         if (isValidGlobalKey(keyCode)
                 && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
-            return -1;
+            return key_consumed;
         }
 
         if (down) {
@@ -2906,13 +2875,13 @@
                 } catch (RemoteException e) {
                     mShortcutKeyServices.delete(shortcutCode);
                 }
-                return -1;
+                return key_consumed;
             }
         }
 
         // Reserve all the META modifier combos for system behavior
         if ((metaState & KeyEvent.META_META_ON) != 0) {
-            return -1;
+            return key_consumed;
         }
 
         // Let the application handle the key.
@@ -3550,8 +3519,21 @@
             return result;
         }
 
+        // Alternate TV power to power key for Android TV device.
+        final HdmiControlManager hdmiControlManager = getHdmiControlManager();
+        if (keyCode == KeyEvent.KEYCODE_TV_POWER && mHasFeatureLeanback
+                && (hdmiControlManager == null || !hdmiControlManager.shouldHandleTvPowerKey())) {
+            event = KeyEvent.obtain(
+                    event.getDownTime(), event.getEventTime(),
+                    event.getAction(), KeyEvent.KEYCODE_POWER,
+                    event.getRepeatCount(), event.getMetaState(),
+                    event.getDeviceId(), event.getScanCode(),
+                    event.getFlags(), event.getSource(), event.getDisplayId(), null);
+            return interceptKeyBeforeQueueing(event, policyFlags);
+        }
+
         if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
-            mKeyCombinationManager.interceptKey(event, interactive);
+            handleKeyGesture(event, interactive);
         }
 
         // Enable haptics if down and virtual key without multiple repetitions. If this is a hard
@@ -3566,12 +3548,13 @@
         switch (keyCode) {
             case KeyEvent.KEYCODE_BACK: {
                 if (down) {
-                    interceptBackKeyDown();
+                    mBackKeyHandled = false;
                 } else {
-                    boolean handled = interceptBackKeyUp(event);
-
+                    if (!hasLongPressOnBackBehavior()) {
+                        mBackKeyHandled |= backKeyPress();
+                    }
                     // Don't pass back press to app if we've already handled it via long press
-                    if (handled) {
+                    if (mBackKeyHandled) {
                         result &= ~ACTION_PASS_TO_USER;
                     }
                 }
@@ -3683,33 +3666,17 @@
             case KeyEvent.KEYCODE_TV_POWER: {
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
-                HdmiControlManager hdmiControlManager = getHdmiControlManager();
-                if (hdmiControlManager != null && hdmiControlManager.shouldHandleTvPowerKey()) {
-                    if (down) {
-                        hdmiControlManager.toggleAndFollowTvPower();
-                    }
-                } else if (mHasFeatureLeanback) {
-                    KeyEvent fallbackEvent = KeyEvent.obtain(
-                            event.getDownTime(), event.getEventTime(),
-                            event.getAction(), KeyEvent.KEYCODE_POWER,
-                            event.getRepeatCount(), event.getMetaState(),
-                            event.getDeviceId(), event.getScanCode(),
-                            event.getFlags(), event.getSource(), event.getDisplayId(), null);
-                    if (down) {
-                        interceptPowerKeyDown(fallbackEvent, interactive);
-                    } else {
-                        interceptPowerKeyUp(fallbackEvent, interactive, canceled);
-                    }
+                if (down && hdmiControlManager != null) {
+                    hdmiControlManager.toggleAndFollowTvPower();
                 }
-                // Ignore this key for any device that is not connected to a TV via HDMI and
-                // not an Android TV device.
                 break;
             }
 
             case KeyEvent.KEYCODE_POWER: {
                 EventLogTags.writeInterceptPower(
                         KeyEvent.actionToString(event.getAction()),
-                        mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter);
+                        mPowerKeyHandled ? 1 : 0,
+                        mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
                 // Any activity on the power button stops the accessibility shortcut
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
@@ -3890,6 +3857,43 @@
         return result;
     }
 
+    private void handleKeyGesture(KeyEvent event, boolean interactive) {
+        if (mKeyCombinationManager.interceptKey(event, interactive)) {
+            // handled by combo keys manager.
+            mSingleKeyGestureDetector.reset();
+            return;
+        }
+
+        if (event.getKeyCode() == KEYCODE_POWER && event.getAction() == KeyEvent.ACTION_DOWN) {
+            mPowerKeyHandled = handleCameraGesture(event, interactive);
+            if (mPowerKeyHandled) {
+                // handled by camera gesture.
+                mSingleKeyGestureDetector.reset();
+                return;
+            }
+        }
+
+        mSingleKeyGestureDetector.interceptKey(event);
+    }
+
+    // The camera gesture will be detected by GestureLauncherService.
+    private boolean handleCameraGesture(KeyEvent event, boolean interactive) {
+        // camera gesture.
+        GestureLauncherService gestureService = LocalServices.getService(
+                GestureLauncherService.class);
+        if (gestureService == null) {
+            return false;
+        }
+
+        final MutableBoolean outLaunched = new MutableBoolean(false);
+        final boolean gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event,
+                interactive, outLaunched);
+        if (outLaunched.value && mRequestedOrGoingToSleep) {
+            mCameraGestureTriggeredDuringGoingToSleep = true;
+        }
+        return gesturedServiceIntercepted;
+    }
+
     /**
      * Handle statusbar expansion events.
      * @param event
@@ -4889,15 +4893,6 @@
         }
     }
 
-    private void schedulePossibleVeryLongPressReboot() {
-        mHandler.removeCallbacks(mPossibleVeryLongPressReboot);
-        mHandler.postDelayed(mPossibleVeryLongPressReboot, mVeryLongPressTimeout);
-    }
-
-    private void cancelPossibleVeryLongPressReboot() {
-        mHandler.removeCallbacks(mPossibleVeryLongPressReboot);
-    }
-
     // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
     private void updateScreenOffSleepToken(boolean acquire) {
         if (acquire) {
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
new file mode 100644
index 0000000..3dafb0ce
--- /dev/null
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+
+/**
+ * Detect single key gesture: press, long press, very long press and multi press.
+ *
+ * Call {@link #reset} if current {@link KeyEvent} has been handled by another policy
+ */
+
+public final class SingleKeyGestureDetector {
+    private static final String TAG = "SingleKeyGesture";
+    private static final boolean DEBUG = false;
+
+    private static final int MSG_KEY_LONG_PRESS = 0;
+    private static final int MSG_KEY_VERY_LONG_PRESS = 1;
+    private static final int MSG_KEY_DELAYED_PRESS = 2;
+
+    private final long mLongPressTimeout;
+    private final long mVeryLongPressTimeout;
+
+    private volatile int mKeyPressCounter;
+
+    private final ArrayList<SingleKeyRule> mRules = new ArrayList();
+    private SingleKeyRule mActiveRule = null;
+
+    // Key code of current key down event, reset when key up.
+    private int mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
+    private volatile boolean mHandledByLongPress = false;
+    private final Handler mHandler;
+    private static final long MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout();
+
+
+    /** Supported gesture flags */
+    public static final int KEY_LONGPRESS = 1 << 1;
+    public static final int KEY_VERYLONGPRESS = 1 << 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "KEY_" }, value = {
+            KEY_LONGPRESS,
+            KEY_VERYLONGPRESS,
+    })
+    public @interface KeyGestureFlag {}
+
+    /**
+     *  Rule definition for single keys gesture.
+     *  E.g : define power key.
+     *  <pre class="prettyprint">
+     *  SingleKeyRule rule =
+     *      new SingleKeyRule(KEYCODE_POWER, KEY_LONGPRESS|KEY_VERYLONGPRESS) {
+     *           int getMaxMultiPressCount() { // maximum multi press count. }
+     *           void onPress(long downTime) { // short press behavior. }
+     *           void onLongPress() { // long press behavior. }
+     *           void onVeryLongPress() { // very long press behavior. }
+     *           void onMultiPress(long downTime, int count) { // multi press behavior.  }
+     *       };
+     *  </pre>
+     */
+    abstract static class SingleKeyRule {
+        private final int mKeyCode;
+        private final int mSupportedGestures;
+
+        SingleKeyRule(int keyCode, @KeyGestureFlag int supportedGestures) {
+            mKeyCode = keyCode;
+            mSupportedGestures = supportedGestures;
+        }
+
+        /**
+         *  True if the rule could intercept the key.
+         */
+        private boolean shouldInterceptKey(int keyCode) {
+            return keyCode == mKeyCode;
+        }
+
+        /**
+         *  True if the rule support long press.
+         */
+        private boolean supportLongPress() {
+            return (mSupportedGestures & KEY_LONGPRESS) != 0;
+        }
+
+        /**
+         *  True if the rule support very long press.
+         */
+        private boolean supportVeryLongPress() {
+            return (mSupportedGestures & KEY_VERYLONGPRESS) != 0;
+        }
+
+        /**
+         *  Maximum count of multi presses.
+         *  Return 1 will trigger onPress immediately when {@link KeyEvent.ACTION_UP}.
+         *  Otherwise trigger onMultiPress immediately when reach max count when
+         *  {@link KeyEvent.ACTION_DOWN}.
+         */
+        int getMaxMultiPressCount() {
+            return 1;
+        }
+
+        /**
+         *  Called when short press has been detected.
+         */
+        abstract void onPress(long downTime);
+        /**
+         *  Callback when multi press (>= 2) has been detected.
+         */
+        void onMultiPress(long downTime, int count) {}
+        /**
+         *  Callback when long press has been detected.
+         */
+        void onLongPress(long downTime) {}
+        /**
+         *  Callback when very long press has been detected.
+         */
+        void onVeryLongPress(long downTime) {}
+
+        @Override
+        public String toString() {
+            return "KeyCode = " + KeyEvent.keyCodeToString(mKeyCode)
+                    + ", long press : " + supportLongPress()
+                    + ", very Long press : " + supportVeryLongPress()
+                    + ", max multi press count : " + getMaxMultiPressCount();
+        }
+    }
+
+    public SingleKeyGestureDetector(Context context) {
+        mLongPressTimeout = ViewConfiguration.get(context).getDeviceGlobalActionKeyTimeout();
+        mVeryLongPressTimeout = context.getResources().getInteger(
+                com.android.internal.R.integer.config_veryLongPressTimeout);
+        mHandler = new KeyHandler();
+    }
+
+    void addRule(SingleKeyRule rule) {
+        mRules.add(rule);
+    }
+
+    void interceptKey(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            interceptKeyDown(event);
+        } else {
+            interceptKeyUp(event);
+        }
+    }
+
+    private void interceptKeyDown(KeyEvent event) {
+        final int keyCode = event.getKeyCode();
+        // same key down.
+        if (mDownKeyCode == keyCode) {
+            if (mActiveRule != null && (event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0
+                    && !mHandledByLongPress) {
+                if (DEBUG) {
+                    Log.i(TAG, "Long press Key " + KeyEvent.keyCodeToString(keyCode));
+                }
+                mHandledByLongPress = true;
+                mHandler.removeMessages(MSG_KEY_LONG_PRESS);
+                mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
+                mActiveRule.onLongPress(event.getEventTime());
+            }
+            return;
+        }
+
+        // When a different key is pressed, stop processing gestures for the currently active key.
+        if (mDownKeyCode != KeyEvent.KEYCODE_UNKNOWN
+                || (mActiveRule != null && !mActiveRule.shouldInterceptKey(keyCode))) {
+            if (DEBUG) {
+                Log.i(TAG, "Press another key " + KeyEvent.keyCodeToString(keyCode));
+            }
+
+            reset();
+        }
+        mDownKeyCode = keyCode;
+
+        // Picks a new rule, return if no rule picked.
+        if (mActiveRule == null) {
+            final int count = mRules.size();
+            for (int index = 0; index < count; index++) {
+                final SingleKeyRule rule = mRules.get(index);
+                if (rule.shouldInterceptKey(keyCode)) {
+                    mActiveRule = rule;
+                    break;
+                }
+            }
+        }
+        if (mActiveRule == null) {
+            return;
+        }
+
+        final long eventTime = event.getEventTime();
+        if (mKeyPressCounter == 0) {
+            if (mActiveRule.supportLongPress()) {
+                final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0,
+                        eventTime);
+                msg.setAsynchronous(true);
+                mHandler.sendMessageDelayed(msg, mLongPressTimeout);
+            }
+
+            if (mActiveRule.supportVeryLongPress()) {
+                final Message msg = mHandler.obtainMessage(MSG_KEY_VERY_LONG_PRESS, keyCode, 0,
+                        eventTime);
+                msg.setAsynchronous(true);
+                mHandler.sendMessageDelayed(msg, mVeryLongPressTimeout);
+            }
+        } else {
+            mHandler.removeMessages(MSG_KEY_LONG_PRESS);
+            mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
+            mHandler.removeMessages(MSG_KEY_DELAYED_PRESS);
+
+            // Trigger multi press immediately when reach max count.( > 1)
+            if (mKeyPressCounter == mActiveRule.getMaxMultiPressCount() - 1) {
+                if (DEBUG) {
+                    Log.i(TAG, "Trigger multi press " + mActiveRule.toString() + " for it"
+                            + " reach the max count " + mKeyPressCounter);
+                }
+                mActiveRule.onMultiPress(eventTime, mKeyPressCounter + 1);
+                mKeyPressCounter = 0;
+            }
+        }
+    }
+
+    private boolean interceptKeyUp(KeyEvent event) {
+        mHandler.removeMessages(MSG_KEY_LONG_PRESS);
+        mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
+        mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
+        if (mActiveRule == null) {
+            return false;
+        }
+
+        if (mHandledByLongPress) {
+            mHandledByLongPress = false;
+            return true;
+        }
+
+        final long downTime = event.getDownTime();
+        if (event.getKeyCode() == mActiveRule.mKeyCode) {
+            // Directly trigger short press when max count is 1.
+            if (mActiveRule.getMaxMultiPressCount() == 1) {
+                mActiveRule.onPress(downTime);
+                return true;
+            }
+
+            // This could be a multi-press.  Wait a little bit longer to confirm.
+            mKeyPressCounter++;
+            Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
+                    mKeyPressCounter, downTime);
+            msg.setAsynchronous(true);
+            mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT);
+            return true;
+        }
+        reset();
+        return false;
+    }
+
+    int getKeyPressCounter(int keyCode) {
+        if (mActiveRule != null && mActiveRule.mKeyCode == keyCode) {
+            return mKeyPressCounter;
+        } else {
+            return 0;
+        }
+    }
+
+    void reset() {
+        if (mActiveRule != null) {
+            if (mDownKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
+                mHandler.removeMessages(MSG_KEY_LONG_PRESS);
+                mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
+            }
+
+            if (mKeyPressCounter > 0) {
+                mHandler.removeMessages(MSG_KEY_DELAYED_PRESS);
+                mKeyPressCounter = 0;
+            }
+            mActiveRule = null;
+        }
+
+        mHandledByLongPress = false;
+        mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
+    }
+
+    boolean isKeyIntercepted(int keyCode) {
+        if (mActiveRule != null && mActiveRule.shouldInterceptKey(keyCode)) {
+            return mHandledByLongPress;
+        }
+        return false;
+    }
+
+    private class KeyHandler extends Handler {
+        KeyHandler() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (mActiveRule == null) {
+                return;
+            }
+            final int keyCode = msg.arg1;
+            final long eventTime = (long) msg.obj;
+            switch(msg.what) {
+                case MSG_KEY_LONG_PRESS:
+                    if (DEBUG) {
+                        Log.i(TAG, "Detect long press " + KeyEvent.keyCodeToString(keyCode));
+                    }
+                    mHandledByLongPress = true;
+                    mActiveRule.onLongPress(eventTime);
+                    break;
+                case MSG_KEY_VERY_LONG_PRESS:
+                    if (DEBUG) {
+                        Log.i(TAG, "Detect very long press "
+                                + KeyEvent.keyCodeToString(keyCode));
+                    }
+                    mHandledByLongPress = true;
+                    mActiveRule.onVeryLongPress(eventTime);
+                    break;
+                case MSG_KEY_DELAYED_PRESS:
+                    if (DEBUG) {
+                        Log.i(TAG, "Detect press " + KeyEvent.keyCodeToString(keyCode)
+                                + ", count " + mKeyPressCounter);
+                    }
+                    if (mKeyPressCounter == 1) {
+                        mActiveRule.onPress(eventTime);
+                    } else {
+                        mActiveRule.onMultiPress(eventTime, mKeyPressCounter);
+                    }
+                    mKeyPressCounter = 0;
+                    break;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8c46445..bc11709 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1645,7 +1645,7 @@
     }
 
     // Called from native code.
-    private void userActivityFromNative(long eventTime, int event, int flags) {
+    private void userActivityFromNative(long eventTime, int event, int displayId, int flags) {
         userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID);
     }
 
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index c4f29ea..ef0079e 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -16,6 +16,8 @@
 
 package com.android.server.powerstats;
 
+import static java.lang.System.currentTimeMillis;
+
 import android.content.Context;
 import android.hardware.power.stats.Channel;
 import android.hardware.power.stats.EnergyConsumer;
@@ -26,11 +28,14 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.SystemClock;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
 import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils;
 import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
@@ -61,6 +66,8 @@
     protected static final int MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY = 1;
     protected static final int MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY = 2;
 
+    // TODO(b/181240441): Add a listener to update the Wall clock baseline when changed
+    private final long mStartWallTime;
     private final PowerStatsDataStorage mPowerStatsMeterStorage;
     private final PowerStatsDataStorage mPowerStatsModelStorage;
     private final PowerStatsDataStorage mPowerStatsResidencyStorage;
@@ -79,6 +86,8 @@
                 // Log power meter data.
                 EnergyMeasurement[] energyMeasurements =
                     mPowerStatsHALWrapper.readEnergyMeter(new int[0]);
+                EnergyMeasurementUtils.adjustTimeSinceBootToEpoch(energyMeasurements,
+                        mStartWallTime);
                 mPowerStatsMeterStorage.write(
                         EnergyMeasurementUtils.getProtoBytes(energyMeasurements));
                 if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements);
@@ -86,6 +95,8 @@
                 // Log power model data without attribution data.
                 EnergyConsumerResult[] ecrNoAttribution =
                     mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
+                EnergyConsumerResultUtils.adjustTimeSinceBootToEpoch(ecrNoAttribution,
+                        mStartWallTime);
                 mPowerStatsModelStorage.write(
                         EnergyConsumerResultUtils.getProtoBytes(ecrNoAttribution, false));
                 if (DEBUG) EnergyConsumerResultUtils.print(ecrNoAttribution);
@@ -97,6 +108,8 @@
                 // Log power model data with attribution data.
                 EnergyConsumerResult[] ecrAttribution =
                     mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
+                EnergyConsumerResultUtils.adjustTimeSinceBootToEpoch(ecrAttribution,
+                        mStartWallTime);
                 mPowerStatsModelStorage.write(
                         EnergyConsumerResultUtils.getProtoBytes(ecrAttribution, true));
                 if (DEBUG) EnergyConsumerResultUtils.print(ecrAttribution);
@@ -108,6 +121,8 @@
                 // Log state residency data.
                 StateResidencyResult[] stateResidencyResults =
                     mPowerStatsHALWrapper.getStateResidency(new int[0]);
+                StateResidencyResultUtils.adjustTimeSinceBootToEpoch(stateResidencyResults,
+                        mStartWallTime);
                 mPowerStatsResidencyStorage.write(
                         StateResidencyResultUtils.getProtoBytes(stateResidencyResults));
                 if (DEBUG) StateResidencyResultUtils.print(stateResidencyResults);
@@ -293,12 +308,19 @@
         return mDeleteResidencyDataOnBoot;
     }
 
+    @VisibleForTesting
+    public long getStartWallTime() {
+        return mStartWallTime;
+    }
+
     public PowerStatsLogger(Context context, File dataStoragePath,
             String meterFilename, String meterCacheFilename,
             String modelFilename, String modelCacheFilename,
             String residencyFilename, String residencyCacheFilename,
             IPowerStatsHALWrapper powerStatsHALWrapper) {
         super(Looper.getMainLooper());
+        mStartWallTime = currentTimeMillis() - SystemClock.elapsedRealtime();
+        if (DEBUG) Slog.d(TAG, "mStartWallTime: " + mStartWallTime);
         mPowerStatsHALWrapper = powerStatsHALWrapper;
         mDataStoragePath = dataStoragePath;
 
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
index 11b22a5..746a098 100644
--- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -109,6 +109,18 @@
     }
 
     static class StateResidencyResultUtils {
+        public static void adjustTimeSinceBootToEpoch(StateResidencyResult[] stateResidencyResult,
+                long startWallTime) {
+            for (int i = 0; i < stateResidencyResult.length; i++) {
+                final int stateLength = stateResidencyResult[i].stateResidencyData.length;
+                for (int j = 0; j < stateLength; j++) {
+                    final StateResidency stateResidencyData =
+                            stateResidencyResult[i].stateResidencyData[j];
+                    stateResidencyData.lastEntryTimestampMs += startWallTime;
+                }
+            }
+        }
+
         public static byte[] getProtoBytes(StateResidencyResult[] stateResidencyResult) {
             ProtoOutputStream pos = new ProtoOutputStream();
             packProtoMessage(stateResidencyResult, pos);
@@ -306,6 +318,13 @@
     }
 
     static class EnergyMeasurementUtils {
+        public static void adjustTimeSinceBootToEpoch(EnergyMeasurement[] energyMeasurement,
+                long startWallTime) {
+            for (int i = 0; i < energyMeasurement.length; i++) {
+                energyMeasurement[i].timestampMs += startWallTime;
+            }
+        }
+
         public static byte[] getProtoBytes(EnergyMeasurement[] energyMeasurement) {
             ProtoOutputStream pos = new ProtoOutputStream();
             packProtoMessage(energyMeasurement, pos);
@@ -518,6 +537,13 @@
     }
 
     static class EnergyConsumerResultUtils {
+        public static void adjustTimeSinceBootToEpoch(EnergyConsumerResult[] energyConsumerResult,
+                long startWallTime) {
+            for (int i = 0; i < energyConsumerResult.length; i++) {
+                energyConsumerResult[i].timestampMs += startWallTime;
+            }
+        }
+
         public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult,
                 boolean includeAttribution) {
             ProtoOutputStream pos = new ProtoOutputStream();
diff --git a/services/core/java/com/android/server/speech/Android.bp b/services/core/java/com/android/server/speech/Android.bp
index 379b075..5605349 100644
--- a/services/core/java/com/android/server/speech/Android.bp
+++ b/services/core/java/com/android/server/speech/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 filegroup {
     name: "services.speech-sources",
     srcs: ["java/**/*.java"],
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index a390df9..8ffbb0a 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1336,7 +1336,7 @@
     }
 
     @Override
-    public void onNotificationClear(String pkg, String tag, int id, int userId, String key,
+    public void onNotificationClear(String pkg, int userId, String key,
             @NotificationStats.DismissalSurface int dismissalSurface,
             @NotificationStats.DismissalSentiment int dismissalSentiment,
             NotificationVisibility nv) {
@@ -1345,7 +1345,7 @@
         final int callingPid = Binder.getCallingPid();
         final long identity = Binder.clearCallingIdentity();
         try {
-            mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId,
+            mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, userId,
                     key, dismissalSurface, dismissalSentiment, nv);
         } finally {
             Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/timedetector/DeviceConfig.java b/services/core/java/com/android/server/timedetector/DeviceConfig.java
deleted file mode 100644
index 7b9ad0f..0000000
--- a/services/core/java/com/android/server/timedetector/DeviceConfig.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.timedetector;
-
-import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringDef;
-
-import java.time.Duration;
-import java.util.concurrent.Executor;
-
-/**
- * A helper class for reading / monitoring the {@link
- * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace for server-configured flags.
- */
-public final class DeviceConfig {
-
-    /**
-     * An annotation used to indicate when a {@link
-     * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} key is required.
-     *
-     * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider
-     * also shares the {@link android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the
-     * prefix "geotz_" on all of its key strings.
-     */
-    @StringDef(prefix = "KEY_", value = {
-            KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED,
-            KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT,
-            KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
-            KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
-            KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
-    })
-    @interface DeviceConfigKey {}
-
-    /**
-     * The key to force location time zone detection on for a device. Only intended for use during
-     * release testing with droidfooders. The user can still disable the feature by turning off the
-     * master location switch, or disabling automatic time zone detection.
-     */
-    @DeviceConfigKey
-    public static final String KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED =
-            "force_location_time_zone_detection_enabled";
-
-    /**
-     * The key for the default value used to determine whether location time zone detection is
-     * enabled when the user hasn't explicitly set it yet.
-     */
-    @DeviceConfigKey
-    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT =
-            "location_time_zone_detection_enabled_default";
-
-    /**
-     * The key for the minimum delay after location time zone detection has been enabled before the
-     * location time zone manager can report it is uncertain about the time zone.
-     */
-    @DeviceConfigKey
-    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS =
-            "location_time_zone_detection_uncertainty_delay_millis";
-
-    /**
-     * The key for the timeout passed to a location time zone provider that tells it how long it has
-     * to provide an explicit first suggestion without being declared uncertain.
-     */
-    @DeviceConfigKey
-    public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS =
-            "ltpz_init_timeout_millis";
-
-    /**
-     * The key for the extra time added to {@link
-     * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone
-     * manager before the location time zone provider will actually be declared uncertain.
-     */
-    @DeviceConfigKey
-    public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
-            "ltpz_init_timeout_fuzz_millis";
-
-    /** Creates an instance. */
-    public DeviceConfig() {}
-
-    /** Adds a listener for the system_time namespace. */
-    public void addListener(
-            @NonNull Executor handlerExecutor, @NonNull Runnable listener) {
-        android.provider.DeviceConfig.addOnPropertiesChangedListener(
-                NAMESPACE_SYSTEM_TIME,
-                handlerExecutor,
-                properties -> listener.run());
-    }
-
-    /**
-     * Returns a boolean value from {@link android.provider.DeviceConfig} from the system_time
-     * namespace, or {@code defaultValue} if there is no explicit value set.
-     */
-    public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) {
-        return android.provider.DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue);
-    }
-
-    /**
-     * Returns a positive duration from {@link android.provider.DeviceConfig} from the system_time
-     * namespace, or {@code defaultValue} if there is no explicit value set.
-     */
-    @Nullable
-    public Duration getDurationFromMillis(
-            @DeviceConfigKey String key, @Nullable Duration defaultValue) {
-        long deviceConfigValue =
-                android.provider.DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1);
-        if (deviceConfigValue < 0) {
-            return defaultValue;
-        }
-        return Duration.ofMillis(deviceConfigValue);
-    }
-}
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
new file mode 100644
index 0000000..8819b37
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -0,0 +1,238 @@
+/*
+ * 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.timedetector;
+
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.content.Context;
+import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.timezonedetector.ConfigurationChangeListener;
+import com.android.server.timezonedetector.ServiceConfigAccessor;
+
+import java.time.Duration;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * A helper class for reading / monitoring the {@link DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace
+ * for server-configured flags.
+ */
+public final class ServerFlags {
+
+    private static final Optional<Boolean> OPTIONAL_TRUE = Optional.of(true);
+    private static final Optional<Boolean> OPTIONAL_FALSE = Optional.of(false);
+
+    /**
+     * An annotation used to indicate when a {@link DeviceConfig#NAMESPACE_SYSTEM_TIME} key is
+     * required.
+     *
+     * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider
+     * also shares the {@link DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the
+     * prefix "geotz_" on all of its key strings.
+     */
+    @StringDef(prefix = "KEY_", value = {
+            KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
+            KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+            KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+            KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+            KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+            KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
+            KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
+            KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
+    })
+    @interface DeviceConfigKey {}
+
+    /**
+     * Controls whether the location time zone manager service will started. Only observed if
+     * the device build is configured to support location-based time zone detection. See
+     * {@link ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupportedInConfig()} and {@link
+     * ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupported()}.
+     */
+    @DeviceConfigKey
+    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED =
+            "location_time_zone_detection_feature_supported";
+
+    /**
+     * The key for the server flag that can override the device config for whether the primary
+     * location time zone provider is enabled or disabled.
+     */
+    @DeviceConfigKey
+    public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
+            "primary_location_time_zone_provider_enabled_override";
+
+    /**
+     * The key for the server flag that can override the device config for whether the secondary
+     * location time zone provider is enabled or disabled.
+     */
+    @DeviceConfigKey
+    public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
+            "secondary_location_time_zone_provider_enabled_override";
+
+    /**
+     * The key for the minimum delay after location time zone detection has been enabled before the
+     * location time zone manager can report it is uncertain about the time zone.
+     */
+    @DeviceConfigKey
+    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS =
+            "location_time_zone_detection_uncertainty_delay_millis";
+
+    /**
+     * The key for the timeout passed to a location time zone provider that tells it how long it has
+     * to provide an explicit first suggestion without being declared uncertain.
+     */
+    @DeviceConfigKey
+    public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS =
+            "ltpz_init_timeout_millis";
+
+    /**
+     * The key for the extra time added to {@link
+     * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone
+     * manager before the location time zone provider will actually be declared uncertain.
+     */
+    @DeviceConfigKey
+    public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
+            "ltpz_init_timeout_fuzz_millis";
+
+    /**
+     * The key for the server flag that can override location time zone detection being enabled for
+     * a user. Only intended for use during release testing with droidfooders. The user can still
+     * disable the feature by turning off the master location switch, or by disabling automatic time
+     * zone detection.
+     */
+    @DeviceConfigKey
+    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE =
+            "location_time_zone_detection_setting_enabled_override";
+
+    /**
+     * The key for the default value used to determine whether location time zone detection is
+     * enabled when the user hasn't explicitly set it yet.
+     */
+    @DeviceConfigKey
+    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT =
+            "location_time_zone_detection_setting_enabled_default";
+
+    @GuardedBy("mListeners")
+    private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>();
+
+    private static final Object SLOCK = new Object();
+
+    @GuardedBy("SLOCK")
+    @Nullable
+    private static ServerFlags sInstance;
+
+    private ServerFlags(Context context) {
+        DeviceConfig.addOnPropertiesChangedListener(
+                NAMESPACE_SYSTEM_TIME,
+                context.getMainExecutor(),
+                this::handlePropertiesChanged);
+    }
+
+    /** Returns the singleton instance. */
+    public static ServerFlags getInstance(Context context) {
+        synchronized (SLOCK) {
+            if (sInstance == null) {
+                sInstance = new ServerFlags(context);
+            }
+            return sInstance;
+        }
+    }
+
+    private void handlePropertiesChanged(@NonNull DeviceConfig.Properties properties) {
+        synchronized (mListeners) {
+            for (Map.Entry<ConfigurationChangeListener, Set<String>> listenerEntry
+                    : mListeners.entrySet()) {
+                if (intersects(listenerEntry.getValue(), properties.getKeyset())) {
+                    listenerEntry.getKey().onChange();
+                }
+            }
+        }
+    }
+
+    private static boolean intersects(@NonNull Set<String> one, @NonNull Set<String> two) {
+        for (String toFind : one) {
+            if (two.contains(toFind)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Adds a listener for the system_time namespace that will trigger if any of the specified keys
+     * change. Listener callbacks are delivered on the main looper thread.
+     *
+     * <p>Note: Only for use by long-lived objects like other singletons. There is deliberately no
+     * associated remove method.
+     */
+    public void addListener(@NonNull ConfigurationChangeListener listener,
+            @NonNull Set<String> keys) {
+        Objects.requireNonNull(listener);
+        Objects.requireNonNull(keys);
+
+        synchronized (mListeners) {
+            mListeners.put(listener, keys);
+        }
+    }
+
+    /**
+     * Returns an optional boolean value from {@link DeviceConfig} from the system_time
+     * namespace, returns {@link Optional#empty()} if there is no explicit value set.
+     */
+    @NonNull
+    public Optional<Boolean> getOptionalBoolean(@DeviceConfigKey String key) {
+        String value = DeviceConfig.getProperty(NAMESPACE_SYSTEM_TIME, key);
+        return parseOptionalBoolean(value);
+    }
+
+    @NonNull
+    private static Optional<Boolean> parseOptionalBoolean(@Nullable String value) {
+        if (value == null) {
+            return Optional.empty();
+        } else {
+            return Boolean.parseBoolean(value) ? OPTIONAL_TRUE : OPTIONAL_FALSE;
+        }
+    }
+
+    /**
+     * Returns a boolean value from {@link DeviceConfig} from the system_time
+     * namespace, or {@code defaultValue} if there is no explicit value set.
+     */
+    public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) {
+        return DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue);
+    }
+
+    /**
+     * Returns a positive duration from {@link DeviceConfig} from the system_time
+     * namespace, or {@code defaultValue} if there is no explicit value set.
+     */
+    @Nullable
+    public Duration getDurationFromMillis(
+            @DeviceConfigKey String key, @Nullable Duration defaultValue) {
+        long deviceConfigValue = DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1);
+        if (deviceConfigValue < 0) {
+            return defaultValue;
+        }
+        return Duration.ofMillis(deviceConfigValue);
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java
index 4c7b1f3..aa8ad37 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java
@@ -17,10 +17,11 @@
 package com.android.server.timezonedetector;
 
 /**
- * A listener used to receive notification that time zone configuration has changed.
+ * A listener used to receive notification that configuration has / may have changed (depending on
+ * the usecase).
  */
 @FunctionalInterface
 public interface ConfigurationChangeListener {
-    /** Called when the current user or a configuration value has changed. */
+    /** Called when the configuration may have changed. */
     void onChange();
 }
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 1f73977..3ae9d64 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -33,26 +33,27 @@
 import java.util.Objects;
 
 /**
- * Holds all configuration values that affect time zone behavior and some associated logic, e.g.
- * {@link #getAutoDetectionEnabledBehavior()}, {@link #getGeoDetectionEnabledBehavior()} and {@link
- * #createCapabilitiesAndConfig()}.
+ * Holds configuration values that affect user-facing time zone behavior and some associated logic.
+ * Some configuration is global, some is user scoped, but this class deliberately doesn't make a
+ * distinction for simplicity.
  */
 public final class ConfigurationInternal {
 
-    private final @UserIdInt int mUserId;
-    private final boolean mUserConfigAllowed;
     private final boolean mAutoDetectionSupported;
     private final boolean mGeoDetectionSupported;
     private final boolean mAutoDetectionEnabled;
+    private final @UserIdInt int mUserId;
+    private final boolean mUserConfigAllowed;
     private final boolean mLocationEnabled;
     private final boolean mGeoDetectionEnabled;
 
     private ConfigurationInternal(Builder builder) {
-        mUserId = builder.mUserId;
-        mUserConfigAllowed = builder.mUserConfigAllowed;
         mAutoDetectionSupported = builder.mAutoDetectionSupported;
         mGeoDetectionSupported = builder.mGeoDetectionSupported;
         mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
+
+        mUserId = builder.mUserId;
+        mUserConfigAllowed = builder.mUserConfigAllowed;
         mLocationEnabled = builder.mLocationEnabled;
         mGeoDetectionEnabled = builder.mGeoDetectionEnabled;
         // if mGeoDetectionSupported then mAutoDetectionSupported, i.e. mGeoDetectionSupported
@@ -60,22 +61,6 @@
         Preconditions.checkState(mAutoDetectionSupported || !mGeoDetectionSupported);
     }
 
-    /** Returns the ID of the user this configuration is associated with. */
-    public @UserIdInt int getUserId() {
-        return mUserId;
-    }
-
-    /** Returns the handle of the user this configuration is associated with. */
-    @NonNull
-    public UserHandle getUserHandle() {
-        return UserHandle.of(mUserId);
-    }
-
-    /** Returns true if the user allowed to modify time zone configuration. */
-    public boolean isUserConfigAllowed() {
-        return mUserConfigAllowed;
-    }
-
     /** Returns true if the device supports any form of auto time zone detection. */
     public boolean isAutoDetectionSupported() {
         return mAutoDetectionSupported;
@@ -98,6 +83,22 @@
         return mAutoDetectionSupported && mAutoDetectionEnabled;
     }
 
+    /** Returns the ID of the user this configuration is associated with. */
+    public @UserIdInt int getUserId() {
+        return mUserId;
+    }
+
+    /** Returns the handle of the user this configuration is associated with. */
+    @NonNull
+    public UserHandle getUserHandle() {
+        return UserHandle.of(mUserId);
+    }
+
+    /** Returns true if the user allowed to modify time zone configuration. */
+    public boolean isUserConfigAllowed() {
+        return mUserConfigAllowed;
+    }
+
     /** Returns true if user's location can be used generally. */
     public boolean isLocationEnabled() {
         return mLocationEnabled;
@@ -283,7 +284,7 @@
         /**
          * Sets whether any form of automatic time zone detection is supported on this device.
          */
-        public Builder setAutoDetectionSupported(boolean supported) {
+        public Builder setAutoDetectionFeatureSupported(boolean supported) {
             mAutoDetectionSupported = supported;
             return this;
         }
@@ -291,7 +292,7 @@
         /**
          * Sets whether geolocation time zone detection is supported on this device.
          */
-        public Builder setGeoDetectionSupported(boolean supported) {
+        public Builder setGeoDetectionFeatureSupported(boolean supported) {
             mGeoDetectionSupported = supported;
             return this;
         }
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index f52b9b1..e3caae94 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -31,9 +31,7 @@
 import android.content.IntentFilter;
 import android.database.ContentObserver;
 import android.location.LocationManager;
-import android.net.ConnectivityManager;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -42,10 +40,9 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
-import com.android.server.timedetector.DeviceConfig;
 
 import java.util.Objects;
-import java.util.concurrent.Executor;
+import java.util.Optional;
 
 /**
  * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
@@ -59,8 +56,7 @@
     @NonNull private final Handler mHandler;
     @NonNull private final ContentResolver mCr;
     @NonNull private final UserManager mUserManager;
-    @NonNull private final DeviceConfig mDeviceConfig;
-    @NonNull private final boolean mGeoDetectionSupported;
+    @NonNull private final ServiceConfigAccessor mServiceConfigAccessor;
     @NonNull private final LocationManager mLocationManager;
 
     // @NonNull after setConfigChangeListener() is called.
@@ -68,17 +64,16 @@
     private ConfigurationChangeListener mConfigChangeListener;
 
     EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
-            @NonNull DeviceConfig deviceConfig, boolean geoDetectionSupported) {
+            @NonNull ServiceConfigAccessor serviceConfigAccessor) {
         mContext = Objects.requireNonNull(context);
         mHandler = Objects.requireNonNull(handler);
-        Executor handlerExecutor = new HandlerExecutor(mHandler);
         mCr = context.getContentResolver();
         mUserManager = context.getSystemService(UserManager.class);
         mLocationManager = context.getSystemService(LocationManager.class);
-        mDeviceConfig = deviceConfig;
-        mGeoDetectionSupported = geoDetectionSupported;
+        mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
 
-        // Wire up the change listeners. All invocations are performed on the mHandler thread.
+        // Wire up the config change listeners. All invocations are performed on the mHandler
+        // thread.
 
         // Listen for the user changing / the user's location mode changing.
         IntentFilter filter = new IntentFilter();
@@ -112,13 +107,6 @@
                         handleConfigChangeOnHandlerThread();
                     }
                 }, UserHandle.USER_ALL);
-
-        // Add async callbacks for changes to server-side flags: some of the flags affect device /
-        // user config. All changes can be treated like a config change. If flags that affect config
-        // haven't changed then call will be a no-op.
-        mDeviceConfig.addListener(
-                handlerExecutor,
-                this::handleConfigChangeOnHandlerThread);
     }
 
     private void handleConfigChangeOnHandlerThread() {
@@ -140,10 +128,12 @@
     @Override
     public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
         return new ConfigurationInternal.Builder(userId)
-                .setUserConfigAllowed(isUserConfigAllowed(userId))
-                .setAutoDetectionSupported(isAutoDetectionSupported())
-                .setGeoDetectionSupported(isGeoDetectionSupported())
+                .setAutoDetectionFeatureSupported(
+                        mServiceConfigAccessor.isAutoDetectionFeatureSupported())
+                .setGeoDetectionFeatureSupported(
+                        mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported())
                 .setAutoDetectionEnabled(isAutoDetectionEnabled())
+                .setUserConfigAllowed(isUserConfigAllowed(userId))
                 .setLocationEnabled(isLocationEnabled(userId))
                 .setGeoDetectionEnabled(isGeoDetectionEnabled(userId))
                 .build();
@@ -186,18 +176,19 @@
         // time zone detection: if we wrote it down then we'd set the value explicitly, which would
         // prevent detecting "default" later. That might influence what happens on later releases
         // that support new types of auto detection on the same hardware.
-        if (isAutoDetectionSupported()) {
+        if (mServiceConfigAccessor.isAutoDetectionFeatureSupported()) {
             final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled();
             setAutoDetectionEnabledIfRequired(autoDetectionEnabled);
 
-            // Avoid writing the geo detection enabled setting for devices that do not support geo
-            // time zone detection: if we wrote it down then we'd set the value explicitly, which
-            // would prevent detecting "default" later. That might influence what happens on later
-            // releases that support geo detection on the same hardware.
-            // Also avoid writing the geo detection enabled setting for devices that are currently
-            // force-enabled: otherwise we might overwrite a droidfood user's real setting
-            // permanently.
-            if (isGeoDetectionSupported() && !isGeoDetectionForceEnabled()) {
+            // Avoid writing the geo detection enabled setting for devices with settings that
+            // are currently overridden by server flags: otherwise we might overwrite a droidfood
+            // user's real setting permanently.
+            // Also avoid writing the geo detection enabled setting for devices that do not support
+            // geo time zone detection: if we wrote it down then we'd set the value explicitly,
+            // which would prevent detecting "default" later. That might influence what happens on
+            // later releases that start to support geo detection on the same hardware.
+            if (!mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride().isPresent()
+                    && mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) {
                 final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
                 setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled);
             }
@@ -209,14 +200,6 @@
         return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle);
     }
 
-    private boolean isAutoDetectionSupported() {
-        return deviceHasTelephonyNetwork() || isGeoDetectionSupported();
-    }
-
-    private boolean isGeoDetectionSupported() {
-        return mGeoDetectionSupported;
-    }
-
     private boolean isAutoDetectionEnabled() {
         return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
     }
@@ -237,24 +220,20 @@
 
     private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
         // We may never use this, but it gives us a way to force location-based time zone detection
-        // on for testers (where their other settings allow).
-        boolean forceEnabled = isGeoDetectionForceEnabled();
-        if (forceEnabled) {
-            return true;
+        // on/off for testers (but only where their other settings would allow them to turn it on
+        // for themselves).
+        Optional<Boolean> override = mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride();
+        if (override.isPresent()) {
+            return override.get();
         }
 
-        final boolean geoDetectionEnabledByDefault = mDeviceConfig.getBoolean(
-                DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT, false);
+        final boolean geoDetectionEnabledByDefault =
+                mServiceConfigAccessor.isGeoDetectionEnabledForUsersByDefault();
         return Settings.Secure.getIntForUser(mCr,
                 Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
                 (geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0;
     }
 
-    private boolean isGeoDetectionForceEnabled() {
-        return mDeviceConfig.getBoolean(
-                DeviceConfig.KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED, false);
-    }
-
     private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) {
         // See comment in setAutoDetectionEnabledIfRequired. http://b/171953500
         if (isGeoDetectionEnabled(userId) != enabled) {
@@ -262,10 +241,4 @@
                     enabled ? 1 : 0, userId);
         }
     }
-
-    private boolean deviceHasTelephonyNetwork() {
-        // TODO b/150583524 Avoid the use of a deprecated API.
-        return mContext.getSystemService(ConnectivityManager.class)
-                .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
-    }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
new file mode 100644
index 0000000..86c32f8
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -0,0 +1,249 @@
+/*
+ * 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.timezonedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.os.SystemProperties;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.timedetector.ServerFlags;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * A singleton that provides access to service configuration for time zone detection. This hides how
+ * configuration is split between static, compile-time config and dynamic, server-pushed flags. It
+ * provides a rudimentary mechanism to signal when values have changed.
+ */
+public final class ServiceConfigAccessor {
+
+    private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet(
+            new ArraySet<>(new String[] {
+                    ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
+                    ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
+                    ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
+                    ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+                    ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+                    ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+                    ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+                    ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS
+            }));
+
+    // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
+    private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1);
+    // TODO(b/179488561): Put this back to 1 minute when primary provider is fully implemented
+    private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
+            Duration.ofSeconds(20);
+    private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
+
+    private static final Object SLOCK = new Object();
+
+    /** The singleton instance. Initialized once in {@link #getInstance(Context)}. */
+    @GuardedBy("SLOCK")
+    @Nullable
+    private static ServiceConfigAccessor sInstance;
+
+    @NonNull private final Context mContext;
+
+    /**
+     * An ultimate "feature switch" for location-based time zone detection. If this is
+     * {@code false}, the device cannot support the feature without a config change or a reboot:
+     * This affects what services are started on boot to minimize expense when the feature is not
+     * wanted.
+     */
+    private final boolean mGeoDetectionFeatureSupportedInConfig;
+
+    @NonNull private final ServerFlags mServerFlags;
+
+    private ServiceConfigAccessor(@NonNull Context context) {
+        mContext = Objects.requireNonNull(context);
+
+        // The config value is expected to be the main feature flag. Platform developers can also
+        // force enable the feature using a persistent system property. Because system properties
+        // can change, this value is cached and only changes on reboot.
+        mGeoDetectionFeatureSupportedInConfig = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection)
+                || SystemProperties.getBoolean(
+                "persist.sys.location_time_zone_detection_feature_supported", false);
+
+        mServerFlags = ServerFlags.getInstance(mContext);
+    }
+
+    /** Returns the singleton instance. */
+    public static ServiceConfigAccessor getInstance(Context context) {
+        synchronized (SLOCK) {
+            if (sInstance == null) {
+                sInstance = new ServiceConfigAccessor(context);
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Adds a listener that will be called server flags related to this class change. The callbacks
+     * are delivered on the main looper thread.
+     *
+     * <p>Note: Only for use by long-lived objects. There is deliberately no associated remove
+     * method.
+     */
+    public void addListener(@NonNull ConfigurationChangeListener listener) {
+        mServerFlags.addListener(listener, SERVER_FLAGS_KEYS_TO_WATCH);
+    }
+
+    /** Returns {@code true} if any form of automatic time zone detection is supported. */
+    public boolean isAutoDetectionFeatureSupported() {
+        return deviceHasTelephonyNetwork() || isGeoTimeZoneDetectionFeatureSupported();
+    }
+
+    private boolean deviceHasTelephonyNetwork() {
+        // TODO b/150583524 Avoid the use of a deprecated API.
+        return mContext.getSystemService(ConnectivityManager.class)
+                .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+    }
+
+    /**
+     * Returns {@code true} if the location-based time zone detection feature can be supported on
+     * this device at all according to config. When {@code false}, implies that various other
+     * location-based settings will be turned off or rendered meaningless. Typically {@link
+     * #isGeoTimeZoneDetectionFeatureSupported()} should be used instead.
+     */
+    public boolean isGeoTimeZoneDetectionFeatureSupportedInConfig() {
+        return mGeoDetectionFeatureSupportedInConfig;
+    }
+
+    /**
+     * Returns {@code true} if the location-based time zone detection feature is supported on the
+     * device. This can be used during feature testing on builds that are capable of location time
+     * zone detection to enable / disable the feature for some users.
+     */
+    public boolean isGeoTimeZoneDetectionFeatureSupported() {
+        return mGeoDetectionFeatureSupportedInConfig
+                && isGeoTimeZoneDetectionFeatureSupportedInternal();
+    }
+
+    private boolean isGeoTimeZoneDetectionFeatureSupportedInternal() {
+        final boolean defaultEnabled = true;
+        return mServerFlags.getBoolean(
+                ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
+                defaultEnabled);
+    }
+
+    /**
+     * Returns {@code true} if the primary location time zone provider can be used.
+     */
+    public boolean isPrimaryLocationTimeZoneProviderEnabled() {
+        return getPrimaryLocationTimeZoneProviderEnabledOverride()
+                .orElse(isPrimaryLocationTimeZoneProviderEnabledInConfig());
+    }
+
+    private boolean isPrimaryLocationTimeZoneProviderEnabledInConfig() {
+        int providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
+        return getConfigBoolean(providerEnabledConfigId);
+    }
+
+    @NonNull
+    private Optional<Boolean> getPrimaryLocationTimeZoneProviderEnabledOverride() {
+        return mServerFlags.getOptionalBoolean(
+                ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
+    }
+
+    /**
+     * Returns {@code true} if the secondary location time zone provider can be used.
+     */
+    public boolean isSecondaryLocationTimeZoneProviderEnabled() {
+        return getSecondaryLocationTimeZoneProviderEnabledOverride()
+                .orElse(isSecondaryLocationTimeZoneProviderEnabledInConfig());
+    }
+
+    private boolean isSecondaryLocationTimeZoneProviderEnabledInConfig() {
+        int providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
+        return getConfigBoolean(providerEnabledConfigId);
+    }
+
+    @NonNull
+    private Optional<Boolean> getSecondaryLocationTimeZoneProviderEnabledOverride() {
+        return mServerFlags.getOptionalBoolean(
+                ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
+    }
+
+    /**
+     * Returns whether location time zone detection is enabled for users when there's no setting
+     * value. Intended for use during feature release testing to "opt-in" users that haven't shown
+     * an explicit preference.
+     */
+    public boolean isGeoDetectionEnabledForUsersByDefault() {
+        return mServerFlags.getBoolean(
+                ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT, false);
+    }
+
+    /**
+     * Returns whether location time zone detection is force enabled/disabled for users. Intended
+     * for use during feature release testing to force a given state.
+     */
+    @NonNull
+    public Optional<Boolean> getGeoDetectionSettingEnabledOverride() {
+        return mServerFlags.getOptionalBoolean(
+                ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE);
+    }
+
+    /**
+     * Returns the time to send to a location time zone provider that informs it how long it has
+     * to return its first time zone suggestion.
+     */
+    @NonNull
+    public Duration getLocationTimeZoneProviderInitializationTimeout() {
+        return mServerFlags.getDurationFromMillis(
+                ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+                DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
+    }
+
+    /**
+     * Returns the time added to {@link #getLocationTimeZoneProviderInitializationTimeout()} by the
+     * server before unilaterally declaring the provider is uncertain.
+     */
+    @NonNull
+    public Duration getLocationTimeZoneProviderInitializationTimeoutFuzz() {
+        return mServerFlags.getDurationFromMillis(
+                ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+                DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
+    }
+
+    /**
+     * Returns the time after uncertainty is detected by providers before the location time zone
+     * manager makes a suggestion to the time zone detector.
+     */
+    @NonNull
+    public Duration getLocationTimeZoneUncertaintyDelay() {
+        return mServerFlags.getDurationFromMillis(
+                ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
+                DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
+    }
+
+    private boolean getConfigBoolean(int providerEnabledConfigId) {
+        Resources resources = mContext.getResources();
+        return resources.getBoolean(providerEnabledConfigId);
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index 203a8a4..cd220b1 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -27,16 +27,21 @@
  */
 public interface TimeZoneDetectorInternal extends Dumpable.Container {
 
-    /** Adds a listener that will be invoked when time zone detection configuration is changed. */
-    void addConfigurationListener(ConfigurationChangeListener listener);
+    /** Adds a listener that will be invoked when {@link ConfigurationInternal} may have changed. */
+    void addConfigurationListener(@NonNull ConfigurationChangeListener listener);
 
     /**
      * Removes a listener previously added via {@link
      * #addConfigurationListener(ConfigurationChangeListener)}.
      */
-    void removeConfigurationListener(ConfigurationChangeListener listener);
+    void removeConfigurationListener(@NonNull ConfigurationChangeListener listener);
 
-    /** Returns the {@link ConfigurationInternal} for the current user. */
+    /**
+     * Returns a snapshot of the {@link ConfigurationInternal} for the current user. This is only a
+     * snapshot so callers must use {@link #addConfigurationListener(ConfigurationChangeListener)}
+     * to be notified when it changes.
+     */
+    @NonNull
     ConfigurationInternal getCurrentUserConfigurationInternal();
 
     /**
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index bd71ddf..c20400a 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -33,7 +33,6 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
-import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -62,27 +61,6 @@
     static final String TAG = "time_zone_detector";
 
     /**
-     * A "feature switch" for location-based time zone detection. If this is {@code false}. It is
-     * initialized and never refreshed; it affects what services are started on boot so consistency
-     * is important.
-     */
-    @Nullable
-    private static Boolean sGeoLocationTimeZoneDetectionSupported;
-
-    /** Returns {@code true} if the location-based time zone detection feature is enabled. */
-    public static boolean isGeoLocationTimeZoneDetectionSupported(Context context) {
-        if (sGeoLocationTimeZoneDetectionSupported == null) {
-            // The config value is expected to be the main switch. Platform developers can also
-            // enable the feature using a persistent system property.
-            sGeoLocationTimeZoneDetectionSupported = context.getResources().getBoolean(
-                    com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection)
-                    || SystemProperties.getBoolean(
-                            "persist.sys.location_time_zone_detection_feature_enabled", false);
-        }
-        return sGeoLocationTimeZoneDetectionSupported;
-    }
-
-    /**
      * Handles the service lifecycle for {@link TimeZoneDetectorService} and
      * {@link TimeZoneDetectorInternalImpl}.
      */
@@ -98,11 +76,10 @@
             Context context = getContext();
             Handler handler = FgThread.getHandler();
 
-            boolean geolocationTimeZoneDetectionSupported =
-                    isGeoLocationTimeZoneDetectionSupported(context);
+            ServiceConfigAccessor serviceConfigAccessor =
+                    ServiceConfigAccessor.getInstance(context);
             TimeZoneDetectorStrategy timeZoneDetectorStrategy =
-                    TimeZoneDetectorStrategyImpl.create(
-                            context, handler, geolocationTimeZoneDetectionSupported);
+                    TimeZoneDetectorStrategyImpl.create(context, handler, serviceConfigAccessor);
 
             // Create and publish the local service for use by internal callers.
             TimeZoneDetectorInternal internal =
@@ -330,7 +307,8 @@
     boolean isGeoTimeZoneDetectionSupported() {
         enforceManageTimeZoneDetectorPermission();
 
-        return isGeoLocationTimeZoneDetectionSupported(mContext);
+        return ServiceConfigAccessor.getInstance(mContext)
+                .isGeoTimeZoneDetectionFeatureSupported();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 0b1d6d7..8266f12 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -75,17 +75,21 @@
 public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container {
 
     /**
-     * Sets a listener that will be triggered whenever time zone detection configuration is
+     * Adds a listener that will be triggered whenever {@link ConfigurationInternal} may have
      * changed.
      */
     void addConfigChangeListener(@NonNull ConfigurationChangeListener listener);
 
-    /** Returns the user's time zone configuration. */
+    /**
+     * Returns a snapshot of the configuration that controls time zone detector behavior for the
+     * specified user.
+     */
     @NonNull
     ConfigurationInternal getConfigurationInternal(@UserIdInt int userId);
 
     /**
-     * Returns the configuration that controls time zone detector behavior for the current user.
+     * Returns a snapshot of the configuration that controls time zone detector behavior for the
+     * current user.
      */
     @NonNull
     ConfigurationInternal getCurrentUserConfigurationInternal();
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index c464b74..d163a0e 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -38,7 +38,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.timedetector.DeviceConfig;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -204,11 +203,9 @@
      */
     public static TimeZoneDetectorStrategyImpl create(
             @NonNull Context context, @NonNull Handler handler,
-            boolean geoDetectionSupported) {
+            @NonNull ServiceConfigAccessor serviceConfigAccessor) {
 
-        DeviceConfig deviceConfig = new DeviceConfig();
-        EnvironmentImpl environment = new EnvironmentImpl(
-                context, handler, deviceConfig, geoDetectionSupported);
+        Environment environment = new EnvironmentImpl(context, handler, serviceConfigAccessor);
         return new TimeZoneDetectorStrategyImpl(environment);
     }
 
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
index e463ee2..98e984d 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
@@ -19,9 +19,9 @@
 import android.annotation.NonNull;
 
 import com.android.server.LocalServices;
-import com.android.server.timedetector.DeviceConfig;
 import com.android.server.timezonedetector.ConfigurationChangeListener;
 import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.ServiceConfigAccessor;
 import com.android.server.timezonedetector.TimeZoneDetectorInternal;
 
 import java.time.Duration;
@@ -33,28 +33,19 @@
  */
 class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
 
-    // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
-    private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1);
-    // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
-    private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
-            Duration.ofSeconds(20);
-    private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
-
     @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
-    @NonNull private final LocationTimeZoneProviderController mController;
-    @NonNull private final DeviceConfig mDeviceConfig;
+    @NonNull private final ServiceConfigAccessor mServiceConfigAccessor;
     @NonNull private final ConfigurationChangeListener mConfigurationChangeListener;
 
     ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
-            @NonNull DeviceConfig deviceConfig,
+            @NonNull ServiceConfigAccessor serviceConfigAccessor,
             @NonNull LocationTimeZoneProviderController controller) {
         super(threadingDomain);
-        mController = Objects.requireNonNull(controller);
-        mDeviceConfig = Objects.requireNonNull(deviceConfig);
+        mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
         mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);
 
         // Listen for configuration changes.
-        mConfigurationChangeListener = () -> mThreadingDomain.post(mController::onConfigChanged);
+        mConfigurationChangeListener = () -> mThreadingDomain.post(controller::onConfigChanged);
         mTimeZoneDetectorInternal.addConfigurationListener(mConfigurationChangeListener);
     }
 
@@ -73,24 +64,18 @@
     @Override
     @NonNull
     Duration getProviderInitializationTimeout() {
-        return mDeviceConfig.getDurationFromMillis(
-                DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
-                DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
+        return mServiceConfigAccessor.getLocationTimeZoneProviderInitializationTimeout();
     }
 
     @Override
     @NonNull
     Duration getProviderInitializationTimeoutFuzz() {
-        return mDeviceConfig.getDurationFromMillis(
-                DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
-                DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
+        return mServiceConfigAccessor.getLocationTimeZoneProviderInitializationTimeoutFuzz();
     }
 
     @Override
     @NonNull
     Duration getUncertaintyDelay() {
-        return mDeviceConfig.getDurationFromMillis(
-                DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
-                DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
+        return mServiceConfigAccessor.getLocationTimeZoneUncertaintyDelay();
     }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 364eaf8..0d1692a 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -21,11 +21,11 @@
 import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
 import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
 import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
+import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.res.Resources;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -43,9 +43,8 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
-import com.android.server.timedetector.DeviceConfig;
+import com.android.server.timezonedetector.ServiceConfigAccessor;
 import com.android.server.timezonedetector.TimeZoneDetectorInternal;
-import com.android.server.timezonedetector.TimeZoneDetectorService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -96,28 +95,31 @@
 
         private LocationTimeZoneManagerService mService;
 
+        @NonNull
+        private final ServiceConfigAccessor mServerConfigAccessor;
+
         public Lifecycle(@NonNull Context context) {
             super(Objects.requireNonNull(context));
+            mServerConfigAccessor = ServiceConfigAccessor.getInstance(context);
         }
 
         @Override
         public void onStart() {
             Context context = getContext();
-            if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
+            if (mServerConfigAccessor.isGeoTimeZoneDetectionFeatureSupportedInConfig()) {
                 mService = new LocationTimeZoneManagerService(context);
 
                 // The service currently exposes no LocalService or Binder API, but it extends
                 // Binder and is registered as a binder service so it can receive shell commands.
-                publishBinderService("location_time_zone_manager", mService);
+                publishBinderService(SERVICE_NAME, mService);
             } else {
-                Slog.i(TAG, getClass() + " is disabled");
+                Slog.d(TAG, "Geo time zone detection feature is disabled in config");
             }
         }
 
         @Override
-        public void onBootPhase(int phase) {
-            Context context = getContext();
-            if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
+        public void onBootPhase(@BootPhase int phase) {
+            if (mServerConfigAccessor.isGeoTimeZoneDetectionFeatureSupportedInConfig()) {
                 if (phase == PHASE_SYSTEM_SERVICES_READY) {
                     // The location service must be functioning after this boot phase.
                     mService.onSystemReady();
@@ -159,6 +161,9 @@
     /** The shared lock from {@link #mThreadingDomain}. */
     @NonNull private final Object mSharedLock;
 
+    @NonNull
+    private final ServiceConfigAccessor mServiceConfigAccessor;
+
     // Lazily initialized. Can be null if the service has been stopped.
     @GuardedBy("mSharedLock")
     private ControllerImpl mLocationTimeZoneDetectorController;
@@ -180,24 +185,38 @@
         mHandler = FgThread.getHandler();
         mThreadingDomain = new HandlerThreadingDomain(mHandler);
         mSharedLock = mThreadingDomain.getLockObject();
+        mServiceConfigAccessor = ServiceConfigAccessor.getInstance(mContext);
     }
 
+    // According to the SystemService docs: All lifecycle methods are called from the system
+    // server's main looper thread.
     void onSystemReady() {
-        // Called on an arbitrary thread during initialization.
-        synchronized (mSharedLock) {
-            // TODO(b/152744911): LocationManagerService watches for packages disappearing. Need to
-            //  do anything here?
+        mServiceConfigAccessor.addListener(this::handleServiceConfigurationChangedOnMainThread);
+    }
 
-            // TODO(b/152744911): LocationManagerService watches for foreground app changes. Need to
-            //  do anything here?
-            // TODO(b/152744911): LocationManagerService watches screen state. Need to do anything
-            //  here?
+    private void handleServiceConfigurationChangedOnMainThread() {
+        // This method is called on the main thread, but service logic takes place on the threading
+        // domain thread, so we post the work there.
+
+        // The way all service-level configuration changes are handled is to just restart this
+        // service - this is simple and effective, and service configuration changes should be rare.
+        mThreadingDomain.post(this::restartIfRequiredOnDomainThread);
+    }
+
+    private void restartIfRequiredOnDomainThread() {
+        mThreadingDomain.assertCurrentThread();
+
+        synchronized (mSharedLock) {
+            // Stop and start the service, waiting until completion.
+            stopOnDomainThread();
+            startOnDomainThread();
         }
     }
 
+    // According to the SystemService docs: All lifecycle methods are called from the system
+    // server's main looper thread.
     void onSystemThirdPartyAppsCanStart() {
-        // Called on an arbitrary thread during initialization. We do not want to wait for
-        // completion as it would delay boot.
+        // Do not wait for completion as it would delay boot.
         final boolean waitForCompletion = false;
         startInternal(waitForCompletion);
     }
@@ -205,6 +224,9 @@
     /**
      * Starts the service during server initialization or during tests after a call to
      * {@link #stop()}.
+     *
+     * <p>Because this method posts work to the {@code mThreadingDomain} thread and waits for
+     * completion, it cannot be called from the {@code mThreadingDomain} thread.
      */
     void start() {
         enforceManageTimeZoneDetectorPermission();
@@ -214,28 +236,17 @@
     }
 
     /**
-     * Starts the service during server initialization or during tests after a call to
-     * {@link #stop()}.
+     * Starts the service during server initialization, if the configuration changes or during tests
+     * after a call to {@link #stop()}.
      *
      * <p>To avoid tests needing to sleep, when {@code waitForCompletion} is {@code true}, this
      * method will not return until all the system server components have started.
+     *
+     * <p>Because this method posts work to the {@code mThreadingDomain} thread, it cannot be
+     * called from the {@code mThreadingDomain} thread when {@code waitForCompletion} is true.
      */
     private void startInternal(boolean waitForCompletion) {
-        Runnable runnable = () -> {
-            synchronized (mSharedLock) {
-                if (mLocationTimeZoneDetectorController == null) {
-                    LocationTimeZoneProvider primary = createPrimaryProvider();
-                    LocationTimeZoneProvider secondary = createSecondaryProvider();
-                    mLocationTimeZoneDetectorController =
-                            new ControllerImpl(mThreadingDomain, primary, secondary);
-                    DeviceConfig deviceConfig = new DeviceConfig();
-                    mEnvironment = new ControllerEnvironmentImpl(
-                            mThreadingDomain, deviceConfig, mLocationTimeZoneDetectorController);
-                    ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
-                    mLocationTimeZoneDetectorController.initialize(mEnvironment, callback);
-                }
-            }
-        };
+        Runnable runnable = this::startOnDomainThread;
         if (waitForCompletion) {
             mThreadingDomain.postAndWait(runnable, BLOCKING_OP_WAIT_DURATION_MILLIS);
         } else {
@@ -243,11 +254,38 @@
         }
     }
 
+    private void startOnDomainThread() {
+        mThreadingDomain.assertCurrentThread();
+
+        synchronized (mSharedLock) {
+            if (!mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) {
+                debugLog("Not starting " + SERVICE_NAME + ": it is disabled in service config");
+                return;
+            }
+
+            if (mLocationTimeZoneDetectorController == null) {
+                LocationTimeZoneProvider primary = createPrimaryProvider();
+                LocationTimeZoneProvider secondary = createSecondaryProvider();
+
+                ControllerImpl controller =
+                        new ControllerImpl(mThreadingDomain, primary, secondary);
+                ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl(
+                        mThreadingDomain, mServiceConfigAccessor, controller);
+                ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
+                controller.initialize(environment, callback);
+
+                mEnvironment = environment;
+                mLocationTimeZoneDetectorController = controller;
+            }
+        }
+    }
+
+    @NonNull
     private LocationTimeZoneProvider createPrimaryProvider() {
         LocationTimeZoneProviderProxy proxy;
         if (isProviderInSimulationMode(PRIMARY_PROVIDER_NAME)) {
             proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
-        } else if (isProviderDisabled(PRIMARY_PROVIDER_NAME)) {
+        } else if (!isProviderEnabled(PRIMARY_PROVIDER_NAME)) {
             proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
         } else {
             proxy = new RealLocationTimeZoneProviderProxy(
@@ -262,11 +300,12 @@
         return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
     }
 
+    @NonNull
     private LocationTimeZoneProvider createSecondaryProvider() {
         LocationTimeZoneProviderProxy proxy;
         if (isProviderInSimulationMode(SECONDARY_PROVIDER_NAME)) {
             proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
-        } else if (isProviderDisabled(SECONDARY_PROVIDER_NAME)) {
+        } else if (!isProviderEnabled(SECONDARY_PROVIDER_NAME)) {
             proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
         } else {
             proxy = new RealLocationTimeZoneProviderProxy(
@@ -282,33 +321,27 @@
     }
 
     /** Used for bug triage and in tests to simulate provider events. */
-    private boolean isProviderInSimulationMode(String providerName) {
+    private boolean isProviderInSimulationMode(@NonNull String providerName) {
         return isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_SIMULATED);
     }
 
-    /** Used for bug triage, tests and experiments to remove a provider. */
-    private boolean isProviderDisabled(String providerName) {
-        return !isProviderEnabledInConfig(providerName)
-                || isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED);
-    }
+    /** Used for bug triage, and by tests and experiments to remove a provider. */
+    private boolean isProviderEnabled(@NonNull String providerName) {
+        if (isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED)) {
+            return false;
+        }
 
-    private boolean isProviderEnabledInConfig(String providerName) {
-        int providerEnabledConfigId;
         switch (providerName) {
             case PRIMARY_PROVIDER_NAME: {
-                providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
-                break;
+                return mServiceConfigAccessor.isPrimaryLocationTimeZoneProviderEnabled();
             }
             case SECONDARY_PROVIDER_NAME: {
-                providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
-                break;
+                return mServiceConfigAccessor.isSecondaryLocationTimeZoneProviderEnabled();
             }
             default: {
                 throw new IllegalArgumentException(providerName);
             }
         }
-        Resources resources = mContext.getResources();
-        return resources.getBoolean(providerEnabledConfigId);
     }
 
     private boolean isProviderModeOverrideSet(@NonNull String providerName, @NonNull String mode) {
@@ -326,22 +359,29 @@
     }
 
     /**
-     * Stops the service for tests. To avoid tests needing to sleep, this method will not return
-     * until all the system server components have stopped.
+     * Stops the service for tests and other rare cases. To avoid tests needing to sleep, this
+     * method will not return until all the system server components have stopped.
+     *
+     * <p>Because this method posts work to the {@code mThreadingDomain} thread and waits it cannot
+     * be called from the {@code mThreadingDomain} thread.
      */
     void stop() {
         enforceManageTimeZoneDetectorPermission();
 
-        mThreadingDomain.postAndWait(() -> {
-            synchronized (mSharedLock) {
-                if (mLocationTimeZoneDetectorController != null) {
-                    mLocationTimeZoneDetectorController.destroy();
-                    mLocationTimeZoneDetectorController = null;
-                    mEnvironment.destroy();
-                    mEnvironment = null;
-                }
+        mThreadingDomain.postAndWait(this::stopOnDomainThread, BLOCKING_OP_WAIT_DURATION_MILLIS);
+    }
+
+    private void stopOnDomainThread() {
+        mThreadingDomain.assertCurrentThread();
+
+        synchronized (mSharedLock) {
+            if (mLocationTimeZoneDetectorController != null) {
+                mLocationTimeZoneDetectorController.destroy();
+                mLocationTimeZoneDetectorController = null;
+                mEnvironment.destroy();
+                mEnvironment = null;
             }
-        }, BLOCKING_OP_WAIT_DURATION_MILLIS);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
index b53150c..bdf4a70a 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
@@ -21,6 +21,7 @@
 import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
 import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
 import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
+import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_DUMP_STATE;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_RECORD_PROVIDER_STATES;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND;
@@ -102,7 +103,7 @@
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();
-        pw.println("Location Time Zone Manager (location_time_zone_manager) commands for tests:");
+        pw.printf("Location Time Zone Manager (%s) commands for tests:\n", SERVICE_NAME);
         pw.println("  help");
         pw.println("    Print this help text.");
         pw.printf("  %s\n", SHELL_COMMAND_START);
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
index 38211ef..531c62c 100644
--- a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
@@ -25,7 +25,6 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -41,6 +40,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.ServiceWatcher;
+import com.android.server.ServiceWatcher.BoundService;
 
 import java.util.Objects;
 import java.util.function.Predicate;
@@ -123,7 +123,7 @@
         return resolves;
     }
 
-    private void onBind(IBinder binder, ComponentName componentName) {
+    private void onBind(IBinder binder, BoundService boundService) {
         mThreadingDomain.assertCurrentThread();
 
         synchronized (mSharedLock) {
diff --git a/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
index 1482031..e8386bc 100644
--- a/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
+++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
@@ -63,8 +63,6 @@
         return mSendUpdates;
     }
 
-    // TODO(b/152744911) - once there are a couple of implementations, decide whether this needs to
-    //  be passed to the TimeZoneProviderService and remove if it is not useful.
     /**
      * Returns the maximum time that the provider is allowed to initialize before it is expected to
      * send an event of any sort. Only valid when {@link #sendUpdates()} is {@code true}. Failure to
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5d3b9c1..dd9619a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3624,7 +3624,7 @@
         }
 
         // Reset the last saved PiP snap fraction on removal.
-        mDisplayContent.mPinnedStackControllerLocked.onActivityHidden(mActivityComponent);
+        mDisplayContent.mPinnedTaskControllerLocked.onActivityHidden(mActivityComponent);
         mWmService.mEmbeddedWindowController.onActivityRemoved(this);
         mRemovingFromDisplay = false;
     }
@@ -4976,7 +4976,7 @@
         ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppStopped: %s", this);
         mAppStopped = true;
         // Reset the last saved PiP snap fraction on app stop.
-        mDisplayContent.mPinnedStackControllerLocked.onActivityHidden(mActivityComponent);
+        mDisplayContent.mPinnedTaskControllerLocked.onActivityHidden(mActivityComponent);
         destroySurfaces();
         // Remove any starting window that was added for this app if they are still around.
         removeStartingWindow();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 60ca725..6ce9048 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1708,7 +1708,7 @@
 
         // If the activity being launched is the same as the one currently at the top, then
         // we need to check if it should only be launched once.
-        final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
+        final Task topRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
         if (topRootTask != null) {
             startResult = deliverToCurrentTopIfNeeded(topRootTask, intentGrants);
             if (startResult != START_SUCCESS) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index b803fc3..af032c1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2095,10 +2095,14 @@
         }
     }
 
-    /** @return the max ANR delay from all registered {@link AnrController} instances */
-    public long getMaxAnrDelayMillis(ApplicationInfo info) {
+    /**
+     * @return the controller with the max ANR delay from all registered
+     * {@link AnrController} instances
+     */
+    @Nullable
+    public AnrController getAnrController(ApplicationInfo info) {
         if (info == null || info.packageName == null) {
-            return 0;
+            return null;
         }
 
         final ArrayList<AnrController> controllers;
@@ -2107,12 +2111,19 @@
         }
 
         final String packageName = info.packageName;
+        final int uid = info.uid;
         long maxDelayMs = 0;
+        AnrController controllerWithMaxDelay = null;
+
         for (AnrController controller : controllers) {
-            maxDelayMs = Math.max(maxDelayMs, controller.getAnrDelayMillis(packageName, info.uid));
+            long delayMs = controller.getAnrDelayMillis(packageName, uid);
+            if (delayMs > 0 && delayMs > maxDelayMs) {
+                controllerWithMaxDelay = controller;
+                maxDelayMs = delayMs;
+            }
         }
-        maxDelayMs = Math.max(maxDelayMs, 0);
-        return maxDelayMs;
+
+        return controllerWithMaxDelay;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index f505daa..a7312b3 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -67,39 +67,40 @@
  *              .setImeContainer(imeContainer)
  *              .setTaskDisplayAreas(rootTdaList);
  *
- *      // Build root hierarchy of front and rear DisplayAreaGroup.
- *      RootDisplayArea frontRoot = new RootDisplayArea(wmService, "FrontRoot", FEATURE_FRONT_ROOT);
- *      DisplayAreaPolicyBuilder.HierarchyBuilder frontGroupHierarchy =
- *          new DisplayAreaPolicyBuilder.HierarchyBuilder(frontRoot)
+ *      // Build root hierarchy of first and second DisplayAreaGroup.
+ *      RootDisplayArea firstRoot = new RootDisplayArea(wmService, "FirstRoot", FEATURE_FIRST_ROOT);
+ *      DisplayAreaPolicyBuilder.HierarchyBuilder firstGroupHierarchy =
+ *          new DisplayAreaPolicyBuilder.HierarchyBuilder(firstRoot)
  *              // (Optional) .addFeature(...)
- *              .setTaskDisplayAreas(frontTdaList);
+ *              .setTaskDisplayAreas(firstTdaList);
  *
- *      RootDisplayArea rearRoot = new RootDisplayArea(wmService, "RearRoot", FEATURE_REAR_ROOT);
- *      DisplayAreaPolicyBuilder.HierarchyBuilder rearGroupHierarchy =
- *          new DisplayAreaPolicyBuilder.HierarchyBuilder(rearRoot)
+ *      RootDisplayArea secondRoot = new RootDisplayArea(wmService, "SecondRoot",
+ *          FEATURE_REAR_ROOT);
+ *      DisplayAreaPolicyBuilder.HierarchyBuilder secondGroupHierarchy =
+ *          new DisplayAreaPolicyBuilder.HierarchyBuilder(secondRoot)
  *              // (Optional) .addFeature(...)
- *              .setTaskDisplayAreas(rearTdaList);
+ *              .setTaskDisplayAreas(secondTdaList);
  *
  *      // Define the function to select root for window to attach.
  *      BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc =
- *                (windowToken, bundle) -> {
- *                    if (bundle == null) {
+ *                (windowToken, options) -> {
+ *                    if (options == null) {
  *                        return root;
  *                    }
  *                    // OEMs need to define the condition.
  *                    if (...) {
- *                        return frontRoot;
+ *                        return firstRoot;
  *                    }
  *                    if (...) {
- *                        return rearRoot;
+ *                        return secondRoot;
  *                    }
  *                    return root;
  *                };
  *
  *      return new DisplayAreaPolicyBuilder()
  *                .setRootHierarchy(rootHierarchy)
- *                .addDisplayAreaGroupHierarchy(frontGroupHierarchy)
- *                .addDisplayAreaGroupHierarchy(rearGroupHierarchy)
+ *                .addDisplayAreaGroupHierarchy(firstGroupHierarchy)
+ *                .addDisplayAreaGroupHierarchy(secondGroupHierarchy)
  *                .setSelectRootForWindowFunc(selectRootForWindowFunc)
  *                .build(wmService, content);
  * </pre>
@@ -110,12 +111,12 @@
  *          - WindowedMagnification
  *              - DisplayArea.Tokens (Wallpapers can be attached here)
  *              - TaskDisplayArea
- *              - RootDisplayArea (FrontRoot)
+ *              - RootDisplayArea (FirstRoot)
  *                  - DisplayArea.Tokens (Wallpapers can be attached here)
  *                  - TaskDisplayArea
  *                  - DisplayArea.Tokens (windows above Tasks up to IME can be attached here)
  *                  - DisplayArea.Tokens (windows above IME can be attached here)
- *              - RootDisplayArea (RearRoot)
+ *              - RootDisplayArea (SecondRoot)
  *                  - DisplayArea.Tokens (Wallpapers can be attached here)
  *                  - TaskDisplayArea
  *                  - DisplayArea.Tokens (windows above Tasks up to IME can be attached here)
@@ -133,14 +134,23 @@
  * {@link RootDisplayArea}.
  */
 class DisplayAreaPolicyBuilder {
+
+    /**
+     * Key to specify the {@link RootDisplayArea} to attach the window to. Should be used by the
+     * function passed in from {@link #setSelectRootForWindowFunc(BiFunction)}
+     */
+    static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id";
+
     @Nullable private HierarchyBuilder mRootHierarchyBuilder;
-    private ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>();
+    private final ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders =
+            new ArrayList<>();
 
     /**
      * When a window is created, the policy will use this function, which takes window type and
      * options, to select the {@link RootDisplayArea} to place that window in. The selected root
      * can be either the one of the {@link #mRootHierarchyBuilder} or the one of any of the
      * {@link #mDisplayAreaGroupHierarchyBuilders}.
+     * @see DefaultSelectRootForWindowFunction as an example.
      **/
     @Nullable private BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
 
@@ -160,7 +170,10 @@
         return this;
     }
 
-    /** The policy will use this function to find the root to place windows in. */
+    /**
+     * The policy will use this function to find the root to place windows in.
+     * @see DefaultSelectRootForWindowFunction as an example.
+     */
     DisplayAreaPolicyBuilder setSelectRootForWindowFunc(
             BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) {
         mSelectRootForWindowFunc = selectRootForWindowFunc;
@@ -169,7 +182,7 @@
 
     /**
      * Makes sure the setting meets the requirement:
-     * 1. {@link mRootHierarchyBuilder} must be set.
+     * 1. {@link #mRootHierarchyBuilder} must be set.
      * 2. {@link RootDisplayArea} and {@link TaskDisplayArea} must have unique ids.
      * 3. {@link Feature} below the same {@link RootDisplayArea} must have unique ids.
      * 4. There must be exactly one {@link HierarchyBuilder} that contains the IME container.
@@ -309,11 +322,57 @@
             hierarchyBuilder.build();
             displayAreaGroupRoots.add(hierarchyBuilder.mRoot);
         }
+        // Use the default function if it is not specified otherwise.
+        if (mSelectRootForWindowFunc == null) {
+            mSelectRootForWindowFunc = new DefaultSelectRootForWindowFunction(
+                    mRootHierarchyBuilder.mRoot, displayAreaGroupRoots);
+        }
         return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
                 mSelectRootForWindowFunc);
     }
 
     /**
+     * The default function to be used for finding {@link RootDisplayArea} for window to be attached
+     * to if there is no other function set through {@link #setSelectRootForWindowFunc(BiFunction)}.
+     *
+     * When a window is created with {@link Bundle} options, this function will select the
+     * {@link RootDisplayArea} based on the options. Returns {@link #mDisplayRoot} if there is no
+     * match found.
+     */
+    private static class DefaultSelectRootForWindowFunction implements
+            BiFunction<Integer, Bundle, RootDisplayArea> {
+        final RootDisplayArea mDisplayRoot;
+        final List<RootDisplayArea> mDisplayAreaGroupRoots;
+
+        DefaultSelectRootForWindowFunction(RootDisplayArea displayRoot,
+                List<RootDisplayArea> displayAreaGroupRoots) {
+            mDisplayRoot = displayRoot;
+            mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
+        }
+
+        @Override
+        public RootDisplayArea apply(Integer windowType, Bundle options) {
+            if (mDisplayAreaGroupRoots.isEmpty()) {
+                return mDisplayRoot;
+            }
+
+            // Select the RootDisplayArea set in options.
+            if (options != null && options.containsKey(KEY_ROOT_DISPLAY_AREA_ID)) {
+                final int rootId = options.getInt(KEY_ROOT_DISPLAY_AREA_ID);
+                if (mDisplayRoot.mFeatureId == rootId) {
+                    return mDisplayRoot;
+                }
+                for (int i = mDisplayAreaGroupRoots.size() - 1; i >= 0; i--) {
+                    if (mDisplayAreaGroupRoots.get(i).mFeatureId == rootId) {
+                        return mDisplayAreaGroupRoots.get(i);
+                    }
+                }
+            }
+            return mDisplayRoot;
+        }
+    }
+
+    /**
      *  Builder to define {@link Feature} and {@link DisplayArea} hierarchy under a
      * {@link RootDisplayArea}
      */
@@ -672,15 +731,10 @@
 
         Result(WindowManagerService wmService, RootDisplayArea root,
                 List<RootDisplayArea> displayAreaGroupRoots,
-                @Nullable BiFunction<Integer, Bundle, RootDisplayArea>
-                        selectRootForWindowFunc) {
+                BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) {
             super(wmService, root);
             mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
-            mSelectRootForWindowFunc = selectRootForWindowFunc == null
-                    // Always return the highest level root of the logical display when the func is
-                    // not specified.
-                    ? (type, options) -> mRoot
-                    : selectRootForWindowFunc;
+            mSelectRootForWindowFunc = selectRootForWindowFunc;
 
             // Cache the default TaskDisplayArea for quick access.
             mDefaultTaskDisplayArea = mRoot.getItemFromTaskDisplayAreas(taskDisplayArea ->
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0143e70..eff4ea6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -36,6 +36,7 @@
 import static android.os.Build.VERSION_CODES.N;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+import static android.util.RotationUtils.deltaRotation;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
 import static android.view.Display.FLAG_PRIVATE;
@@ -47,7 +48,6 @@
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
 import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.View.GONE;
@@ -150,7 +150,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
-import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.ScreenOrientation;
@@ -158,10 +157,8 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Insets;
-import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.Region;
 import android.graphics.Region.Op;
 import android.hardware.HardwareBuffer;
@@ -206,6 +203,7 @@
 import android.view.RemoteAnimationDefinition;
 import android.view.RoundedCorners;
 import android.view.Surface;
+import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
@@ -449,8 +447,6 @@
     /** Save allocating when calculating rects */
     private final Rect mTmpRect = new Rect();
     private final Rect mTmpRect2 = new Rect();
-    private final RectF mTmpRectF = new RectF();
-    private final Matrix mTmpMatrix = new Matrix();
     private final Region mTmpRegion = new Region();
 
     /** Used for handing back size of display */
@@ -461,8 +457,8 @@
     /** Remove this display when animation on it has completed. */
     private boolean mDeferredRemoval;
 
-    final DockedStackDividerController mDividerControllerLocked;
-    final PinnedStackController mPinnedStackControllerLocked;
+    final DockedTaskDividerController mDividerControllerLocked;
+    final PinnedTaskController mPinnedTaskControllerLocked;
 
     final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>();
     /** A collection of windows that provide tap exclude regions inside of them. */
@@ -1012,8 +1008,8 @@
             mDisplayPolicy.systemReady();
         }
         mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius();
-        mDividerControllerLocked = new DockedStackDividerController(this);
-        mPinnedStackControllerLocked = new PinnedStackController(mWmService, this);
+        mDividerControllerLocked = new DockedTaskDividerController(this);
+        mPinnedTaskControllerLocked = new PinnedTaskController(mWmService, this);
 
         final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
                 .setOpaque(true)
@@ -1289,7 +1285,7 @@
         return mInsetsPolicy;
     }
 
-    @Surface.Rotation
+    @Rotation
     int getRotation() {
         return mDisplayRotation.getRotation();
     }
@@ -1479,7 +1475,7 @@
      * Returns a valid rotation if the activity can use different orientation than the display.
      * Otherwise {@link #ROTATION_UNDEFINED}.
      */
-    @Surface.Rotation
+    @Rotation
     int rotationForActivityInDifferentOrientation(@NonNull ActivityRecord r) {
         if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM) {
             return ROTATION_UNDEFINED;
@@ -1567,7 +1563,7 @@
             // has it own policy for bounds, the activity bounds based on parent is unknown.
             return false;
         }
-        if (mPinnedStackControllerLocked.isPipActiveOrWindowingModeChanging()) {
+        if (mPinnedTaskControllerLocked.isPipActiveOrWindowingModeChanging()) {
             // Use normal rotation animation because seamless PiP rotation is not supported yet.
             return false;
         }
@@ -1614,7 +1610,7 @@
      * Sets the provided record to {@link #mFixedRotationLaunchingApp} if possible to apply fixed
      * rotation transform to it and indicate that the display may be rotated after it is launched.
      */
-    void setFixedRotationLaunchingApp(@NonNull ActivityRecord r, @Surface.Rotation int rotation) {
+    void setFixedRotationLaunchingApp(@NonNull ActivityRecord r, @Rotation int rotation) {
         final WindowToken prevRotatedLaunchingApp = mFixedRotationLaunchingApp;
         if (prevRotatedLaunchingApp == r
                 && r.getWindowConfiguration().getRotation() == rotation) {
@@ -2296,12 +2292,12 @@
         }
     }
 
-    DockedStackDividerController getDockedDividerController() {
+    DockedTaskDividerController getDockedDividerController() {
         return mDividerControllerLocked;
     }
 
-    PinnedStackController getPinnedStackController() {
-        return mPinnedStackControllerLocked;
+    PinnedTaskController getPinnedTaskController() {
+        return mPinnedTaskControllerLocked;
     }
 
     /**
@@ -2382,10 +2378,10 @@
      * for bounds calculations.
      */
     void preOnConfigurationChanged() {
-        final PinnedStackController pinnedStackController = getPinnedStackController();
+        final PinnedTaskController pinnedTaskController = getPinnedTaskController();
 
-        if (pinnedStackController != null) {
-            getPinnedStackController().onConfigurationChanged();
+        if (pinnedTaskController != null) {
+            getPinnedTaskController().onConfigurationChanged();
         }
     }
 
@@ -2929,7 +2925,7 @@
         final boolean imeVisible = imeWin != null && imeWin.isVisible()
                 && imeWin.isDisplayed();
         final int imeHeight = getInputMethodWindowVisibleHeight();
-        mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight);
+        mPinnedTaskControllerLocked.setAdjustedForIme(imeVisible, imeHeight);
     }
 
     int getInputMethodWindowVisibleHeight() {
@@ -2951,27 +2947,10 @@
         forAllRootTasks(Task::prepareFreezingTaskBounds);
     }
 
-    void rotateBounds(int oldRotation, int newRotation, Rect bounds) {
-        getBounds(mTmpRect, newRotation);
-        rotateBounds(mTmpRect, oldRotation, newRotation, bounds);
-    }
-
-    void rotateBounds(Rect parentBounds, int oldRotation, int newRotation, Rect bounds) {
-        // Compute a transform matrix to undo the coordinate space transformation,
-        // and present the window at the same physical position it previously occupied.
-        final int deltaRotation = deltaRotation(newRotation, oldRotation);
-        createRotationMatrix(
-                deltaRotation, parentBounds.width(), parentBounds.height(), mTmpMatrix);
-
-        mTmpRectF.set(bounds);
-        mTmpMatrix.mapRect(mTmpRectF);
-        mTmpRectF.round(bounds);
-    }
-
-    static int deltaRotation(int oldRotation, int newRotation) {
-        int delta = newRotation - oldRotation;
-        if (delta < 0) delta += 4;
-        return delta;
+    void rotateBounds(@Rotation int oldRotation, @Rotation int newRotation, Rect inOutBounds) {
+        // Get display bounds on oldRotation as parent bounds for the rotation.
+        getBounds(mTmpRect, oldRotation);
+        RotationUtils.rotateBounds(inOutBounds, mTmpRect, oldRotation, newRotation);
     }
 
     public void setRotationAnimation(ScreenRotationAnimation screenRotationAnimation) {
@@ -2985,35 +2964,6 @@
         return mScreenRotationAnimation;
     }
 
-    private static void createRotationMatrix(int rotation, float displayWidth, float displayHeight,
-            Matrix outMatrix) {
-        // For rotations without Z-ordering we don't need the target rectangle's position.
-        createRotationMatrix(rotation, 0 /* rectLeft */, 0 /* rectTop */, displayWidth,
-                displayHeight, outMatrix);
-    }
-
-    static void createRotationMatrix(int rotation, float rectLeft, float rectTop,
-            float displayWidth, float displayHeight, Matrix outMatrix) {
-        switch (rotation) {
-            case ROTATION_0:
-                outMatrix.reset();
-                break;
-            case ROTATION_270:
-                outMatrix.setRotate(270, 0, 0);
-                outMatrix.postTranslate(0, displayHeight);
-                outMatrix.postTranslate(rectTop, 0);
-                break;
-            case ROTATION_180:
-                outMatrix.reset();
-                break;
-            case ROTATION_90:
-                outMatrix.setRotate(90, 0, 0);
-                outMatrix.postTranslate(displayWidth, 0);
-                outMatrix.postTranslate(-rectTop, rectLeft);
-                break;
-        }
-    }
-
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
             @WindowTraceLogLevel int logLevel) {
@@ -3200,7 +3150,7 @@
         }
 
         pw.println();
-        mPinnedStackControllerLocked.dump(prefix, pw);
+        mPinnedTaskControllerLocked.dump(prefix, pw);
 
         pw.println();
         mDisplayFrames.dump(prefix, pw);
@@ -3639,8 +3589,7 @@
     }
 
     private boolean isImeControlledByApp() {
-        return mImeInputTarget != null && !WindowConfiguration.isSplitScreenWindowingMode(
-                        mImeInputTarget.getWindowingMode());
+        return mImeInputTarget != null && !mImeInputTarget.inMultiWindowMode();
     }
 
     boolean isImeAttachedToApp() {
@@ -4289,17 +4238,14 @@
         out.set(left, top, left + width, top + height);
     }
 
-    private void getBounds(Rect out, int orientation) {
+    private void getBounds(Rect out, @Rotation int rotation) {
         getBounds(out);
 
         // Rotate the Rect if needed.
         final int currentRotation = mDisplayInfo.rotation;
-        final int rotationDelta = deltaRotation(currentRotation, orientation);
+        final int rotationDelta = deltaRotation(currentRotation, rotation);
         if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
-            createRotationMatrix(rotationDelta, mBaseDisplayWidth, mBaseDisplayHeight, mTmpMatrix);
-            mTmpRectF.set(out);
-            mTmpMatrix.mapRect(mTmpRectF);
-            mTmpRectF.round(out);
+            out.set(0, 0, out.height(), out.width());
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 5460e36..f64f04c 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -24,6 +24,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
+import static android.util.RotationUtils.deltaRotation;
+import static android.util.RotationUtils.rotateBounds;
 import static android.view.Display.TYPE_INTERNAL;
 import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
@@ -1046,11 +1048,17 @@
             return;
         }
 
-        // Get displayFrames bounds
-        sTmpDisplayFrameBounds.set(0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight);
+        // Get displayFrames bounds as it is on WindowState's rotation.
+        final int deltaRotation = deltaRotation(windowRotation, displayFrames.mRotation);
+        if (deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270) {
+            sTmpDisplayFrameBounds.set(
+                    0, 0, displayFrames.mDisplayHeight, displayFrames.mDisplayWidth);
+        } else {
+            sTmpDisplayFrameBounds.set(
+                    0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight);
+        }
         // Rotate the WindowState's bounds based on the displayFrames rotation
-        mDisplayContent.rotateBounds(sTmpDisplayFrameBounds, windowRotation,
-                displayFrames.mRotation, outBounds);
+        rotateBounds(outBounds, sTmpDisplayFrameBounds, deltaRotation);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index d5ded97..63cb38a 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.util.RotationUtils.deltaRotation;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
@@ -475,7 +476,7 @@
                 "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
                         displayId, rotation, oldRotation, lastOrientation);
 
-        if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
+        if (deltaRotation(oldRotation, rotation) != Surface.ROTATION_180) {
             mDisplayContent.mWaitingForConfig = true;
         }
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedTaskDividerController.java
similarity index 89%
rename from services/core/java/com/android/server/wm/DockedStackDividerController.java
rename to services/core/java/com/android/server/wm/DockedTaskDividerController.java
index de4bdaa..fb9d064 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedTaskDividerController.java
@@ -19,16 +19,16 @@
 import android.graphics.Rect;
 
 /**
- * Keeps information about the docked stack divider.
+ * Keeps information about the docked task divider.
  */
-public class DockedStackDividerController {
+public class DockedTaskDividerController {
 
     private final DisplayContent mDisplayContent;
     private boolean mResizing;
 
     private final Rect mTouchRegion = new Rect();
 
-    DockedStackDividerController(DisplayContent displayContent) {
+    DockedTaskDividerController(DisplayContent displayContent) {
         mDisplayContent = displayContent;
     }
 
@@ -58,6 +58,6 @@
 
     private void resetDragResizingChangeReported() {
         mDisplayContent.forAllWindows(WindowState::resetDragResizingChangeReported,
-                true /* traverseTopToBottom */ );
+                true /* traverseTopToBottom */);
     }
 }
diff --git a/services/core/java/com/android/server/wm/FactoryErrorDialog.java b/services/core/java/com/android/server/wm/FactoryErrorDialog.java
index 88b5475..afdf1ee 100644
--- a/services/core/java/com/android/server/wm/FactoryErrorDialog.java
+++ b/services/core/java/com/android/server/wm/FactoryErrorDialog.java
@@ -41,6 +41,11 @@
     public void onStop() {
     }
 
+    @Override
+    protected void closeDialog() {
+        /* Do nothing */
+    }
+
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
             throw new RuntimeException("Rebooting from failed factory test");
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
similarity index 79%
rename from services/core/java/com/android/server/wm/PinnedStackController.java
rename to services/core/java/com/android/server/wm/PinnedTaskController.java
index 8fe2481..15e078b 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -29,18 +29,18 @@
 import android.util.Log;
 import android.util.Slog;
 import android.view.DisplayInfo;
-import android.view.IPinnedStackListener;
+import android.view.IPinnedTaskListener;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * Holds the common state of the pinned stack between the system and SystemUI. If SystemUI ever
+ * Holds the common state of the pinned task between the system and SystemUI. If SystemUI ever
  * needs to be restarted, it will be notified with the last known state.
  *
- * Changes to the pinned stack also flow through this controller, and generally, the system only
- * changes the pinned stack bounds through this controller in two ways:
+ * Changes to the pinned task also flow through this controller, and generally, the system only
+ * changes the pinned task bounds through this controller in two ways:
  *
  * 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio
  *    and IME state into account.
@@ -49,18 +49,18 @@
  *    SystemUI adjustments (ie. expanded for menu, interaction, etc).
  *
  * Other changes in the system, including adjustment of IME, configuration change, and more are
- * handled by SystemUI (similar to the docked stack divider).
+ * handled by SystemUI (similar to the docked task divider).
  */
-class PinnedStackController {
+class PinnedTaskController {
 
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedTaskController" : TAG_WM;
 
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
 
-    private IPinnedStackListener mPinnedStackListener;
-    private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler =
-            new PinnedStackListenerDeathHandler();
+    private IPinnedTaskListener mPinnedTaskListener;
+    private final PinnedTaskListenerDeathHandler mPinnedTaskListenerDeathHandler =
+            new PinnedTaskListenerDeathHandler();
 
     /** Whether the PiP is entering or leaving. */
     private boolean mIsPipWindowingModeChanging;
@@ -72,7 +72,7 @@
     private ArrayList<RemoteAction> mActions = new ArrayList<>();
     private float mAspectRatio = -1f;
 
-    // Used to calculate stack bounds across rotations
+    // Used to calculate task bounds across rotations
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
 
     // The aspect ratio bounds of the PIP.
@@ -85,19 +85,19 @@
     /**
      * Handler for the case where the listener dies.
      */
-    private class PinnedStackListenerDeathHandler implements IBinder.DeathRecipient {
+    private class PinnedTaskListenerDeathHandler implements IBinder.DeathRecipient {
 
         @Override
         public void binderDied() {
             // Clean up the state if the listener dies
-            if (mPinnedStackListener != null) {
-                mPinnedStackListener.asBinder().unlinkToDeath(mPinnedStackListenerDeathHandler, 0);
+            if (mPinnedTaskListener != null) {
+                mPinnedTaskListener.asBinder().unlinkToDeath(mPinnedTaskListenerDeathHandler, 0);
             }
-            mPinnedStackListener = null;
+            mPinnedTaskListener = null;
         }
     }
 
-    PinnedStackController(WindowManagerService service, DisplayContent displayContent) {
+    PinnedTaskController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
         mDisplayContent = displayContent;
         mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
@@ -121,17 +121,17 @@
     }
 
     /**
-     * Registers a pinned stack listener.
+     * Registers a pinned task listener.
      */
-    void registerPinnedStackListener(IPinnedStackListener listener) {
+    void registerPinnedTaskListener(IPinnedTaskListener listener) {
         try {
-            listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0);
-            mPinnedStackListener = listener;
+            listener.asBinder().linkToDeath(mPinnedTaskListenerDeathHandler, 0);
+            mPinnedTaskListener = listener;
             notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
             notifyMovementBoundsChanged(false /* fromImeAdjustment */);
             notifyActionsChanged(mActions);
         } catch (RemoteException e) {
-            Log.e(TAG, "Failed to register pinned stack listener", e);
+            Log.e(TAG, "Failed to register pinned task listener", e);
         }
     }
 
@@ -139,8 +139,8 @@
      * @return whether the given {@param aspectRatio} is valid.
      */
     public boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
-        return Float.compare(mMinAspectRatio, aspectRatio) <= 0 &&
-                Float.compare(aspectRatio, mMaxAspectRatio) <= 0;
+        return Float.compare(mMinAspectRatio, aspectRatio) <= 0
+                && Float.compare(aspectRatio, mMaxAspectRatio) <= 0;
     }
 
     /** Returns {@code true} if the PiP is on screen or is changing windowing mode. */
@@ -152,7 +152,7 @@
         return pinnedTask != null && pinnedTask.hasChild();
     }
 
-    /** Sets whether a visible stack is changing from or to pinned mode. */
+    /** Sets whether a visible task is changing from or to pinned mode. */
     void setPipWindowingModeChanging(boolean isPipWindowingModeChanging) {
         mIsPipWindowingModeChanging = isPipWindowingModeChanging;
     }
@@ -162,9 +162,9 @@
      * so that the default bounds will be returned for the next session.
      */
     void onActivityHidden(ComponentName componentName) {
-        if (mPinnedStackListener == null) return;
+        if (mPinnedTaskListener == null) return;
         try {
-            mPinnedStackListener.onActivityHidden(componentName);
+            mPinnedTaskListener.onActivityHidden(componentName);
         } catch (RemoteException e) {
             Slog.e(TAG_WM, "Error delivering reset reentry fraction event.", e);
         }
@@ -223,9 +223,9 @@
      * Notifies listeners that the PIP needs to be adjusted for the IME.
      */
     private void notifyImeVisibilityChanged(boolean imeVisible, int imeHeight) {
-        if (mPinnedStackListener != null) {
+        if (mPinnedTaskListener != null) {
             try {
-                mPinnedStackListener.onImeVisibilityChanged(imeVisible, imeHeight);
+                mPinnedTaskListener.onImeVisibilityChanged(imeVisible, imeHeight);
             } catch (RemoteException e) {
                 Slog.e(TAG_WM, "Error delivering bounds changed event.", e);
             }
@@ -233,9 +233,9 @@
     }
 
     private void notifyAspectRatioChanged(float aspectRatio) {
-        if (mPinnedStackListener == null) return;
+        if (mPinnedTaskListener == null) return;
         try {
-            mPinnedStackListener.onAspectRatioChanged(aspectRatio);
+            mPinnedTaskListener.onAspectRatioChanged(aspectRatio);
         } catch (RemoteException e) {
             Slog.e(TAG_WM, "Error delivering aspect ratio changed event.", e);
         }
@@ -245,9 +245,9 @@
      * Notifies listeners that the PIP actions have changed.
      */
     private void notifyActionsChanged(List<RemoteAction> actions) {
-        if (mPinnedStackListener != null) {
+        if (mPinnedTaskListener != null) {
             try {
-                mPinnedStackListener.onActionsChanged(new ParceledListSlice(actions));
+                mPinnedTaskListener.onActionsChanged(new ParceledListSlice(actions));
             } catch (RemoteException e) {
                 Slog.e(TAG_WM, "Error delivering actions changed event.", e);
             }
@@ -259,11 +259,11 @@
      */
     private void notifyMovementBoundsChanged(boolean fromImeAdjustment) {
         synchronized (mService.mGlobalLock) {
-            if (mPinnedStackListener == null) {
+            if (mPinnedTaskListener == null) {
                 return;
             }
             try {
-                mPinnedStackListener.onMovementBoundsChanged(fromImeAdjustment);
+                mPinnedTaskListener.onMovementBoundsChanged(fromImeAdjustment);
             } catch (RemoteException e) {
                 Slog.e(TAG_WM, "Error delivering actions changed event.", e);
             }
@@ -271,7 +271,7 @@
     }
 
     void dump(String prefix, PrintWriter pw) {
-        pw.println(prefix + "PinnedStackController");
+        pw.println(prefix + "PinnedTaskController");
         pw.println(prefix + "  mIsImeShowing=" + mIsImeShowing);
         pw.println(prefix + "  mImeHeight=" + mImeHeight);
         pw.println(prefix + "  mAspectRatio=" + mAspectRatio);
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 533c82e..95a4f69e 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.util.RotationUtils.deltaRotation;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
@@ -174,7 +175,7 @@
         // apply rotation animation because there should be a top app shown as rotated. So the
         // specified original rotation customizes the direction of animation to have better look
         // when restoring the rotated app to the same rotation as current display.
-        final int delta = DisplayContent.deltaRotation(originalRotation, realOriginalRotation);
+        final int delta = deltaRotation(originalRotation, realOriginalRotation);
         final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270;
         mOriginalWidth = flipped ? originalHeight : originalWidth;
         mOriginalHeight = flipped ? originalWidth : originalHeight;
@@ -330,7 +331,7 @@
         // Compute the transformation matrix that must be applied
         // to the snapshot to make it stay in the same original position
         // with the current screen rotation.
-        int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0);
+        int delta = deltaRotation(rotation, Surface.ROTATION_0);
         RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
 
         setRotationTransform(t, mSnapshotInitialMatrix);
@@ -352,8 +353,7 @@
         mStarted = true;
 
         // Figure out how the screen has moved from the original rotation.
-        int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);
-
+        int delta = deltaRotation(mCurRotation, mOriginalRotation);
 
         final boolean customAnim;
         if (exitAnim != 0 && enterAnim != 0) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d60b6e0..29c7ff1 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2267,7 +2267,7 @@
         }
 
         if (pipChanging) {
-            mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(true);
+            mDisplayContent.getPinnedTaskController().setPipWindowingModeChanging(true);
             // If the top activity is using fixed rotation, it should be changing from PiP to
             // fullscreen with display orientation change. Do not notify fullscreen task organizer
             // because the restoration of task surface and the transformation of activity surface
@@ -2297,7 +2297,7 @@
             }
         } finally {
             if (pipChanging) {
-                mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(false);
+                mDisplayContent.getPinnedTaskController().setPipWindowingModeChanging(false);
             }
         }
 
@@ -2361,9 +2361,7 @@
             final int newRotation = getWindowConfiguration().getRotation();
             final boolean rotationChanged = prevRotation != newRotation;
             if (rotationChanged) {
-                mDisplayContent.rotateBounds(
-                        newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation,
-                        newBounds);
+                mDisplayContent.rotateBounds(prevRotation, newRotation, newBounds);
                 setBounds(newBounds);
             }
         }
@@ -7792,18 +7790,18 @@
             return;
         }
 
-        final PinnedStackController pinnedStackController =
-                getDisplayContent().getPinnedStackController();
+        final PinnedTaskController pinnedTaskController =
+                getDisplayContent().getPinnedTaskController();
 
-        if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) {
+        if (Float.compare(aspectRatio, pinnedTaskController.getAspectRatio()) == 0) {
             return;
         }
 
         // Notify the pinned stack controller about aspect ratio change.
         // This would result a callback delivered from SystemUI to WM to start animation,
         // if the bounds are ought to be altered due to aspect ratio change.
-        pinnedStackController.setAspectRatio(
-                pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
+        pinnedTaskController.setAspectRatio(
+                pinnedTaskController.isValidPictureInPictureAspectRatio(aspectRatio)
                         ? aspectRatio : -1f);
     }
 
@@ -7819,7 +7817,7 @@
             return;
         }
 
-        getDisplayContent().getPinnedStackController().setActions(actions);
+        getDisplayContent().getPinnedTaskController().setActions(actions);
     }
 
     /** Returns true if a removal action is still being deferred. */
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 9b72570..04ec4bd 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -130,8 +130,8 @@
         l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2);
     };
 
-    private final TaskStackConsumer mNotifyActivityDismissingDockedStack = (l, m) -> {
-        l.onActivityDismissingDockedStack();
+    private final TaskStackConsumer mNotifyActivityDismissingDockedTask = (l, m) -> {
+        l.onActivityDismissingDockedTask();
     };
 
     private final TaskStackConsumer mNotifyActivityLaunchOnSecondaryDisplayFailed = (l, m) -> {
@@ -235,7 +235,7 @@
                     forAllRemoteListeners(mNotifyActivityForcedResizable, msg);
                     break;
                 case NOTIFY_ACTIVITY_DISMISSING_DOCKED_ROOT_TASK_MSG:
-                    forAllRemoteListeners(mNotifyActivityDismissingDockedStack, msg);
+                    forAllRemoteListeners(mNotifyActivityDismissingDockedTask, msg);
                     break;
                 case NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG:
                     forAllRemoteListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg);
@@ -391,7 +391,7 @@
     void notifyActivityDismissingDockedRootTask() {
         mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_ROOT_TASK_MSG);
         final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_ROOT_TASK_MSG);
-        forAllLocalListeners(mNotifyActivityDismissingDockedStack, msg);
+        forAllLocalListeners(mNotifyActivityDismissingDockedTask, msg);
         msg.sendToTarget();
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index c0bce6b..1531e56 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -596,9 +596,7 @@
         try {
             synchronized (mGlobalLock) {
                 final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
-                if (wc == null) {
-                    throw new IllegalArgumentException("Can't resolve window from token");
-                }
+                if (wc == null) return false;
                 final Task task = wc.asTask();
                 if (task == null) return false;
                 if (!task.mCreatedByOrganizer) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 89d3040..c0ccd81 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -230,7 +230,7 @@
 import android.view.IDisplayWindowRotationController;
 import android.view.IInputFilter;
 import android.view.IOnKeyguardExitResult;
-import android.view.IPinnedStackListener;
+import android.view.IPinnedTaskListener;
 import android.view.IRecentsAnimationRunner;
 import android.view.IRotationWatcher;
 import android.view.IScrollCaptureCallbacks;
@@ -253,6 +253,7 @@
 import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.view.RemoteAnimationAdapter;
+import android.view.ScrollCaptureResponse;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -2393,15 +2394,9 @@
                 }
             }
 
-            // We may be deferring layout passes at the moment, but since the client is interested
-            // in the new out values right now we need to force a layout.
-            mWindowPlacerLocked.performSurfacePlacement(true /* force */);
-
+            // Create surfaceControl before surface placement otherwise layout will be skipped
+            // (because WS.isGoneForLayout() is true when there is no surface.
             if (shouldRelayout) {
-                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
-
-                result = win.relayoutVisibleWindow(result);
-
                 try {
                     result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                 } catch (Exception e) {
@@ -2413,6 +2408,17 @@
                     Binder.restoreCallingIdentity(origId);
                     return 0;
                 }
+            }
+
+            // We may be deferring layout passes at the moment, but since the client is interested
+            // in the new out values right now we need to force a layout.
+            mWindowPlacerLocked.performSurfacePlacement(true /* force */);
+
+            if (shouldRelayout) {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
+
+                result = win.relayoutVisibleWindow(result);
+
                 if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                     focusMayChange = true;
                 }
@@ -2981,7 +2987,7 @@
     }
 
     boolean isValidPictureInPictureAspectRatio(DisplayContent displayContent, float aspectRatio) {
-        return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio(
+        return displayContent.getPinnedTaskController().isValidPictureInPictureAspectRatio(
                 aspectRatio);
     }
 
@@ -6880,7 +6886,7 @@
     }
 
     @Override
-    public void setDockedStackDividerTouchRegion(Rect touchRegion) {
+    public void setDockedTaskDividerTouchRegion(Rect touchRegion) {
         synchronized (mGlobalLock) {
             final DisplayContent dc = getDefaultDisplayContentLocked();
             dc.getDockedDividerController().setTouchRegion(touchRegion);
@@ -6905,10 +6911,9 @@
         return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
     }
 
-    @Override
-    public void registerPinnedStackListener(int displayId, IPinnedStackListener listener) {
+    public void registerPinnedTaskListener(int displayId, IPinnedTaskListener listener) {
         if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
-                "registerPinnedStackListener()")) {
+                "registerPinnedTaskListener()")) {
             return;
         }
         if (!mAtmService.mSupportsPictureInPicture) {
@@ -6916,7 +6921,7 @@
         }
         synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-            displayContent.getPinnedStackController().registerPinnedStackListener(listener);
+            displayContent.getPinnedTaskController().registerPinnedTaskListener(listener);
         }
     }
 
@@ -7155,12 +7160,14 @@
         }
         final long token = Binder.clearCallingIdentity();
         try {
+            ScrollCaptureResponse.Builder responseBuilder = new ScrollCaptureResponse.Builder();
             synchronized (mGlobalLock) {
                 DisplayContent dc = mRoot.getDisplayContent(displayId);
                 if (dc == null) {
                     ProtoLog.e(WM_ERROR,
                             "Invalid displayId for requestScrollCapture: %d", displayId);
-                    callbacks.onUnavailable();
+                    responseBuilder.setDescription(String.format("bad displayId: %d", displayId));
+                    callbacks.onScrollCaptureResponse(responseBuilder.build());
                     return;
                 }
                 WindowState topWindow = null;
@@ -7169,17 +7176,20 @@
                 }
                 WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId);
                 if (targetWindow == null) {
-                    callbacks.onUnavailable();
+                    responseBuilder.setDescription("findScrollCaptureTargetWindow returned null");
+                    callbacks.onScrollCaptureResponse(responseBuilder.build());
                     return;
                 }
-                // Forward to the window for handling.
                 try {
+                    // Forward to the window for handling, which will respond using the callback.
                     targetWindow.mClient.requestScrollCapture(callbacks);
                 } catch (RemoteException e) {
                     ProtoLog.w(WM_ERROR,
                             "requestScrollCapture: caught exception dispatching to window."
                                     + "token=%s", targetWindow.mClient.asBinder());
-                    callbacks.onUnavailable();
+                    responseBuilder.setWindowTitle(targetWindow.getName());
+                    responseBuilder.setDescription(String.format("caught exception: %s", e));
+                    callbacks.onScrollCaptureResponse(responseBuilder.build());
                 }
             }
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 63a0832..9973664 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static android.Manifest.permission.READ_FRAME_BUFFER;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
@@ -426,8 +425,9 @@
         }
 
         if (windowingMode > -1) {
-            if (mService.isInLockTaskMode() && windowingMode != WINDOWING_MODE_FULLSCREEN) {
-                throw new UnsupportedOperationException("Not supported to set non-fullscreen"
+            if (mService.isInLockTaskMode()
+                    && WindowConfiguration.inMultiWindowMode(windowingMode)) {
+                throw new UnsupportedOperationException("Not supported to set multi-window"
                         + " windowing mode during locked task mode.");
             }
             container.setWindowingMode(windowingMode);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 60e95e5..9ae5beb 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1224,7 +1224,7 @@
                     // But in docked we want to behave like fullscreen and behave as if the task
                     // were given smaller bounds for the purposes of layout. Skip adjustments for
                     // the root pinned task, they are handled separately in the
-                    // PinnedStackController.
+                    // PinnedTaskController.
                     windowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom;
                 }
             }
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index f054e7c..8c6d084 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -232,21 +232,6 @@
     compactProcessOrFallback(pid, compactionFlags);
 }
 
-static void com_android_server_am_CachedAppOptimizer_enableFreezerInternal(
-        JNIEnv *env, jobject clazz, jboolean enable) {
-    bool success = true;
-
-    if (enable) {
-        success = SetTaskProfiles(0, {"FreezerEnabled"}, true);
-    } else {
-        success = SetTaskProfiles(0, {"FreezerDisabled"}, true);
-    }
-
-    if (!success) {
-        jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
-    }
-}
-
 static void com_android_server_am_CachedAppOptimizer_freezeBinder(
         JNIEnv *env, jobject clazz, jint pid, jboolean freeze) {
 
@@ -280,15 +265,19 @@
 
 static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIEnv* env,
                                                                             jobject clazz) {
-    return env->NewStringUTF(CGROUP_FREEZE_PATH);
+    std::string path;
+
+    if (!getAttributePathForTask("FreezerState", getpid(), &path)) {
+        path = "";
+    }
+
+    return env->NewStringUTF(path.c_str());
 }
 
 static const JNINativeMethod sMethods[] = {
         /* name, signature, funcPtr */
         {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
         {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
-        {"enableFreezerInternal", "(Z)V",
-         (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
         {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
         {"getBinderFreezeInfo", "(I)I",
          (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo},
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 21d57d8..10705af 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -331,7 +331,7 @@
                                           uint32_t policyFlags) override;
     bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
                               uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
-    void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override;
+    void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override;
     bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override;
     void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
     void setPointerCapture(bool enabled) override;
@@ -1325,9 +1325,9 @@
     return result;
 }
 
-void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
+void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) {
     ATRACE_CALL();
-    android_server_PowerManagerService_userActivity(eventTime, eventType);
+    android_server_PowerManagerService_userActivity(eventTime, eventType, displayId);
 }
 
 bool NativeInputManager::checkInjectEventsPermissionNonReentrant(
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 63a6eed..9b7e27d 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -103,7 +103,8 @@
     return result == power::HalResult::SUCCESSFUL;
 }
 
-void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
+void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType,
+                                                     int32_t displayId) {
     if (gPowerManagerServiceObj) {
         // Throttle calls into user activity by event type.
         // We're a little conservative about argument checking here in case the caller
@@ -127,7 +128,7 @@
 
         env->CallVoidMethod(gPowerManagerServiceObj,
                 gPowerManagerServiceClassInfo.userActivityFromNative,
-                nanoseconds_to_milliseconds(eventTime), eventType, 0);
+                nanoseconds_to_milliseconds(eventTime), eventType, displayId, 0);
         checkAndClearExceptionFromCallback(env, "userActivityFromNative");
     }
 }
@@ -285,7 +286,7 @@
     FIND_CLASS(clazz, "com/android/server/power/PowerManagerService");
 
     GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz,
-            "userActivityFromNative", "(JII)V");
+            "userActivityFromNative", "(JIII)V");
 
     // Initialize
     for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) {
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.h b/services/core/jni/com_android_server_power_PowerManagerService.h
index a17fd65..a2f335c 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.h
+++ b/services/core/jni/com_android_server_power_PowerManagerService.h
@@ -24,7 +24,8 @@
 
 namespace android {
 
-extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType);
+extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType,
+                                                            int32_t displayId);
 
 } // namespace android
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index b52347f..ef7afc8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -129,6 +129,15 @@
             FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
     }
 
+    @Override
+    public void setDeviceOwnerType(@NonNull ComponentName admin, int deviceOwnerType) {
+    }
+
+    @Override
+    public int getDeviceOwnerType(@NonNull ComponentName admin) {
+        return 0;
+    }
+
     public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {}
 
     public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7260732..524892b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -156,6 +156,7 @@
 import android.app.admin.DevicePolicyCache;
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.DeviceOwnerType;
 import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
 import android.app.admin.DevicePolicyManager.OperationSafetyReason;
 import android.app.admin.DevicePolicyManager.PasswordComplexity;
@@ -333,6 +334,7 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -340,6 +342,9 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.reflect.Constructor;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
 import java.text.DateFormat;
 import java.time.LocalDate;
 import java.util.ArrayList;
@@ -3399,8 +3404,10 @@
             return;
         }
         Objects.requireNonNull(adminReceiver, "ComponentName is null");
-        Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
-                "Non-shell user attempted to call forceRemoveActiveAdmin");
+        Preconditions.checkCallAuthorization(isAdb(getCallerIdentity())
+                        || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS),
+                "Caller must be shell or hold MANAGE_PROFILE_AND_DEVICE_OWNERS to call "
+                        + "forceRemoveActiveAdmin");
         mInjector.binderWithCleanCallingIdentity(() -> {
             synchronized (getLockObject()) {
                 if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) {
@@ -5622,7 +5629,7 @@
         // Get attestation flags, if any.
         final int[] attestationUtilsFlags = translateIdAttestationFlags(idAttestationFlags);
         final boolean deviceIdAttestationRequired = attestationUtilsFlags != null;
-        final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
+        KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
         final String alias = keySpec.getKeystoreAlias();
 
         Preconditions.checkStringNotEmpty(alias, "Empty alias provided");
@@ -5643,6 +5650,9 @@
                     || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
         }
 
+        if (TextUtils.isEmpty(alias)) {
+            throw new IllegalArgumentException("Empty alias provided.");
+        }
         // As the caller will be granted access to the key, ensure no UID was specified, as
         // it will not have the desired effect.
         if (keySpec.getUid() != KeyStore.UID_SELF) {
@@ -5651,19 +5661,26 @@
             return false;
         }
 
+        if (deviceIdAttestationRequired) {
+            if (keySpec.getAttestationChallenge() == null) {
+                throw new IllegalArgumentException(
+                        "Requested Device ID attestation but challenge is empty.");
+            }
+            KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(keySpec);
+            specBuilder.setAttestationIds(attestationUtilsFlags);
+            specBuilder.setDevicePropertiesAttestationIncluded(true);
+            keySpec = specBuilder.build();
+        }
+
+        final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
         final long id = mInjector.binderClearCallingIdentity();
         try {
             try (KeyChainConnection keyChainConnection =
-                    KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
+                    KeyChain.bindAsUser(mContext, userHandle)) {
                 IKeyChainService keyChain = keyChainConnection.getService();
 
-                // Copy the provided keySpec, excluding the attestation challenge, which will be
-                // used later for requesting key attestation record.
-                final KeyGenParameterSpec noAttestationSpec = new KeyGenParameterSpec.Builder(
-                        keySpec).setAttestationChallenge(null).build();
-
                 final int generationResult = keyChain.generateKeyPair(algorithm,
-                    new ParcelableKeyGenParameterSpec(noAttestationSpec));
+                        new ParcelableKeyGenParameterSpec(keySpec));
                 if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
                     Log.e(LOG_TAG, String.format(
                             "KeyChain failed to generate a keypair, error %d.", generationResult));
@@ -5672,6 +5689,9 @@
                             throw new ServiceSpecificException(
                                     DevicePolicyManager.KEY_GEN_STRONGBOX_UNAVAILABLE,
                                     String.format("KeyChain error: %d", generationResult));
+                        case KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS:
+                            throw new UnsupportedOperationException(
+                                "Device does not support Device ID attestation.");
                         default:
                             logGenerateKeyPairFailure(caller, isCredentialManagementApp);
                             return false;
@@ -5685,23 +5705,27 @@
                 // that UID.
                 keyChain.setGrant(caller.getUid(), alias, true);
 
-                final byte[] attestationChallenge = keySpec.getAttestationChallenge();
-                if (attestationChallenge != null) {
-                    final int attestationResult = keyChain.attestKey(
-                            alias, attestationChallenge, attestationUtilsFlags, attestationChain);
-                    if (attestationResult != KeyChain.KEY_ATTESTATION_SUCCESS) {
-                        Log.e(LOG_TAG, String.format(
-                                "Attestation for %s failed (rc=%d), deleting key.",
-                                alias, attestationResult));
-                        keyChain.removeKeyPair(alias);
-                        if (attestationResult == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) {
-                            throw new UnsupportedOperationException(
-                                    "Device does not support Device ID attestation.");
+                try {
+                    final List<byte[]> encodedCerts = new ArrayList();
+                    final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+                    final byte[] certChainBytes = keyChain.getCaCertificates(alias);
+                    encodedCerts.add(keyChain.getCertificate(alias));
+                    if (certChainBytes != null) {
+                        final Collection<X509Certificate> certs =
+                                (Collection<X509Certificate>) certFactory.generateCertificates(
+                                    new ByteArrayInputStream(certChainBytes));
+                        for (X509Certificate cert : certs) {
+                            encodedCerts.add(cert.getEncoded());
                         }
-                        logGenerateKeyPairFailure(caller, isCredentialManagementApp);
-                        return false;
                     }
+
+                    attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts));
+                } catch (CertificateException e) {
+                    logGenerateKeyPairFailure(caller, isCredentialManagementApp);
+                    Log.e(LOG_TAG, "While retrieving certificate chain.", e);
+                    return false;
                 }
+
                 DevicePolicyEventLogger
                         .createEvent(DevicePolicyEnums.GENERATE_KEY_PAIR)
                         .setAdmin(caller.getPackageName())
@@ -7560,8 +7584,7 @@
     }
 
     private void sendProfileOwnerCommand(String action, Bundle extras, @UserIdInt int userId) {
-        sendActiveAdminCommand(action, extras, userId,
-                mOwners.getProfileOwnerComponent(userId));
+        sendActiveAdminCommand(action, extras, userId, mOwners.getProfileOwnerComponent(userId));
     }
 
     private void sendActiveAdminCommand(String action, Bundle extras,
@@ -8089,7 +8112,8 @@
             return null;
         }
         if (!callingUserOnly) {
-            Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+            Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+                    || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
         }
         synchronized (getLockObject()) {
             if (!mOwners.hasDeviceOwner()) {
@@ -12351,8 +12375,10 @@
             extras.putInt(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_REASON, reason);
             extras.putBoolean(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_STATE, isSafe);
 
-            sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
-                    extras);
+            if (mOwners.hasDeviceOwner()) {
+                sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
+                        extras);
+            }
             for (int profileOwnerId : mOwners.getProfileOwnerKeys()) {
                 sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
                         extras, profileOwnerId);
@@ -12543,8 +12569,10 @@
 
     @Override
     public void clearSystemUpdatePolicyFreezePeriodRecord() {
-        Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
-                "Non-shell user attempted to call clearSystemUpdatePolicyFreezePeriodRecord");
+        Preconditions.checkCallAuthorization(isAdb(getCallerIdentity())
+                        || hasCallingOrSelfPermission(permission.CLEAR_FREEZE_PERIOD),
+                "Caller must be shell, or hold CLEAR_FREEZE_PERIOD permission to call "
+                        + "clearSystemUpdatePolicyFreezePeriodRecord");
         synchronized (getLockObject()) {
             // Print out current record to help diagnosed CTS failures
             Slog.i(LOG_TAG, "Clear freeze period record: "
@@ -13487,7 +13515,8 @@
         final CallerIdentity caller = getCallerIdentity();
         // Only adb or system apps with the right permission can mark a profile owner on
         // organization-owned device.
-        if (!(isAdb(caller) || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) {
+        if (!(isAdb(caller) || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED)
+                || hasCallingPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS))) {
             throw new SecurityException(
                     "Only the system can mark a profile owner of organization-owned device.");
         }
@@ -13806,8 +13835,10 @@
 
     @Override
     public long forceSecurityLogs() {
-        Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
-                "Non-shell user attempted to call forceSecurityLogs");
+        Preconditions.checkCallAuthorization(isAdb(getCallerIdentity())
+                        || hasCallingOrSelfPermission(permission.FORCE_DEVICE_POLICY_MANAGER_LOGS),
+                "Caller must be shell or hold FORCE_DEVICE_POLICY_MANAGER_LOGS to call "
+                        + "forceSecurityLogs");
         if (!mInjector.securityLogGetLoggingEnabledProperty()) {
             throw new IllegalStateException("logging is not available");
         }
@@ -14327,8 +14358,10 @@
 
     @Override
     public long forceNetworkLogs() {
-        Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
-                "Non-shell user attempted to call forceNetworkLogs");
+        Preconditions.checkCallAuthorization(isAdb(getCallerIdentity())
+                || hasCallingOrSelfPermission(permission.FORCE_DEVICE_POLICY_MANAGER_LOGS),
+                "Caller must be shell or hold FORCE_DEVICE_POLICY_MANAGER_LOGS to call "
+                        + "forceNetworkLogs");
         synchronized (getLockObject()) {
             if (!isNetworkLoggingEnabledInternalLocked()) {
                 throw new IllegalStateException("logging is not available");
@@ -16731,6 +16764,37 @@
     }
 
     @Override
+    public void setDeviceOwnerType(@NonNull ComponentName admin,
+            @DeviceOwnerType int deviceOwnerType) {
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+                permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+        verifyDeviceOwnerTypePreconditions(admin);
+
+        final String packageName = admin.getPackageName();
+        Preconditions.checkState(!mOwners.isDeviceOwnerTypeSetForDeviceOwner(packageName),
+                "The device owner type has already been set for " + packageName);
+
+        synchronized (getLockObject()) {
+            mOwners.setDeviceOwnerType(packageName, deviceOwnerType);
+        }
+    }
+
+    @Override
+    @DeviceOwnerType
+    public int getDeviceOwnerType(@NonNull ComponentName admin) {
+        verifyDeviceOwnerTypePreconditions(admin);
+        synchronized (getLockObject()) {
+            return mOwners.getDeviceOwnerType(admin.getPackageName());
+        }
+    }
+
+    private void verifyDeviceOwnerTypePreconditions(@NonNull ComponentName admin) {
+        Preconditions.checkState(mOwners.hasDeviceOwner(), "there is no device owner");
+        Preconditions.checkState(mOwners.getDeviceOwnerComponent().equals(admin),
+                "admin is not the device owner");
+    }
+
+    @Override
     public void setUsbDataSignalingEnabled(String packageName, boolean enabled) {
         Objects.requireNonNull(packageName, "Admin package name must be provided");
         final CallerIdentity caller = getCallerIdentity(packageName);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
index 776b444..257fc64 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
@@ -23,6 +23,8 @@
 import android.app.admin.DevicePolicyManager.OperationSafetyReason;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.DevicePolicySafetyChecker;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Slog;
 
 import com.android.internal.os.IResultReceiver;
@@ -42,10 +44,15 @@
 
     private static final String TAG = OneTimeSafetyChecker.class.getSimpleName();
 
+    private static final long SELF_DESTRUCT_TIMEOUT_MS = 10_000;
+
     private final DevicePolicyManagerService mService;
     private final DevicePolicySafetyChecker mRealSafetyChecker;
     private final @DevicePolicyOperation int mOperation;
     private final @OperationSafetyReason int mReason;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    private boolean mDone;
 
     OneTimeSafetyChecker(DevicePolicyManagerService service,
             @DevicePolicyOperation int operation, @OperationSafetyReason int reason) {
@@ -53,7 +60,11 @@
         mOperation = operation;
         mReason = reason;
         mRealSafetyChecker = service.getDevicePolicySafetyChecker();
-        Slog.i(TAG, "Saving real DevicePolicySafetyChecker as " + mRealSafetyChecker);
+        Slog.i(TAG, "OneTimeSafetyChecker constructor: operation= " + operationToString(operation)
+                + ", reason=" + operationSafetyReasonToString(reason)
+                + ", realChecker=" + mRealSafetyChecker
+                + ", maxDuration=" + SELF_DESTRUCT_TIMEOUT_MS + "ms");
+        mHandler.postDelayed(() -> selfDestruct(), SELF_DESTRUCT_TIMEOUT_MS);
     }
 
     @Override
@@ -99,8 +110,21 @@
     }
 
     private void disableSelf() {
+        if (mDone) {
+            Slog.w(TAG, "disableSelf(): already disabled");
+            return;
+        }
         Slog.i(TAG, "restoring DevicePolicySafetyChecker to " + mRealSafetyChecker);
         mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker);
+        mDone = true;
+    }
+
+    private void selfDestruct() {
+        if (mDone) return;
+
+        // Usually happens when a CTS failed before calling the DPM method that would clear it
+        Slog.e(TAG, "Self destructing " + this + ", as it was not automatically disabled");
+        disableSelf();
     }
 
     @Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 1e70d59..7fdd6ee 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -16,10 +16,13 @@
 
 package com.android.server.devicepolicy;
 
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManagerInternal;
+import android.app.admin.DevicePolicyManager.DeviceOwnerType;
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
 import android.content.ComponentName;
@@ -93,6 +96,7 @@
     private static final String TAG_PROFILE_OWNER = "profile-owner";
     // Holds "context" for device-owner, this must not be show up before device-owner.
     private static final String TAG_DEVICE_OWNER_CONTEXT = "device-owner-context";
+    private static final String TAG_DEVICE_OWNER_TYPE = "device-owner-type";
 
     private static final String ATTR_NAME = "name";
     private static final String ATTR_PACKAGE = "package";
@@ -109,6 +113,7 @@
     // New attribute for profile owner of organization-owned device.
     private static final String ATTR_PROFILE_OWNER_OF_ORG_OWNED_DEVICE =
             "isPoOrganizationOwnedDevice";
+    private static final String ATTR_DEVICE_OWNER_TYPE_VALUE = "value";
 
     private final UserManager mUserManager;
     private final UserManagerInternal mUserManagerInternal;
@@ -121,6 +126,9 @@
     // Internal state for the device owner package.
     private OwnerInfo mDeviceOwner;
 
+    // Device owner type for a managed device.
+    private final ArrayMap<String, Integer> mDeviceOwnerTypes = new ArrayMap<>();
+
     private int mDeviceOwnerUserId = UserHandle.USER_NULL;
 
     // Internal state for the profile owner packages.
@@ -334,6 +342,7 @@
 
     void clearDeviceOwner() {
         synchronized (mLock) {
+            mDeviceOwnerTypes.remove(mDeviceOwner.packageName);
             mDeviceOwner = null;
             mDeviceOwnerUserId = UserHandle.USER_NULL;
 
@@ -384,12 +393,16 @@
 
     void transferDeviceOwnership(ComponentName target) {
         synchronized (mLock) {
+            Integer previousDeviceOwnerType = mDeviceOwnerTypes.remove(mDeviceOwner.packageName);
             // We don't set a name because it's not used anyway.
             // See DevicePolicyManagerService#getDeviceOwnerName
             mDeviceOwner = new OwnerInfo(null, target,
                     mDeviceOwner.userRestrictionsMigrated, mDeviceOwner.remoteBugreportUri,
                     mDeviceOwner.remoteBugreportHash, /* isOrganizationOwnedDevice =*/
                     mDeviceOwner.isOrganizationOwnedDevice);
+            if (previousDeviceOwnerType != null) {
+                mDeviceOwnerTypes.put(mDeviceOwner.packageName, previousDeviceOwnerType);
+            }
             pushToPackageManagerLocked();
             pushToActivityTaskManagerLocked();
             pushToActivityManagerLocked();
@@ -596,6 +609,37 @@
         }
     }
 
+    void setDeviceOwnerType(String packageName, @DeviceOwnerType int deviceOwnerType) {
+        synchronized (mLock) {
+            if (!hasDeviceOwner()) {
+                Slog.e(TAG, "Attempting to set a device owner type when there is no device owner");
+                return;
+            } else if (isDeviceOwnerTypeSetForDeviceOwner(packageName)) {
+                Slog.e(TAG, "Device owner type for " + packageName + " has already been set");
+                return;
+            }
+
+            mDeviceOwnerTypes.put(packageName, deviceOwnerType);
+            writeDeviceOwner();
+        }
+    }
+
+    @DeviceOwnerType
+    int getDeviceOwnerType(String packageName) {
+        synchronized (mLock) {
+            if (isDeviceOwnerTypeSetForDeviceOwner(packageName)) {
+                return mDeviceOwnerTypes.get(packageName);
+            }
+            return DEVICE_OWNER_TYPE_DEFAULT;
+        }
+    }
+
+    boolean isDeviceOwnerTypeSetForDeviceOwner(String packageName) {
+        synchronized (mLock) {
+            return !mDeviceOwnerTypes.isEmpty() && mDeviceOwnerTypes.containsKey(packageName);
+        }
+    }
+
     private boolean readLegacyOwnerFileLocked(File file) {
         if (!file.exists()) {
             // Already migrated or the device has no owners.
@@ -880,6 +924,16 @@
                 out.startTag(null, TAG_DEVICE_OWNER_CONTEXT);
                 out.attributeInt(null, ATTR_USERID, mDeviceOwnerUserId);
                 out.endTag(null, TAG_DEVICE_OWNER_CONTEXT);
+
+            }
+
+            if (!mDeviceOwnerTypes.isEmpty()) {
+                for (ArrayMap.Entry<String, Integer> entry : mDeviceOwnerTypes.entrySet()) {
+                    out.startTag(null, TAG_DEVICE_OWNER_TYPE);
+                    out.attribute(null, ATTR_PACKAGE, entry.getKey());
+                    out.attributeInt(null, ATTR_DEVICE_OWNER_TYPE_VALUE, entry.getValue());
+                    out.endTag(null, TAG_DEVICE_OWNER_TYPE);
+                }
             }
 
             if (mSystemUpdatePolicy != null) {
@@ -942,6 +996,12 @@
                         }
                     }
                     break;
+                case TAG_DEVICE_OWNER_TYPE:
+                    String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+                    int deviceOwnerType = parser.getAttributeInt(null, ATTR_DEVICE_OWNER_TYPE_VALUE,
+                            DEVICE_OWNER_TYPE_DEFAULT);
+                    mDeviceOwnerTypes.put(packageName, deviceOwnerType);
+                    break;
                 default:
                     Slog.e(TAG, "Unexpected tag: " + tag);
                     return false;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d50db91a..a3d3353 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -162,6 +162,7 @@
 import com.android.server.pm.UserManagerService;
 import com.android.server.pm.dex.SystemServerDexLoadReporter;
 import com.android.server.pm.verify.domain.DomainVerificationService;
+import com.android.server.policy.AppOpsPolicy;
 import com.android.server.policy.PermissionPolicyService;
 import com.android.server.policy.PhoneWindowManager;
 import com.android.server.policy.role.RoleServicePlatformHelperImpl;
@@ -1717,14 +1718,9 @@
             startTextToSpeechManagerService(context, t);
 
             // System Speech Recognition Service
-            if (deviceHasConfigString(context,
-                    R.string.config_defaultOnDeviceSpeechRecognitionService)) {
-                t.traceBegin("StartSpeechRecognitionManagerService");
-                mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS);
-                t.traceEnd();
-            } else {
-                Slog.d(TAG, "System speech recognition is not defined by OEM");
-            }
+            t.traceBegin("StartSpeechRecognitionManagerService");
+            mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS);
+            t.traceEnd();
 
             // App prediction manager service
             if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) {
@@ -2661,6 +2657,14 @@
             }
             t.traceEnd();
 
+            t.traceBegin("RegisterAppOpsPolicy");
+            try {
+                mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy());
+            } catch (Throwable e) {
+                reportWtf("registering app ops policy", e);
+            }
+            t.traceEnd();
+
             // No dependency on Webview preparation in system server. But this should
             // be completed before allowing 3rd party
             final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation";
diff --git a/services/musicrecognition/Android.bp b/services/musicrecognition/Android.bp
index fea9efa..8298dec 100644
--- a/services/musicrecognition/Android.bp
+++ b/services/musicrecognition/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 filegroup {
     name: "services.musicsearch-sources",
     srcs: ["java/**/*.java"],
@@ -10,4 +19,4 @@
     defaults: ["platform_service_defaults"],
     srcs: [":services.musicsearch-sources"],
     libs: ["services.core", "app-compat-annotations"],
-}
\ No newline at end of file
+}
diff --git a/services/net/Android.bp b/services/net/Android.bp
index c68004a..b01e425 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -52,6 +52,7 @@
     libs: [
         "unsupportedappusage",
         "framework-wifi-util-lib",
+        "framework-connectivity"
     ],
     static_libs: [
         // All the classes in netd_aidl_interface must be jarjar so they do not conflict with the
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index cbebe69..2219d47 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -38,7 +38,6 @@
 
 import android.annotation.UserIdInt;
 import android.app.Application;
-import android.app.backup.BackupManager.OperationType;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
 import android.app.backup.IFullBackupRestoreObserver;
@@ -874,8 +873,7 @@
                 SecurityException.class,
                 () ->
                         backupManagerService.requestBackup(
-                                mUserTwoId, packages, observer, monitor, 0,
-                                OperationType.BACKUP));
+                                mUserTwoId, packages, observer, monitor, 0));
     }
 
     /**
@@ -893,11 +891,9 @@
         IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
 
-        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0,
-                OperationType.BACKUP);
+        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
 
-        verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0,
-                OperationType.BACKUP);
+        verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
@@ -910,11 +906,9 @@
         IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0,
-                OperationType.BACKUP);
+        backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0);
 
-        verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0,
-                OperationType.BACKUP);
+        verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
@@ -927,11 +921,9 @@
         IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
-        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0,
-                OperationType.BACKUP);
+        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
 
-        verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0,
-                OperationType.BACKUP);
+        verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0);
     }
 
     /**
@@ -1092,11 +1084,9 @@
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT,
-                OperationType.BACKUP);
+        backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT);
 
-        verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT,
-                OperationType.BACKUP);
+        verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
     }
 
     /** Test that the backup service does not route methods for non-registered users. */
@@ -1106,11 +1096,9 @@
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
-        backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT,
-                OperationType.BACKUP);
+        backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT);
 
-        verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT,
-                OperationType.BACKUP);
+        verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
diff --git a/services/searchui/Android.bp b/services/searchui/Android.bp
index cc63294..3081a51 100644
--- a/services/searchui/Android.bp
+++ b/services/searchui/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 filegroup {
     name: "services.searchui-sources",
     srcs: ["java/**/*.java"],
diff --git a/services/smartspace/Android.bp b/services/smartspace/Android.bp
index fcf780d..640a88d 100644
--- a/services/smartspace/Android.bp
+++ b/services/smartspace/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 filegroup {
     name: "services.smartspace-sources",
     srcs: ["java/**/*.java"],
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
index b7a0624..e70a734 100644
--- a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 java_library {
     name: "PackageManagerServiceHostTestsIntentVerifyUtils",
     srcs: ["src/**/*.kt"],
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
index af0ac77..7e4f0e7 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test_helper_app {
     name: "PackageManagerServiceDeviceSideTests",
     sdk_version: "test_current",
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
index e82f57d..4f3f2eb 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test_helper_app {
     name: "PackageManagerTestIntentVerifier",
     srcs: [ "src/**/*.kt" ],
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
index 7161fdd..9f9ed24 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test_helper_app {
     name: "PackageManagerTestIntentVerifierTarget1",
     manifest: "AndroidManifest1.xml",
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
index 58f17f2..ebad5af 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test_helper_app {
     name: "PackageManagerTestAppDeclaresStaticLibrary",
     manifest: "AndroidManifestDeclaresStaticLibrary.xml",
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
index 4aa8abc..334e53a 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "PackageManagerServiceUnitTests",
     srcs: ["src/**/*.kt"],
diff --git a/services/tests/inprocesstests/Android.bp b/services/tests/inprocesstests/Android.bp
index 6dd059f..7c237ac 100644
--- a/services/tests/inprocesstests/Android.bp
+++ b/services/tests/inprocesstests/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "FrameworksInProcessTests",
     srcs: ["src/**/*.java"],
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index 928065a..a32bf2c 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 cc_library_shared {
     name: "libactivitymanagermockingservicestestjni",
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index a382e85..1c45203 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -46,6 +46,7 @@
 import static com.android.server.am.ProcessList.HOME_APP_ADJ;
 import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ;
 import static com.android.server.am.ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
+import static com.android.server.am.ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ;
 import static com.android.server.am.ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
 import static com.android.server.am.ProcessList.PERSISTENT_PROC_ADJ;
 import static com.android.server.am.ProcessList.PERSISTENT_SERVICE_ADJ;
@@ -877,6 +878,39 @@
 
     @SuppressWarnings("GuardedBy")
     @Test
+    public void testUpdateOomAdj_DoOne_Service_MediumPerceptible() {
+        {
+            ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                    MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+            ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                    MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+            bindService(app, client, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
+            client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
+
+            assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ, app.mState.getSetAdj());
+        }
+
+        {
+            ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                    MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+            ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                    MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+            WindowProcessController wpc = client.getWindowProcessController();
+            doReturn(true).when(wpc).isHeavyWeightProcess();
+            bindService(app, client, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
+            client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
+            doReturn(false).when(wpc).isHeavyWeightProcess();
+
+            assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ, app.mState.getSetAdj());
+        }
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
     public void testUpdateOomAdj_DoOne_Service_Other() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 728b97c..18184b0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -27,11 +27,14 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -44,6 +47,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.internal.R;
 import com.android.server.LocalServices;
 import com.android.server.display.LocalDisplayAdapter.BacklightAdapter;
 import com.android.server.lights.LightsManager;
@@ -98,6 +102,9 @@
 
     @Mock
     private LocalDisplayAdapter.SurfaceControlProxy mSurfaceControlProxy;
+    private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
+    private static final int[] BACKLIGHT_RANGE = { 1, 255 };
+    private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f };
 
     @Before
     public void setUp() throws Exception {
@@ -114,6 +121,18 @@
                 mListener, mInjector);
         spyOn(mAdapter);
         doReturn(mMockedContext).when(mAdapter).getOverlayContext();
+
+        TypedArray mockNitsRange = createFloatTypedArray(DISPLAY_RANGE_NITS);
+        when(mMockedResources.obtainTypedArray(R.array.config_screenBrightnessNits))
+                .thenReturn(mockNitsRange);
+        when(mMockedResources.getIntArray(R.array.config_screenBrightnessBacklight))
+                .thenReturn(BACKLIGHT_RANGE);
+        when(mMockedResources.getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessSettingMinimumFloat))
+                .thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[0]);
+        when(mMockedResources.getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessSettingMaximumFloat))
+                .thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[1]);
     }
 
     @After
@@ -629,13 +648,13 @@
         // Test as default display
         BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/,
                 mSurfaceControlProxy);
-        ba.setBrightness(0.514f);
+        ba.setBacklight(0.514f);
         verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.514f);
 
         // Test as not default display
         BacklightAdapter ba2 = new BacklightAdapter(displayToken, false /*isDefault*/,
                 mSurfaceControlProxy);
-        ba2.setBrightness(0.323f);
+        ba2.setBacklight(0.323f);
         verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.323f);
     }
 
@@ -648,7 +667,7 @@
 
         BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/,
                 mSurfaceControlProxy);
-        ba.setBrightness(0.123f);
+        ba.setBacklight(0.123f);
         verify(mMockedBacklight).setBrightness(0.123f);
     }
 
@@ -661,7 +680,7 @@
 
         BacklightAdapter ba = new BacklightAdapter(displayToken, false /*isDefault*/,
                 mSurfaceControlProxy);
-        ba.setBrightness(0.456f);
+        ba.setBacklight(0.456f);
 
         // Adapter does not forward any brightness in this case.
         verify(mMockedBacklight, never()).setBrightness(anyFloat());
@@ -864,4 +883,23 @@
         }
     }
 
+    private TypedArray createFloatTypedArray(float[] vals) {
+        TypedArray mockArray = mock(TypedArray.class);
+        when(mockArray.length()).thenAnswer(invocation -> {
+            return vals.length;
+        });
+        when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> {
+            final float def = (float) invocation.getArguments()[1];
+            if (vals == null) {
+                return def;
+            }
+            int idx = (int) invocation.getArguments()[0];
+            if (idx >= 0 && idx < vals.length) {
+                return vals[idx];
+            } else {
+                return def;
+            }
+        });
+        return mockArray;
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 88a691b..bf95f4c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -31,6 +31,7 @@
 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
 import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.JobSchedulerService.sSystemClock;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -58,6 +59,7 @@
 import android.app.IActivityManager;
 import android.app.IUidObserver;
 import android.app.job.JobInfo;
+import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
@@ -85,6 +87,7 @@
 import com.android.server.job.JobStore;
 import com.android.server.job.controllers.QuotaController.ExecutionStats;
 import com.android.server.job.controllers.QuotaController.QcConstants;
+import com.android.server.job.controllers.QuotaController.ShrinkableDebits;
 import com.android.server.job.controllers.QuotaController.TimingSession;
 import com.android.server.usage.AppStandbyInternal;
 
@@ -125,6 +128,7 @@
     private int mSourceUid;
     private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener;
     private IUidObserver mUidObserver;
+    private UsageStatsManagerInternal.UsageEventListener mUsageEventListener;
     DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
 
     private MockitoSession mMockingSession;
@@ -218,6 +222,8 @@
                 ArgumentCaptor.forClass(IUidObserver.class);
         ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor =
                 ArgumentCaptor.forClass(PowerAllowlistInternal.TempAllowlistChangeListener.class);
+        ArgumentCaptor<UsageStatsManagerInternal.UsageEventListener> ueListenerCaptor =
+                ArgumentCaptor.forClass(UsageStatsManagerInternal.UsageEventListener.class);
         mQuotaController = new QuotaController(mJobSchedulerService,
                 mock(BackgroundJobsController.class), mock(ConnectivityController.class));
 
@@ -229,6 +235,8 @@
         verify(mPowerAllowlistInternal)
                 .registerTempAllowlistChangeListener(taChangeCaptor.capture());
         mTempAllowlistListener = taChangeCaptor.getValue();
+        verify(mUsageStatsManager).registerListener(ueListenerCaptor.capture());
+        mUsageEventListener = ueListenerCaptor.getValue();
         try {
             verify(activityManager).registerUidObserver(
                     uidObserverCaptor.capture(),
@@ -288,7 +296,7 @@
                 verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)).delete(eq(uid));
                 assertFalse(foregroundUids.get(uid));
             }
-            waitForQuietBackground();
+            waitForNonDelayedMessagesProcessed();
         } catch (Exception e) {
             fail("exception encountered: " + e.getMessage());
         }
@@ -385,13 +393,8 @@
         }
     }
 
-    private void waitForQuietBackground() throws Exception {
-        for (int i = 0; i < 5; ++i) {
-            if (!mQuotaController.isActiveBackgroundProcessing()) {
-                break;
-            }
-            Thread.sleep(500);
-        }
+    private void waitForNonDelayedMessagesProcessed() {
+        mQuotaController.getHandler().runWithScissors(() -> {}, 15_000);
     }
 
     @Test
@@ -1330,7 +1333,9 @@
                         timeUsedMs, 5), true);
         JobStatus job = createExpeditedJobStatus("testGetMaxJobExecutionTimeLocked_EJ", 0);
         setStandbyBucket(RARE_INDEX, job);
-        mQuotaController.maybeStartTrackingJobLocked(job, null);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStartTrackingJobLocked(job, null);
+        }
 
         setCharging();
         synchronized (mQuotaController.mLock) {
@@ -5158,4 +5163,257 @@
                 eq(10 * SECOND_IN_MILLIS));
         verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs));
     }
+
+    @Test
+    public void testEJDebitTallying() {
+        setStandbyBucket(RARE_INDEX);
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+        // 15 seconds for each 30 second chunk.
+        setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 30 * SECOND_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 15 * SECOND_IN_MILLIS);
+
+        // No history. Debits should be 0.
+        ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(10 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        // Regular job shouldn't affect EJ tally.
+        JobStatus regJob = createJobStatus("testEJDebitTallying", 1);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStartTrackingJobLocked(regJob, null);
+            mQuotaController.prepareForExecutionLocked(regJob);
+        }
+        advanceElapsedClock(5000);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStopTrackingJobLocked(regJob, null, false);
+        }
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(10 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        // EJ job should affect EJ tally.
+        JobStatus eJob = createExpeditedJobStatus("testEJDebitTallying", 2);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStartTrackingJobLocked(eJob, null);
+            mQuotaController.prepareForExecutionLocked(eJob);
+        }
+        advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStopTrackingJobLocked(eJob, null, false);
+        }
+        assertEquals(5 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+        assertEquals(5 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        // Instantaneous event for a different user shouldn't affect tally.
+        advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS);
+
+        UsageEvents.Event event =
+                new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis());
+        event.mPackage = SOURCE_PACKAGE;
+        mUsageEventListener.onUsageEvent(SOURCE_USER_ID + 10, event);
+        assertEquals(5 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+
+        // Instantaneous event for correct user should reduce tally.
+        advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+
+        mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+        waitForNonDelayedMessagesProcessed();
+        assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+        assertEquals(6 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        // Activity start shouldn't reduce tally, but duration with activity started should affect
+        // remaining EJ time.
+        advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+        event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_RESUMED, sSystemClock.millis());
+        event.mPackage = SOURCE_PACKAGE;
+        mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+        waitForNonDelayedMessagesProcessed();
+        advanceElapsedClock(30 * SECOND_IN_MILLIS);
+        assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+        assertEquals(6 * MINUTE_IN_MILLIS + 15 * SECOND_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+        advanceElapsedClock(30 * SECOND_IN_MILLIS);
+        assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+        assertEquals(6 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        // With activity pausing/stopping/destroying, tally should be updated.
+        advanceElapsedClock(MINUTE_IN_MILLIS);
+        event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_DESTROYED, sSystemClock.millis());
+        event.mPackage = SOURCE_PACKAGE;
+        mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+        waitForNonDelayedMessagesProcessed();
+        assertEquals(3 * MINUTE_IN_MILLIS, debit.getTallyLocked());
+        assertEquals(7 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+    }
+
+    @Test
+    public void testEJDebitTallying_StaleSession() {
+        setStandbyBucket(RARE_INDEX);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        TimingSession ts = new TimingSession(nowElapsed, nowElapsed + 10 * MINUTE_IN_MILLIS, 5);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true);
+
+        // Make the session stale.
+        advanceElapsedClock(12 * MINUTE_IN_MILLIS + mQcConstants.EJ_WINDOW_SIZE_MS);
+
+        // With lazy deletion, we don't update the tally until getRemainingEJExecutionTimeLocked()
+        // is called, so call that first.
+        assertEquals(10 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+        ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
+        assertEquals(0, debit.getTallyLocked());
+    }
+
+    /**
+     * Tests that rewards are properly accounted when there's no EJ running and the rewards exceed
+     * the accumulated debits.
+     */
+    @Test
+    public void testEJDebitTallying_RewardExceedDebits_NoActiveSession() {
+        setStandbyBucket(WORKING_INDEX);
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 30 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS);
+
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        TimingSession ts = new TimingSession(nowElapsed - 5 * MINUTE_IN_MILLIS,
+                nowElapsed - 4 * MINUTE_IN_MILLIS, 2);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true);
+
+        ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
+        assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked());
+        assertEquals(29 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(30 * SECOND_IN_MILLIS);
+        UsageEvents.Event event =
+                new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis());
+        event.mPackage = SOURCE_PACKAGE;
+        mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+        waitForNonDelayedMessagesProcessed();
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(30 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(MINUTE_IN_MILLIS);
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(30 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        // Excessive rewards don't increase maximum quota.
+        event = new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis());
+        event.mPackage = SOURCE_PACKAGE;
+        mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+        waitForNonDelayedMessagesProcessed();
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(30 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+    }
+
+    /**
+     * Tests that rewards are properly accounted when there's an active EJ running and the rewards
+     * exceed the accumulated debits.
+     */
+    @Test
+    public void testEJDebitTallying_RewardExceedDebits_ActiveSession() {
+        setStandbyBucket(WORKING_INDEX);
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+        setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 30 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS);
+        // 15 seconds for each 30 second chunk.
+        setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 30 * SECOND_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 15 * SECOND_IN_MILLIS);
+
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        TimingSession ts = new TimingSession(nowElapsed - 5 * MINUTE_IN_MILLIS,
+                nowElapsed - 4 * MINUTE_IN_MILLIS, 2);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true);
+
+        ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
+        assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked());
+        assertEquals(29 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        // With rewards coming in while an EJ is running, the remaining execution time should be
+        // adjusted accordingly (decrease due to EJ running + increase from reward).
+        JobStatus eJob =
+                createExpeditedJobStatus("testEJDebitTallying_RewardExceedDebits_ActiveSession", 1);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStartTrackingJobLocked(eJob, null);
+            mQuotaController.prepareForExecutionLocked(eJob);
+        }
+        advanceElapsedClock(30 * SECOND_IN_MILLIS);
+        assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked());
+        assertEquals(28 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(30 * SECOND_IN_MILLIS);
+        UsageEvents.Event event =
+                new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis());
+        event.mPackage = SOURCE_PACKAGE;
+        mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+        waitForNonDelayedMessagesProcessed();
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(29 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(MINUTE_IN_MILLIS);
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(28 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        // Activity start shouldn't reduce tally, but duration with activity started should affect
+        // remaining EJ time.
+        event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_RESUMED, sSystemClock.millis());
+        event.mPackage = SOURCE_PACKAGE;
+        mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+        waitForNonDelayedMessagesProcessed();
+        advanceElapsedClock(30 * SECOND_IN_MILLIS);
+        assertEquals(0, debit.getTallyLocked());
+        // Decrease by 30 seconds for running EJ, increase by 15 seconds due to ongoing activity.
+        assertEquals(27 * MINUTE_IN_MILLIS + 45 * SECOND_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+        advanceElapsedClock(30 * SECOND_IN_MILLIS);
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(MINUTE_IN_MILLIS);
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(27 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        event = new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis());
+        event.mPackage = SOURCE_PACKAGE;
+        mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+        waitForNonDelayedMessagesProcessed();
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(28 * MINUTE_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(MINUTE_IN_MILLIS);
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        // At this point, with activity pausing/stopping/destroying, since we're giving a reward,
+        // tally should remain 0, and time remaining shouldn't change since it was accounted for
+        // at every step.
+        event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_DESTROYED, sSystemClock.millis());
+        event.mPackage = SOURCE_PACKAGE;
+        mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event);
+        waitForNonDelayedMessagesProcessed();
+        assertEquals(0, debit.getTallyLocked());
+        assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+                mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 66b037d..c4c9ad0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -123,9 +123,10 @@
             .setPowerUsage(POWER_USAGE_HIGH)
             .setAccuracy(ProviderProperties.ACCURACY_FINE)
             .build();
+    private static final CallerIdentity PROVIDER_IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1,
+            "mypackage", "attribution");
     private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1,
-            "mypackage",
-            "attribution");
+            "mypackage", "attribution", "listener");
     private static final WorkSource WORK_SOURCE = new WorkSource(IDENTITY.getUid());
 
     private Random mRandom;
@@ -169,7 +170,7 @@
         mPassive.startManager();
         mPassive.setRealProvider(new PassiveLocationProvider(mContext));
 
-        mProvider = new TestProvider(PROPERTIES, IDENTITY);
+        mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY);
         mProvider.setProviderAllowed(true);
 
         mManager = new LocationProviderManager(mContext, mInjector, eventLog, NAME, mPassive);
@@ -351,7 +352,8 @@
 
     @Test
     public void testGetLastLocation_ClearOnMockRemoval() {
-        MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, IDENTITY);
+        MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, PROVIDER_IDENTITY,
+                null);
         mockProvider.setAllowed(true);
         mManager.setMockProvider(mockProvider);
 
@@ -441,7 +443,7 @@
     @Test
     public void testRegisterListener_SameProcess() throws Exception {
         CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
-                "attribution");
+                "attribution", "listener");
 
         ILocationListener listener = createMockLocationListener();
         mManager.registerLocationRequest(
@@ -477,7 +479,7 @@
     @Test
     public void testRegisterListener_Unregister_SameProcess() throws Exception {
         CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
-                "attribution");
+                "attribution", "listener");
 
         ILocationListener listener = createMockLocationListener();
         mManager.registerLocationRequest(
@@ -604,7 +606,7 @@
     @Test
     public void testRegisterListener_Wakelock() throws Exception {
         CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
-                "attribution");
+                "attribution", "listener");
 
         ILocationListener listener = createMockLocationListener();
         mManager.registerLocationRequest(
@@ -1047,7 +1049,7 @@
         private final ArrayList<Runnable> mFlushCallbacks = new ArrayList<>();
 
         TestProvider(ProviderProperties properties, CallerIdentity identity) {
-            super(DIRECT_EXECUTOR, identity, properties);
+            super(DIRECT_EXECUTOR, identity, properties, null);
         }
 
         public void setProviderAllowed(boolean allowed) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
index 07170da..e8a0bb5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
@@ -71,7 +71,8 @@
                         .setPowerUsage(POWER_USAGE_LOW)
                         .setAccuracy(ACCURACY_FINE)
                         .build(),
-                CallerIdentity.forTest(0, 1, "testpackage", "test"));
+                CallerIdentity.forTest(0, 1, "testpackage", "test"),
+                null);
 
         mProvider = new MockableLocationProvider(lock);
         mProvider.getController().setListener(mListener);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
index 775bdd5..a1eadbe 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
@@ -40,7 +40,7 @@
     private final FakeProviderInterface mFakeInterface;
 
     public FakeProvider(FakeProviderInterface fakeInterface) {
-        super(Runnable::run, null, null);
+        super(Runnable::run, null, null, null);
         mFakeInterface = fakeInterface;
     }
 
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 73b0105..6890ed1 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
@@ -28,6 +28,7 @@
 import android.app.appsearch.SetSchemaResponse;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.content.Context;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -55,6 +56,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 public class AppSearchImplTest {
@@ -971,21 +973,46 @@
     }
 
     @Test
-    public void testHasSchemaType() throws Exception {
-        // Nothing exists yet
-        assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isFalse();
+    public void testGetPackageToDatabases() throws Exception {
+        Map<String, Set<String>> existingMapping = mAppSearchImpl.getPackageToDatabases();
+        Map<String, Set<String>> expectedMapping = new ArrayMap<>();
+        expectedMapping.putAll(existingMapping);
 
+        // Has database1
+        expectedMapping.put("package1", ImmutableSet.of("database1"));
         mAppSearchImpl.setSchema(
-                "package",
-                "database",
-                Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
+                "package1",
+                "database1",
+                Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
                 /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
                 /*schemasPackageAccessible=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false);
-        assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isTrue();
+        assertThat(mAppSearchImpl.getPackageToDatabases())
+                .containsExactlyEntriesIn(expectedMapping);
 
-        assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "UnknownSchema"))
-                .isFalse();
+        // Has both databases
+        expectedMapping.put("package1", ImmutableSet.of("database1", "database2"));
+        mAppSearchImpl.setSchema(
+                "package1",
+                "database2",
+                Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false);
+        assertThat(mAppSearchImpl.getPackageToDatabases())
+                .containsExactlyEntriesIn(expectedMapping);
+
+        // Has both packages
+        expectedMapping.put("package2", ImmutableSet.of("database1"));
+        mAppSearchImpl.setSchema(
+                "package2",
+                "database1",
+                Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false);
+        assertThat(mAppSearchImpl.getPackageToDatabases())
+                .containsExactlyEntriesIn(expectedMapping);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index b98f025..205ff30 100644
--- a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.when;
 
+import android.app.backup.BackupAgent;
 import android.app.backup.BackupManager.OperationType;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
@@ -30,6 +31,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import com.android.internal.backup.IBackupTransport;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -56,6 +58,7 @@
     @Mock IBackupObserver mBackupObserver;
     @Mock PackageManager mPackageManager;
     @Mock TransportClient mTransportClient;
+    @Mock IBackupTransport mBackupTransport;
     @Mock BackupEligibilityRules mBackupEligibilityRules;
 
 
@@ -132,6 +135,33 @@
         assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules);
     }
 
+    @Test
+    public void testGetOperationTypeFromTransport_returnsBackupByDefault()
+            throws Exception {
+        when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport);
+        when(mBackupTransport.getTransportFlags()).thenReturn(0);
+
+        int operationType = mService.getOperationTypeFromTransport(mTransportClient);
+
+        assertThat(operationType).isEqualTo(OperationType.BACKUP);
+    }
+
+    @Test
+    public void testGetOperationTypeFromTransport_returnsMigrationForMigrationTransport()
+            throws Exception {
+        // This is a temporary flag to control the new behaviour until it's ready to be fully
+        // rolled out.
+        mService.shouldUseNewBackupEligibilityRules = true;
+
+        when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport);
+        when(mBackupTransport.getTransportFlags()).thenReturn(
+                BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER);
+
+        int operationType = mService.getOperationTypeFromTransport(mTransportClient);
+
+        assertThat(operationType).isEqualTo(OperationType.MIGRATION);
+    }
+
     private static PackageInfo getPackageInfo(String packageName) {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.applicationInfo = new ApplicationInfo();
@@ -141,6 +171,7 @@
 
     private static class TestBackupService extends UserBackupManagerService {
         boolean isEnabledStatePersisted = false;
+        boolean shouldUseNewBackupEligibilityRules = false;
 
         TestBackupService(Context context, PackageManager packageManager) {
             super(context, packageManager);
@@ -158,5 +189,10 @@
 
         @Override
         void updateStateOnBackupEnabled(boolean wasEnabled, boolean enable) {}
+
+        @Override
+        boolean shouldUseNewBackupEligibilityRules() {
+            return shouldUseNewBackupEligibilityRules;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index 2cbc3f3..a694d5e 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -153,4 +153,25 @@
         assertEquals("Period not restored", periodic.periodMillis, oneoff.periodMillis);
         assertEquals("Flex not restored", periodic.flexMillis, oneoff.flexMillis);
     }
+
+    @SmallTest
+    public void testScheduleAsEjIsInExtras() {
+        Account account1 = new Account("account1", "type1");
+        Bundle b1 = new Bundle();
+        b1.putBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true);
+
+        SyncOperation op1 = new SyncOperation(account1, 0, 1, "foo", 0,
+                SyncOperation.REASON_USER_START, "authority1", b1, false,
+                ContentResolver.SYNC_EXEMPTION_NONE);
+        assertTrue(op1.isScheduledAsExpeditedJob());
+
+        PersistableBundle pb = op1.toJobInfoExtras();
+        assertTrue("EJ extra not found in job extras",
+                ((PersistableBundle) pb.get("syncExtras"))
+                        .containsKey(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB));
+
+        SyncOperation op2 = SyncOperation.maybeCreateFromJobExtras(pb);
+        assertTrue("EJ extra not found in extras", op2.getClonedExtras()
+                .getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 6add8d1..87100a6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -20,6 +20,8 @@
 import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
 import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
 import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
 import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
 import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
 import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
@@ -826,7 +828,7 @@
      * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)}
      */
     @Test
-    public void testForceRemoveActiveAdmin() throws Exception {
+    public void testForceRemoveActiveAdmin_nonShellCaller() throws Exception {
         mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
 
         // Add admin.
@@ -840,8 +842,53 @@
         // Calling from a non-shell uid should fail with a SecurityException
         mContext.binder.callingUid = 123456;
         assertExpectException(SecurityException.class,
-                /* messageRegex =*/ "Non-shell user attempted to call",
+                /* messageRegex = */ null,
                 () -> dpms.forceRemoveActiveAdmin(admin1, CALLER_USER_HANDLE));
+    }
+
+    /**
+     * Test for:
+     * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)}
+     */
+    @Test
+    public void testForceRemoveActiveAdmin_nonShellCallerWithPermission() throws Exception {
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+        // Add admin.
+        setupPackageInPackageManager(admin1.getPackageName(),
+                /* userId= */ CALLER_USER_HANDLE,
+                /* appId= */ 10138,
+                /* flags= */ ApplicationInfo.FLAG_TEST_ONLY);
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+        assertThat(dpm.isAdminActive(admin1)).isTrue();
+
+        mContext.binder.callingUid = 123456;
+        mContext.callerPermissions.add(
+                android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        dpms.forceRemoveActiveAdmin(admin1, CALLER_USER_HANDLE);
+
+        mContext.callerPermissions.add(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        // Verify
+        assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse();
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                null, CALLER_USER_HANDLE);
+    }
+
+    /**
+     * Test for:
+     * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)}
+     */
+    @Test
+    public void testForceRemoveActiveAdmin_ShellCaller() throws Exception {
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+        // Add admin.
+        setupPackageInPackageManager(admin1.getPackageName(),
+                /* userId= */ CALLER_USER_HANDLE,
+                /* appId= */ 10138,
+                /* flags= */ ApplicationInfo.FLAG_TEST_ONLY);
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+        assertThat(dpm.isAdminActive(admin1)).isTrue();
 
         mContext.binder.callingUid = Process.SHELL_UID;
         dpms.forceRemoveActiveAdmin(admin1, CALLER_USER_HANDLE);
@@ -7028,6 +7075,71 @@
     }
 
     @Test
+    public void testSetDeviceOwnerType_throwsExceptionWhenCallerNotAuthorized() {
+        assertThrows(SecurityException.class,
+                () -> dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT));
+    }
+
+    @Test
+    public void testSetDeviceOwnerType_throwsExceptionWhenThereIsNoDeviceOwner() {
+        mContext.binder.clearCallingIdentity();
+        assertThrows(IllegalStateException.class,
+                () -> dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT));
+    }
+
+    @Test
+    public void testSetDeviceOwnerType_throwsExceptionWhenNotAsDeviceOwnerAdmin() throws Exception {
+        setDeviceOwner();
+
+        assertThrows(IllegalStateException.class,
+                () -> dpm.setDeviceOwnerType(admin2, DEVICE_OWNER_TYPE_FINANCED));
+    }
+
+    @Test
+    public void testSetDeviceOwnerType_asDeviceOwner_toFinancedDevice() throws Exception {
+        setDeviceOwner();
+
+        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+        int returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1);
+        assertThat(dpms.mOwners.hasDeviceOwner()).isTrue();
+        assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
+
+        initializeDpms();
+
+        returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1);
+        assertThat(dpms.mOwners.hasDeviceOwner()).isTrue();
+        assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
+    }
+
+    @Test
+    public void testSetDeviceOwnerType_asDeviceOwner_throwsExceptionWhenSetDeviceOwnerTypeAgain()
+            throws Exception {
+        setDeviceOwner();
+
+        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+        int returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1);
+        assertThat(dpms.mOwners.hasDeviceOwner()).isTrue();
+        assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
+
+        assertThrows(IllegalStateException.class,
+                () -> dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT));
+    }
+
+    @Test
+    public void testGetDeviceOwnerType_throwsExceptionWhenThereIsNoDeviceOwner() {
+        assertThrows(IllegalStateException.class, () -> dpm.getDeviceOwnerType(admin1));
+    }
+
+    @Test
+    public void testGetDeviceOwnerType_throwsExceptionWhenNotAsDeviceOwnerAdmin() throws Exception {
+        setDeviceOwner();
+
+        assertThrows(IllegalStateException.class, () -> dpm.getDeviceOwnerType(admin2));
+    }
+
+    @Test
     public void testSetUsbDataSignalingEnabled_noDeviceOwnerOrPoOfOrgOwnedDevice() {
         assertThrows(SecurityException.class,
                 () -> dpm.setUsbDataSignalingEnabled(true));
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index bfe183c..39ca925d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.devicepolicy;
 
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.ComponentName;
@@ -35,7 +38,6 @@
  * <p>Run this test with:
  *
  * {@code atest FrameworksServicesTests:com.android.server.devicepolicy.OwnersTest}
- *
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -67,6 +69,8 @@
 
             assertThat(owners.hasDeviceOwner()).isFalse();
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
             assertThat(owners.getSystemUpdatePolicy()).isNull();
             assertThat(owners.getProfileOwnerKeys()).isEmpty();
 
@@ -75,6 +79,12 @@
             assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
             assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
             assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
+
+            owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
+                    DEVICE_OWNER_TYPE_FINANCED);
+            // There is no device owner, so the default owner type should be returned.
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
         }
 
         // Then re-read and check.
@@ -84,6 +94,8 @@
 
             assertThat(owners.hasDeviceOwner()).isFalse();
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
             assertThat(owners.getSystemUpdatePolicy()).isNull();
             assertThat(owners.getProfileOwnerKeys()).isEmpty();
 
@@ -122,6 +134,8 @@
             assertThat(owners.getDeviceOwnerName()).isEqualTo(null);
             assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc");
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM);
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
 
             assertThat(owners.getSystemUpdatePolicy()).isNull();
             assertThat(owners.getProfileOwnerKeys()).isEmpty();
@@ -142,6 +156,8 @@
             assertThat(owners.getDeviceOwnerName()).isEqualTo(null);
             assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc");
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM);
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
 
             assertThat(owners.getSystemUpdatePolicy()).isNull();
             assertThat(owners.getProfileOwnerKeys()).isEmpty();
@@ -180,6 +196,8 @@
             assertThat(owners.hasDeviceOwner()).isFalse();
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
             assertThat(owners.getSystemUpdatePolicy()).isNull();
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
 
             assertThat(owners.getProfileOwnerKeys()).hasSize(2);
             assertThat(owners.getProfileOwnerComponent(10))
@@ -208,6 +226,8 @@
             assertThat(owners.hasDeviceOwner()).isFalse();
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
             assertThat(owners.getSystemUpdatePolicy()).isNull();
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
 
             assertThat(owners.getProfileOwnerKeys()).hasSize(2);
             assertThat(owners.getProfileOwnerComponent(10))
@@ -260,6 +280,8 @@
             assertThat(owners.getDeviceOwnerName()).isEqualTo(null);
             assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc");
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM);
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
 
             assertThat(owners.getSystemUpdatePolicy()).isNotNull();
             assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5);
@@ -292,6 +314,8 @@
             assertThat(owners.getDeviceOwnerName()).isEqualTo(null);
             assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc");
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM);
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
 
             assertThat(owners.getSystemUpdatePolicy()).isNotNull();
             assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5);
@@ -315,12 +339,21 @@
             assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
 
             owners.setDeviceOwnerUserRestrictionsMigrated();
+
+            owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
+                    DEVICE_OWNER_TYPE_FINANCED);
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_FINANCED);
         }
 
         {
             final OwnersTestable owners = new OwnersTestable(getServices());
             owners.load();
 
+            assertThat(owners.hasDeviceOwner()).isTrue();
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_FINANCED);
+
             assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
             assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue();
             assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue();
@@ -328,12 +361,22 @@
             assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
 
             owners.setProfileOwnerUserRestrictionsMigrated(11);
+
+            owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
+                    DEVICE_OWNER_TYPE_DEFAULT);
+            // The previous device owner type should persist.
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_FINANCED);
         }
 
         {
             final OwnersTestable owners = new OwnersTestable(getServices());
             owners.load();
 
+            assertThat(owners.hasDeviceOwner()).isTrue();
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_FINANCED);
+
             assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
             assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue();
             assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
@@ -369,6 +412,8 @@
 
             assertThat(owners.hasDeviceOwner()).isFalse();
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
 
 
             assertThat(owners.getSystemUpdatePolicy()).isNull();
@@ -388,6 +433,8 @@
 
             assertThat(owners.hasDeviceOwner()).isFalse();
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
 
 
             assertThat(owners.getSystemUpdatePolicy()).isNull();
@@ -425,6 +472,8 @@
 
             assertThat(owners.hasDeviceOwner()).isFalse();
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
             assertThat(owners.getProfileOwnerKeys()).isEmpty();
 
             assertThat(owners.getSystemUpdatePolicy()).isNotNull();
@@ -444,6 +493,8 @@
 
             assertThat(owners.hasDeviceOwner()).isFalse();
             assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
+            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                    DEVICE_OWNER_TYPE_DEFAULT);
             assertThat(owners.getProfileOwnerKeys()).isEmpty();
 
             assertThat(owners.getSystemUpdatePolicy()).isNotNull();
@@ -472,9 +523,16 @@
         assertThat(owners.getLegacyConfigFile().exists()).isFalse();
 
         assertThat(owners.getDeviceOwnerFile().exists()).isTrue();
+        assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
+                DEVICE_OWNER_TYPE_DEFAULT);
         assertThat(owners.getProfileOwnerFile(10).exists()).isTrue();
         assertThat(owners.getProfileOwnerFile(11).exists()).isTrue();
 
+        String previousDeviceOwnerPackageName = owners.getDeviceOwnerPackageName();
+        owners.setDeviceOwnerType(previousDeviceOwnerPackageName, DEVICE_OWNER_TYPE_FINANCED);
+        assertThat(owners.getDeviceOwnerType(previousDeviceOwnerPackageName)).isEqualTo(
+                DEVICE_OWNER_TYPE_FINANCED);
+
         // Then clear all information and save.
         owners.clearDeviceOwner();
         owners.clearSystemUpdatePolicy();
@@ -491,5 +549,8 @@
         assertThat(owners.getDeviceOwnerFile().exists()).isFalse();
         assertThat(owners.getProfileOwnerFile(10).exists()).isFalse();
         assertThat(owners.getProfileOwnerFile(11).exists()).isFalse();
+
+        assertThat(owners.getDeviceOwnerType(previousDeviceOwnerPackageName)).isEqualTo(
+                DEVICE_OWNER_TYPE_DEFAULT);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 23a4c2f..54825ee 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -64,7 +64,6 @@
     @Mock HysteresisLevels mAmbientBrightnessThresholds;
     @Mock HysteresisLevels mScreenBrightnessThresholds;
     @Mock Handler mNoOpHandler;
-    @Mock DisplayDeviceConfig mDisplayDeviceConfig;
     @Mock DisplayDevice mDisplayDevice;
 
     private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index f0b4f1b..285806b 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -88,7 +88,9 @@
     };
 
     private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
-    private static final int[] BACKLIGHT_RANGE = { 1, 255 };
+    private static final float[] DISPLAY_LEVELS_RANGE_NITS = { 13.25f, 478.5f };
+    private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f };
+    private static final float[] DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT = { 0.03149606299f, 1.0f };
 
     private static final float[] EMPTY_FLOAT_ARRAY = new float[0];
     private static final int[] EMPTY_INT_ARRAY = new int[0];
@@ -114,25 +116,28 @@
     };
     private static final Spline GAMMA_CORRECTION_SPLINE = Spline.createSpline(
             new float[] { 0.0f, 100.0f, 1000.0f, 2500.0f, 4000.0f, 4900.0f, 5000.0f },
-            new float[] { 0.035f, 0.035f, 0.221f, 0.523f, 0.797f, 0.980f, 1.0f });
+            new float[] { 0.0475f, 0.0475f, 0.2225f, 0.5140f, 0.8056f, 0.9805f, 1.0f });
 
     @Test
     public void testSimpleStrategyMappingAtControlPoints() {
         Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
-        BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc);
         assertNotNull("BrightnessMappingStrategy should not be null", simple);
         for (int i = 0; i < LUX_LEVELS.length; i++) {
-            final float expectedLevel =
-                    (float) DISPLAY_LEVELS_BACKLIGHT[i] / PowerManager.BRIGHTNESS_ON;
+            final float expectedLevel = MathUtils.map(PowerManager.BRIGHTNESS_OFF + 1,
+                    PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN,
+                    PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_BACKLIGHT[i]);
             assertEquals(expectedLevel,
-                    simple.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/);
+                    simple.getBrightness(LUX_LEVELS[i]), 0.0001f /*tolerance*/);
         }
     }
 
     @Test
     public void testSimpleStrategyMappingBetweenControlPoints() {
         Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
-        BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc);
         assertNotNull("BrightnessMappingStrategy should not be null", simple);
         for (int i = 1; i < LUX_LEVELS.length; i++) {
             final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
@@ -146,66 +151,71 @@
     @Test
     public void testSimpleStrategyIgnoresNewConfiguration() {
         Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
 
-        final int N = LUX_LEVELS.length;
         final float[] lux = { 0f, 1f };
         final float[] nits = { 0, PowerManager.BRIGHTNESS_ON };
 
         BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
                 .build();
         strategy.setBrightnessConfiguration(config);
-        assertNotEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/);
+        assertNotEquals(1.0f, strategy.getBrightness(1f), 0.0001f /*tolerance*/);
     }
 
     @Test
     public void testSimpleStrategyIgnoresNullConfiguration() {
         Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
 
         strategy.setBrightnessConfiguration(null);
         final int N = DISPLAY_LEVELS_BACKLIGHT.length;
         final float expectedBrightness =
                 (float) DISPLAY_LEVELS_BACKLIGHT[N - 1] / PowerManager.BRIGHTNESS_ON;
         assertEquals(expectedBrightness,
-                strategy.getBrightness(LUX_LEVELS[N - 1]), 0.01 /*tolerance*/);
+                strategy.getBrightness(LUX_LEVELS[N - 1]), 0.0001f /*tolerance*/);
     }
 
     @Test
     public void testPhysicalStrategyMappingAtControlPoints() {
-        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS,
-                DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
-        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc);
         assertNotNull("BrightnessMappingStrategy should not be null", physical);
         for (int i = 0; i < LUX_LEVELS.length; i++) {
-            final float expectedLevel = DISPLAY_LEVELS_NITS[i] / DISPLAY_RANGE_NITS[1];
+            final float expectedLevel = MathUtils.map(DISPLAY_RANGE_NITS[0], DISPLAY_RANGE_NITS[1],
+                    DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT[0],
+                    DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT[1],
+                    DISPLAY_LEVELS_NITS[i]);
             assertEquals(expectedLevel,
-                    physical.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/);
+                    physical.getBrightness(LUX_LEVELS[i]),
+                    0.0001f /*tolerance*/);
         }
     }
 
     @Test
     public void testPhysicalStrategyMappingBetweenControlPoints() {
-        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS,
-                DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
-        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
+        DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
+        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc);
         assertNotNull("BrightnessMappingStrategy should not be null", physical);
-        Spline backlightToBrightness =
-                Spline.createSpline(toFloatArray(BACKLIGHT_RANGE), DISPLAY_RANGE_NITS);
+        Spline brightnessToNits =
+                Spline.createSpline(BACKLIGHT_RANGE_ZERO_TO_ONE, DISPLAY_RANGE_NITS);
         for (int i = 1; i < LUX_LEVELS.length; i++) {
-            final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
-            final float backlight = physical.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
-            final float nits = backlightToBrightness.interpolate(backlight);
-            assertTrue("Desired brightness should be between adjacent control points.",
+            final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2.0f;
+            final float brightness = physical.getBrightness(lux);
+            final float nits = brightnessToNits.interpolate(brightness);
+            assertTrue("Desired brightness should be between adjacent control points: " + nits,
                     nits > DISPLAY_LEVELS_NITS[i - 1] && nits < DISPLAY_LEVELS_NITS[i]);
         }
     }
 
     @Test
     public void testPhysicalStrategyUsesNewConfigurations() {
-        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS,
-                DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
 
         final float[] lux = { 0f, 1f };
         final float[] nits = {
@@ -216,46 +226,53 @@
         BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
                 .build();
         strategy.setBrightnessConfiguration(config);
-        assertEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/);
+        assertEquals(1.0f, strategy.getBrightness(1f), 0.0001f /*tolerance*/);
 
         // Check that null returns us to the default configuration.
         strategy.setBrightnessConfiguration(null);
         final int N = DISPLAY_LEVELS_NITS.length;
         final float expectedBrightness = DISPLAY_LEVELS_NITS[N - 1] / DISPLAY_RANGE_NITS[1];
         assertEquals(expectedBrightness,
-                strategy.getBrightness(LUX_LEVELS[N - 1]), 0.01f /*tolerance*/);
+                strategy.getBrightness(LUX_LEVELS[N - 1]), 0.0001f /*tolerance*/);
     }
 
     @Test
     public void testPhysicalStrategyRecalculateSplines() {
-        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
-                BACKLIGHT_RANGE);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
+        DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS);
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
         float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length];
         for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) {
             adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f;
         }
 
         // Default is unadjusted
-        assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
-        assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+        assertEquals(DISPLAY_RANGE_NITS[0], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]),
+                0.0001f /* tolerance */);
+        assertEquals(DISPLAY_RANGE_NITS[1], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]),
+                0.0001f /* tolerance */);
 
         // When adjustment is turned on, adjustment array is used
         strategy.recalculateSplines(true, adjustedNits50p);
-        assertEquals(1.3425f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
-        assertEquals(239.25f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+        assertEquals(DISPLAY_RANGE_NITS[0] / 2,
+                strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]), 0.0001f /* tolerance */);
+        assertEquals(DISPLAY_RANGE_NITS[1] / 2,
+                strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]), 0.0001f /* tolerance */);
 
         // When adjustment is turned off, adjustment array is ignored
         strategy.recalculateSplines(false, adjustedNits50p);
-        assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
-        assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+        assertEquals(DISPLAY_RANGE_NITS[0], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]),
+                0.0001f /* tolerance */);
+        assertEquals(DISPLAY_RANGE_NITS[1], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]),
+                0.0001f /* tolerance */);
     }
 
     @Test
     public void testDefaultStrategyIsPhysical() {
         Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
-                DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+                DISPLAY_LEVELS_NITS);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
         assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy);
     }
 
@@ -266,15 +283,15 @@
         int tmp = lux[idx];
         lux[idx] = lux[idx+1];
         lux[idx+1] = tmp;
-        Resources res = createResources(lux, DISPLAY_LEVELS_NITS,
-                DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+        Resources res = createResources(lux, DISPLAY_LEVELS_NITS);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
         assertNull(strategy);
 
         // And make sure we get the same result even if it's monotone but not increasing.
         lux[idx] = lux[idx+1];
-        res = createResources(lux, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
-        strategy = BrightnessMappingStrategy.create(res);
+        res = createResources(lux, DISPLAY_LEVELS_NITS);
+        strategy = BrightnessMappingStrategy.create(res, ddc);
         assertNull(strategy);
     }
 
@@ -285,13 +302,13 @@
         // Make sure it's strictly increasing so that the only failure is the differing array
         // lengths
         lux[lux.length - 1] = lux[lux.length - 2] + 1;
-        Resources res = createResources(lux, DISPLAY_LEVELS_NITS,
-                DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+        Resources res = createResources(lux, DISPLAY_LEVELS_NITS);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
         assertNull(strategy);
 
         res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT);
-        strategy = BrightnessMappingStrategy.create(res);
+        strategy = BrightnessMappingStrategy.create(res, ddc);
         assertNull(strategy);
 
         // Extra backlight level
@@ -299,43 +316,45 @@
                 DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1);
         backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
         res = createResources(LUX_LEVELS, backlight);
-        strategy = BrightnessMappingStrategy.create(res);
+        strategy = BrightnessMappingStrategy.create(res, ddc);
         assertNull(strategy);
 
         // Extra nits level
         final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1);
         nits[nits.length - 1] = nits[nits.length - 2] + 1;
-        res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
-        strategy = BrightnessMappingStrategy.create(res);
+        res = createResources(LUX_LEVELS, nits);
+        strategy = BrightnessMappingStrategy.create(res, ddc);
         assertNull(strategy);
     }
 
     @Test
     public void testPhysicalStrategyRequiresNitsMapping() {
         Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
-                DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, BACKLIGHT_RANGE);
-        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
+                DISPLAY_LEVELS_NITS);
+        DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY /*nitsRange*/);
+        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc);
         assertNull(physical);
 
         res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
-                DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, EMPTY_INT_ARRAY /*backlightRange*/);
-        physical = BrightnessMappingStrategy.create(res);
+                DISPLAY_LEVELS_NITS);
+        physical = BrightnessMappingStrategy.create(res, ddc);
         assertNull(physical);
 
         res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
-                DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/,
-                EMPTY_INT_ARRAY /*backlightRange*/);
-        physical = BrightnessMappingStrategy.create(res);
+                DISPLAY_LEVELS_NITS);
+        physical = BrightnessMappingStrategy.create(res, ddc);
         assertNull(physical);
     }
 
     @Test
     public void testStrategiesAdaptToUserDataPoint() {
-        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS,
-                DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
-        assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res));
+        Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
+                DISPLAY_LEVELS_NITS);
+        DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
+        assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc));
+        ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
         res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
-        assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res));
+        assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc));
     }
 
     private static void assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy strategy) {
@@ -351,7 +370,7 @@
 
         // Then make sure that all control points after the middle lux level are also set to max...
         for (int i = idx; i < LUX_LEVELS.length; i++) {
-            assertEquals(strategy.getBrightness(LUX_LEVELS[idx]), 1.0, 0.01 /*tolerance*/);
+            assertEquals(strategy.getBrightness(LUX_LEVELS[idx]), 1.0, 0.0001f /*tolerance*/);
         }
 
         // ...and that all control points before the middle lux level are strictly less than the
@@ -369,12 +388,12 @@
         strategy.clearUserDataPoints();
         for (int i = 0; i < LUX_LEVELS.length; i++) {
             assertEquals(initialBrightnessLevels[i], strategy.getBrightness(LUX_LEVELS[i]),
-                    0.01 /*tolerance*/);
+                    0.0001f /*tolerance*/);
         }
 
         // Now set the middle of the lux range to something just above the minimum.
         float minBrightness = strategy.getBrightness(LUX_LEVELS[0]);
-        strategy.addUserDataPoint(LUX_LEVELS[idx], minBrightness + 0.01f);
+        strategy.addUserDataPoint(LUX_LEVELS[idx], minBrightness + 0.0001f);
 
         // Then make sure the curve is still monotonic.
         prevBrightness = 0f;
@@ -389,31 +408,21 @@
         // be true assuming that there are more than two lux levels in the curve since we picked a
         // brightness just barely above the minimum for the middle of the curve.
         minBrightness = (float) MathUtils.pow(minBrightness, MAXIMUM_GAMMA); // Gamma correction.
-        assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.01 /*tolerance*/);
-    }
-
-    private static float[] toFloatArray(int[] vals) {
-        float[] newVals = new float[vals.length];
-        for (int i = 0; i < vals.length; i++) {
-            newVals[i] = (float) vals[i];
-        }
-        return newVals;
+        assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.0001f /*tolerance*/);
     }
 
     private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight) {
         return createResources(luxLevels, brightnessLevelsBacklight,
-                EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/, EMPTY_FLOAT_ARRAY /*nitsRange*/,
-                EMPTY_INT_ARRAY /*backlightRange*/);
+                EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/);
     }
 
-    private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits,
-            float[] nitsRange, int[] backlightRange) {
+    private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits) {
         return createResources(luxLevels, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
-                brightnessLevelsNits, nitsRange, backlightRange);
+                brightnessLevelsNits);
     }
 
     private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight,
-            float[] brightnessLevelsNits, float[] nitsRange, int[] backlightRange) {
+            float[] brightnessLevelsNits) {
         Resources mockResources = mock(Resources.class);
         // For historical reasons, the lux levels resource implicitly defines the first point as 0,
         // so we need to chop it off of the array the mock resource object returns.
@@ -430,15 +439,6 @@
                 com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
                 .thenReturn(mockBrightnessLevelNits);
 
-        TypedArray mockNitsRange = createFloatTypedArray(nitsRange);
-        when(mockResources.obtainTypedArray(
-                com.android.internal.R.array.config_screenBrightnessNits))
-                .thenReturn(mockNitsRange);
-
-        when(mockResources.getIntArray(
-                com.android.internal.R.array.config_screenBrightnessBacklight))
-                .thenReturn(backlightRange);
-
         when(mockResources.getInteger(
                 com.android.internal.R.integer.config_screenBrightnessSettingMinimum))
                 .thenReturn(1);
@@ -451,6 +451,21 @@
         return mockResources;
     }
 
+    private DisplayDeviceConfig createDdc() {
+        return createDdc(DISPLAY_RANGE_NITS);
+    }
+
+    private DisplayDeviceConfig createDdc(float[] nitsArray) {
+        return createDdc(nitsArray, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT);
+    }
+
+    private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray) {
+        DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class);
+        when(mockDdc.getNits()).thenReturn(nitsArray);
+        when(mockDdc.getBrightness()).thenReturn(backlightArray);
+        return mockDdc;
+    }
+
     private TypedArray createFloatTypedArray(float[] vals) {
         TypedArray mockArray = mock(TypedArray.class);
         when(mockArray.length()).thenAnswer(invocation -> {
@@ -488,21 +503,22 @@
         final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1);
         final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
         final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
-        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
-                DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
+
+        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc);
         // Let's start with a validity check:
-        assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */);
-        assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
-        assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */);
+        assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */);
+        assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
+        assertEquals(y3, strategy.getBrightness(x3), 0.0001f /* tolerance */);
         // OK, let's roll:
         float gamma = 0.5f;
         strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma));
-        assertEquals(MathUtils.pow(y1, gamma), strategy.getBrightness(x1), 0.01f /* tolerance */);
-        assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */);
-        assertEquals(MathUtils.pow(y3, gamma), strategy.getBrightness(x3), 0.01f /* tolerance */);
-        // The adjustment should be +0.63 (manual calculation).
-        assertEquals(+0.63f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+        assertEquals(MathUtils.pow(y1, gamma), strategy.getBrightness(x1), 0.0001f /* tolerance */);
+        assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.0001f /* tolerance */);
+        assertEquals(MathUtils.pow(y3, gamma), strategy.getBrightness(x3), 0.0001f /* tolerance */);
+        // The adjustment should be +0.6308 (manual calculation).
+        assertEquals(+0.6308f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */);
     }
 
     @Test
@@ -516,39 +532,39 @@
         final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1);
         final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
         final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
-        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
-                DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
+        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc);
         // Validity check:
-        assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */);
-        assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
-        assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */);
+        assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */);
+        assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
+        assertEquals(y3, strategy.getBrightness(x3), 0.0001f /* tolerance */);
         // Let's roll:
         float gamma = 0.25f;
         final float minGamma = 1.0f / MAXIMUM_GAMMA;
         strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma));
-        assertEquals(MathUtils.pow(y1, minGamma), strategy.getBrightness(x1),
-                0.01f /* tolerance */);
-        assertEquals(MathUtils.pow(y2, gamma),    strategy.getBrightness(x2),
-                0.01f /* tolerance */);
-        assertEquals(MathUtils.pow(y3, minGamma), strategy.getBrightness(x3),
-                0.01f /* tolerance */);
+        assertEquals(MathUtils.pow(y1, minGamma),
+                strategy.getBrightness(x1), 0.0001f /* tolerance */);
+        assertEquals(MathUtils.pow(y2, gamma),
+                strategy.getBrightness(x2), 0.0001f /* tolerance */);
+        assertEquals(MathUtils.pow(y3, minGamma),
+                strategy.getBrightness(x3), 0.0001f /* tolerance */);
         // The adjustment should be +1.0 (maximum adjustment).
-        assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+        assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */);
     }
 
     @Test
     public void testGammaCorrectionExtremeChangeAtCenter() {
         // Extreme changes (e.g. setting brightness to 0.0 or 1.0) can't be gamma corrected, so we
         // just make sure the adjustment reflects the change.
-        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
-                DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
-        assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc);
+        assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */);
         strategy.addUserDataPoint(2500, 1.0f);
-        assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+        assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */);
         strategy.addUserDataPoint(2500, 0.0f);
-        assertEquals(-1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+        assertEquals(-1.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */);
     }
 
     @Test
@@ -562,28 +578,28 @@
         final float y0 = GAMMA_CORRECTION_SPLINE.interpolate(x0);
         final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
         final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4);
-        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
-                DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
+        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
+        DisplayDeviceConfig ddc = createDdc();
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc);
         // Validity, as per tradition:
-        assertEquals(y0, strategy.getBrightness(x0), 0.01f /* tolerance */);
-        assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
-        assertEquals(y4, strategy.getBrightness(x4), 0.01f /* tolerance */);
+        assertEquals(y0, strategy.getBrightness(x0), 0.0001f /* tolerance */);
+        assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
+        assertEquals(y4, strategy.getBrightness(x4), 0.0001f /* tolerance */);
         // Rollin':
         float adjustment = 0.3f;
         float gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment);
         strategy.addUserDataPoint(x0, y0 + adjustment);
-        assertEquals(y0 + adjustment, strategy.getBrightness(x0), 0.01f /* tolerance */);
-        assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */);
-        assertEquals(MathUtils.pow(y4, gamma), strategy.getBrightness(x4), 0.01f /* tolerance */);
-        assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+        assertEquals(y0 + adjustment, strategy.getBrightness(x0), 0.0001f /* tolerance */);
+        assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.0001f /* tolerance */);
+        assertEquals(MathUtils.pow(y4, gamma), strategy.getBrightness(x4), 0.0001f /* tolerance */);
+        assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */);
         // Similarly, if we set a user data point at (x4, 1.0), the adjustment should be 1 - y4.
         adjustment = 1.0f - y4;
         gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment);
         strategy.addUserDataPoint(x4, 1.0f);
-        assertEquals(MathUtils.pow(y0, gamma), strategy.getBrightness(x0), 0.01f /* tolerance */);
-        assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */);
-        assertEquals(1.0f, strategy.getBrightness(x4), 0.01f /* tolerance */);
-        assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+        assertEquals(MathUtils.pow(y0, gamma), strategy.getBrightness(x0), 0.0001f /* tolerance */);
+        assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.0001f /* tolerance */);
+        assertEquals(1.0f, strategy.getBrightness(x4), 0.0001f /* tolerance */);
+        assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
index 27fce3c..912da94 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
@@ -45,7 +45,7 @@
     public void testWriteRead() throws Exception {
         long expectedModifiedDate = 1234567890;
         PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
-        config.lastModifiedDate = expectedModifiedDate;
+        config.lastModifiedMillis = expectedModifiedDate;
         config.updatedFontDirs.add("~~abc");
         config.updatedFontDirs.add("~~def");
 
@@ -65,7 +65,7 @@
                 PersistentSystemFontConfig.Config another = new PersistentSystemFontConfig.Config();
                 PersistentSystemFontConfig.loadFromXml(bais, another);
 
-                assertThat(another.lastModifiedDate).isEqualTo(expectedModifiedDate);
+                assertThat(another.lastModifiedMillis).isEqualTo(expectedModifiedDate);
                 assertThat(another.updatedFontDirs).containsExactly("~~abc", "~~def");
                 assertThat(another.fontFamilies).containsExactly(fontFamily);
             }
@@ -82,7 +82,7 @@
                      new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
             PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
             PersistentSystemFontConfig.loadFromXml(bais, config);
-            assertThat(config.lastModifiedDate).isEqualTo(0);
+            assertThat(config.lastModifiedMillis).isEqualTo(0);
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 7771afc..843296e 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -151,7 +151,7 @@
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
         PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
-        config.lastModifiedDate = expectedModifiedDate;
+        config.lastModifiedMillis = expectedModifiedDate;
         writeConfig(config, mConfigFile);
         UpdatableFontDir dirForPreparation = new UpdatableFontDir(
                 mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
@@ -507,7 +507,7 @@
         File readonlyFile = new File(readonlyDir, "readonly_config.xml");
 
         PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
-        config.lastModifiedDate = expectedModifiedDate;
+        config.lastModifiedMillis = expectedModifiedDate;
         writeConfig(config, readonlyFile);
 
         assertThat(readonlyDir.setWritable(false, false)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 7d7af03..74bf4f5 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -115,7 +115,6 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicy;
-import android.net.NetworkPolicyManager;
 import android.net.NetworkState;
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
@@ -386,8 +385,7 @@
                 Log.d(TAG, "set mUidObserver to " + mUidObserver);
                 return null;
             }
-        }).when(mActivityManager).registerUidObserver(any(), anyInt(),
-                eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), any(String.class));
+        }).when(mActivityManager).registerUidObserver(any(), anyInt(), anyInt(), any(String.class));
 
         mFutureIntent = newRestrictBackgroundChangedFuture();
         mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index ddbe81c..26b34fd 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -318,7 +318,8 @@
         assertTrue(pssProto.energyMeasurement.length == ENERGY_METER_COUNT);
         for (int i = 0; i < pssProto.energyMeasurement.length; i++) {
             assertTrue(pssProto.energyMeasurement[i].id == i);
-            assertTrue(pssProto.energyMeasurement[i].timestampMs == i);
+            assertTrue(pssProto.energyMeasurement[i].timestampMs ==
+                    i + mPowerStatsLogger.getStartWallTime());
             assertTrue(pssProto.energyMeasurement[i].durationMs == i);
             assertTrue(pssProto.energyMeasurement[i].energyUws == i);
         }
@@ -359,7 +360,8 @@
         assertTrue(pssProto.energyConsumerResult.length == ENERGY_CONSUMER_COUNT);
         for (int i = 0; i < pssProto.energyConsumerResult.length; i++) {
             assertTrue(pssProto.energyConsumerResult[i].id == i);
-            assertTrue(pssProto.energyConsumerResult[i].timestampMs == i);
+            assertTrue(pssProto.energyConsumerResult[i].timestampMs ==
+                    i + mPowerStatsLogger.getStartWallTime());
             assertTrue(pssProto.energyConsumerResult[i].energyUws == i);
             assertTrue(pssProto.energyConsumerResult[i].attribution.length
                     == ENERGY_CONSUMER_ATTRIBUTION_COUNT);
@@ -420,7 +422,8 @@
                 assertTrue(stateResidency.id == j);
                 assertTrue(stateResidency.totalTimeInStateMs == j);
                 assertTrue(stateResidency.totalStateEntryCount == j);
-                assertTrue(stateResidency.lastEntryTimestampMs == j);
+                assertTrue(stateResidency.lastEntryTimestampMs ==
+                        j + mPowerStatsLogger.getStartWallTime());
             }
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
index 816bc6b..33385af 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
@@ -1 +1 @@
-include /core/java/android/media/soundtrigger/OWNERS
+include /media/aidl/android/media/soundtrigger_middleware/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 682a80c..5d27552 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -46,8 +46,8 @@
     public void test_unrestricted() {
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(true)
-                .setAutoDetectionSupported(true)
-                .setGeoDetectionSupported(true)
+                .setAutoDetectionFeatureSupported(true)
+                .setGeoDetectionFeatureSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(true)
@@ -108,8 +108,8 @@
     public void test_restricted() {
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(false)
-                .setAutoDetectionSupported(true)
-                .setGeoDetectionSupported(true)
+                .setAutoDetectionFeatureSupported(true)
+                .setGeoDetectionFeatureSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(true)
@@ -170,8 +170,8 @@
     public void test_autoDetectNotSupported() {
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(true)
-                .setAutoDetectionSupported(false)
-                .setGeoDetectionSupported(false)
+                .setAutoDetectionFeatureSupported(false)
+                .setGeoDetectionFeatureSupported(false)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(true)
@@ -232,8 +232,8 @@
     public void test_geoDetectNotSupported() {
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(true)
-                .setAutoDetectionSupported(true)
-                .setGeoDetectionSupported(false)
+                .setAutoDetectionFeatureSupported(true)
+                .setGeoDetectionFeatureSupported(false)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(true)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index d2452ea..14e0bbd 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -364,8 +364,8 @@
         // the tests.
         final boolean geoDetectionEnabled = autoDetectionEnabled;
         return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
-                .setAutoDetectionSupported(true)
-                .setGeoDetectionSupported(true)
+                .setAutoDetectionFeatureSupported(true)
+                .setGeoDetectionFeatureSupported(true)
                 .setUserConfigAllowed(true)
                 .setAutoDetectionEnabled(autoDetectionEnabled)
                 .setLocationEnabled(geoDetectionEnabled)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index c8dba5f..f1f8b2f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -90,8 +90,8 @@
     private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(false)
-                    .setAutoDetectionSupported(true)
-                    .setGeoDetectionSupported(true)
+                    .setAutoDetectionFeatureSupported(true)
+                    .setGeoDetectionFeatureSupported(true)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
@@ -100,8 +100,8 @@
     private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(false)
-                    .setAutoDetectionSupported(true)
-                    .setGeoDetectionSupported(true)
+                    .setAutoDetectionFeatureSupported(true)
+                    .setGeoDetectionFeatureSupported(true)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(true)
@@ -110,8 +110,8 @@
     private static final ConfigurationInternal CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(true)
-                    .setAutoDetectionSupported(false)
-                    .setGeoDetectionSupported(false)
+                    .setAutoDetectionFeatureSupported(false)
+                    .setGeoDetectionFeatureSupported(false)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
@@ -120,8 +120,8 @@
     private static final ConfigurationInternal CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(true)
-                    .setAutoDetectionSupported(true)
-                    .setGeoDetectionSupported(false)
+                    .setAutoDetectionFeatureSupported(true)
+                    .setGeoDetectionFeatureSupported(false)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(true)
@@ -130,8 +130,8 @@
     private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(true)
-                    .setAutoDetectionSupported(true)
-                    .setGeoDetectionSupported(true)
+                    .setAutoDetectionFeatureSupported(true)
+                    .setGeoDetectionFeatureSupported(true)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
@@ -139,8 +139,8 @@
 
     private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED =
             new ConfigurationInternal.Builder(USER_ID)
-                    .setAutoDetectionSupported(true)
-                    .setGeoDetectionSupported(true)
+                    .setAutoDetectionFeatureSupported(true)
+                    .setGeoDetectionFeatureSupported(true)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
@@ -149,8 +149,8 @@
 
     private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED =
             new ConfigurationInternal.Builder(USER_ID)
-                    .setAutoDetectionSupported(true)
-                    .setGeoDetectionSupported(true)
+                    .setAutoDetectionFeatureSupported(true)
+                    .setGeoDetectionFeatureSupported(true)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
index d319488..8280cdc 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
@@ -44,8 +44,8 @@
             @UserIdInt int userId, boolean geoDetectionEnabled) {
         return new ConfigurationInternal.Builder(userId)
                 .setUserConfigAllowed(true)
-                .setAutoDetectionSupported(true)
-                .setGeoDetectionSupported(true)
+                .setAutoDetectionFeatureSupported(true)
+                .setGeoDetectionFeatureSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(geoDetectionEnabled)
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index a640509..cebdbbe 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3671,8 +3671,8 @@
         mService.addNotification(r);
 
         final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true);
-        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getSbn().getTag(),
-                r.getSbn().getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
+        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getUserId(),
+                r.getKey(), NotificationStats.DISMISSAL_AOD,
                 NotificationStats.DISMISS_SENTIMENT_POSITIVE, nv);
         waitForIdle();
 
@@ -3694,8 +3694,8 @@
         mService.addNotification(r);
 
         final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true);
-        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getSbn().getTag(),
-                r.getSbn().getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
+        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getUserId(),
+                r.getKey(), NotificationStats.DISMISSAL_AOD,
                 NotificationStats.DISMISS_SENTIMENT_NEGATIVE, nv);
         waitForIdle();
 
@@ -6693,8 +6693,7 @@
         final NotificationVisibility nv = NotificationVisibility.obtain(nrSummary.getKey(), 1, 2,
                 true);
         mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG,
-                nrSummary.getSbn().getTag(),
-                nrSummary.getSbn().getId(), nrSummary.getUserId(), nrSummary.getKey(),
+                nrSummary.getUserId(), nrSummary.getKey(),
                 NotificationStats.DISMISSAL_SHADE,
                 NotificationStats.DISMISS_SENTIMENT_NEUTRAL, nv);
         waitForIdle();
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
new file mode 100644
index 0000000..3025a95
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import static android.view.KeyEvent.ACTION_DOWN;
+import static android.view.KeyEvent.ACTION_UP;
+import static android.view.KeyEvent.KEYCODE_POWER;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.policy.SingleKeyGestureDetector.KEY_LONGPRESS;
+import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.SystemClock;
+import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test class for {@link SingleKeyGestureDetector}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:SingleKeyGestureTests
+ */
+public class SingleKeyGestureTests {
+    private SingleKeyGestureDetector mDetector;
+
+    private int mMaxMultiPressPowerCount = 2;
+
+    private CountDownLatch mShortPressed = new CountDownLatch(1);
+    private CountDownLatch mLongPressed = new CountDownLatch(1);
+    private CountDownLatch mVeryLongPressed = new CountDownLatch(1);
+    private CountDownLatch mMultiPressed = new CountDownLatch(1);
+
+    private final Instrumentation mInstrumentation = getInstrumentation();
+    private final Context mContext = mInstrumentation.getTargetContext();
+    private long mWaitTimeout;
+    private long mLongPressTime;
+    private long mVeryLongPressTime;
+
+    @Before
+    public void setUp() {
+        mDetector = new SingleKeyGestureDetector(mContext);
+        initSingleKeyGestureRules();
+        mWaitTimeout = ViewConfiguration.getMultiPressTimeout() + 50;
+        mLongPressTime = ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout() + 50;
+        mVeryLongPressTime = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_veryLongPressTimeout) + 50;
+    }
+
+    private void initSingleKeyGestureRules() {
+        mDetector.addRule(new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER,
+                KEY_LONGPRESS | KEY_VERYLONGPRESS) {
+            @Override
+            int getMaxMultiPressCount() {
+                return mMaxMultiPressPowerCount;
+            }
+            @Override
+            public void onPress(long downTime) {
+                mShortPressed.countDown();
+            }
+
+            @Override
+            void onLongPress(long downTime) {
+                mLongPressed.countDown();
+            }
+
+            @Override
+            void onVeryLongPress(long downTime) {
+                mVeryLongPressed.countDown();
+            }
+
+            @Override
+            void onMultiPress(long downTime, int count) {
+                mMultiPressed.countDown();
+                assertEquals(mMaxMultiPressPowerCount, count);
+            }
+        });
+    }
+
+    private void pressKey(long eventTime, int keyCode, long pressTime) {
+        final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN,
+                keyCode, 0 /* repeat */, 0 /* metaState */);
+        mDetector.interceptKey(keyDown);
+
+        // keep press down.
+        try {
+            Thread.sleep(pressTime);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        eventTime += pressTime;
+        final KeyEvent keyUp = new KeyEvent(eventTime, eventTime, ACTION_UP,
+                keyCode, 0 /* repeat */, 0 /* metaState */);
+
+        mDetector.interceptKey(keyUp);
+    }
+
+    @Test
+    public void testShortPress() throws InterruptedException {
+        final long eventTime = SystemClock.uptimeMillis();
+        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+        assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testLongPress() throws InterruptedException {
+        final long eventTime = SystemClock.uptimeMillis();
+        pressKey(eventTime, KEYCODE_POWER, mLongPressTime);
+        assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testVeryLongPress() throws InterruptedException {
+        final long eventTime = SystemClock.uptimeMillis();
+        pressKey(eventTime, KEYCODE_POWER, mVeryLongPressTime);
+        assertTrue(mVeryLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testMultiPress() throws InterruptedException {
+        final long eventTime = SystemClock.uptimeMillis();
+        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+        assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index d13e4dc..7df17fd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -38,6 +38,7 @@
 
 import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
 import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
+import static com.android.server.wm.DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -595,6 +596,17 @@
         assertThat(token.isDescendantOf(mRoot)).isTrue();
         assertThat(token.isDescendantOf(mGroupRoot1)).isFalse();
         assertThat(token.isDescendantOf(mGroupRoot2)).isFalse();
+
+        // When the window has options for target root id, attach it to the target root.
+        final Bundle options = new Bundle();
+        options.putInt(KEY_ROOT_DISPLAY_AREA_ID, mGroupRoot2.mFeatureId);
+        final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class),
+                TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+                true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
+                false /* fromClientToken */, options);
+        policy.addWindow(token2);
+
+        assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue();
     }
 
     @Test
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 dc702e6..137cf65 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -33,6 +33,7 @@
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
@@ -1443,7 +1444,7 @@
         // Assume the animation of PipTaskOrganizer is done and then commit fullscreen to task.
         pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         displayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
-        assertFalse(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging());
+        assertFalse(displayContent.getPinnedTaskController().isPipActiveOrWindowingModeChanging());
         assertEquals(pinnedConfigOrientation, displayConfig.orientation);
 
         clearInvocations(mWm);
@@ -1454,7 +1455,7 @@
         assertFalse(displayContent.hasTopFixedRotationLaunchingApp());
         verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
         assertEquals(homeConfigOrientation, displayConfig.orientation);
-        assertTrue(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging());
+        assertTrue(displayContent.getPinnedTaskController().isPipActiveOrWindowingModeChanging());
     }
 
     @Test
@@ -1845,6 +1846,37 @@
         verify(t).show(mDisplayContent.mImeScreenshot);
     }
 
+    @Test
+    public void testRotateBounds_keepSamePhysicalPosition() {
+        final DisplayContent dc =
+                new TestDisplayContent.Builder(mAtm, 1000, 2000).build();
+        final Rect initBounds = new Rect(0, 0, 700, 1500);
+        final Rect rotateBounds = new Rect(initBounds);
+
+        // Rotate from 0 to 0
+        dc.rotateBounds(ROTATION_0, ROTATION_0, rotateBounds);
+
+        assertEquals(new Rect(0, 0, 700, 1500), rotateBounds);
+
+        // Rotate from 0 to 90
+        rotateBounds.set(initBounds);
+        dc.rotateBounds(ROTATION_0, ROTATION_90, rotateBounds);
+
+        assertEquals(new Rect(0, 300, 1500, 1000), rotateBounds);
+
+        // Rotate from 0 to 180
+        rotateBounds.set(initBounds);
+        dc.rotateBounds(ROTATION_0, ROTATION_180, rotateBounds);
+
+        assertEquals(new Rect(300, 500, 1000, 2000), rotateBounds);
+
+        // Rotate from 0 to 270
+        rotateBounds.set(initBounds);
+        dc.rotateBounds(ROTATION_0, ROTATION_270, rotateBounds);
+
+        assertEquals(new Rect(500, 0, 2000, 700), rotateBounds);
+    }
+
     private boolean isOptionsPanelAtRight(int displayId) {
         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 8969695..51aec65 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -251,7 +251,7 @@
 
         // Simulate the window is in split screen primary stack and the current state is
         // minimized and home stack is resizable, so that we should ignore input for the stack.
-        final DockedStackDividerController controller =
+        final DockedTaskDividerController controller =
                 mDisplayContent.getDockedDividerController();
         final Task stack = createTaskStackOnDisplay(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
                 ACTIVITY_TYPE_STANDARD, mDisplayContent);
diff --git a/services/texttospeech/Android.bp b/services/texttospeech/Android.bp
index bacc932..391ab89 100644
--- a/services/texttospeech/Android.bp
+++ b/services/texttospeech/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 filegroup {
     name: "services.texttospeech-sources",
     srcs: ["java/**/*.java"],
@@ -10,4 +19,4 @@
     defaults: ["platform_service_defaults"],
     srcs: [":services.texttospeech-sources"],
     libs: ["services.core"],
-}
\ No newline at end of file
+}
diff --git a/services/translation/Android.bp b/services/translation/Android.bp
index 804a617..f257f1b 100644
--- a/services/translation/Android.bp
+++ b/services/translation/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 filegroup {
     name: "services.translation-sources",
     srcs: ["java/**/*.java"],
@@ -10,4 +19,4 @@
     defaults: ["platform_service_defaults"],
     srcs: [":services.translation-sources"],
     libs: ["services.core"],
-}
\ No newline at end of file
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 8628f89..dce63eb 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -76,6 +76,7 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IHotwordRecognitionStatusCallback;
 import com.android.internal.app.IVoiceActionCheckCallback;
@@ -643,6 +644,10 @@
         }
 
         ComponentName findAvailRecognizer(String prefPackage, int userHandle) {
+            if (prefPackage == null) {
+                prefPackage = getDefaultRecognizer();
+            }
+
             List<ResolveInfo> available =
                     mContext.getPackageManager().queryIntentServicesAsUser(
                             new Intent(RecognitionService.SERVICE_INTERFACE),
@@ -670,6 +675,12 @@
             }
         }
 
+        @Nullable
+        public String getDefaultRecognizer() {
+            String recognizer = mContext.getString(R.string.config_systemSpeechRecognizer);
+            return TextUtils.isEmpty(recognizer) ? null : recognizer;
+        }
+
         ComponentName getCurRecognizer(int userHandle) {
             String curRecognizer = Settings.Secure.getStringForUser(
                     mContext.getContentResolver(),
diff --git a/telecomm/java/android/telecom/Connection.aidl b/telecomm/java/android/telecom/Connection.aidl
new file mode 100644
index 0000000..5b40036
--- /dev/null
+++ b/telecomm/java/android/telecom/Connection.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom;
+
+/**
+ * {@hide}
+ */
+parcelable Connection.CallFilteringCompletionInfo;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 942a54e..7c6253ce 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -29,6 +29,7 @@
 import android.app.Notification;
 import android.bluetooth.BluetoothDevice;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.hardware.camera2.CameraManager;
 import android.net.Uri;
@@ -38,7 +39,9 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.telephony.ims.ImsStreamMediaProfile;
@@ -3379,6 +3382,121 @@
     public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
 
     /**
+     * Information provided to a {@link Connection} upon completion of call filtering in Telecom.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class CallFilteringCompletionInfo implements Parcelable {
+        private final boolean mIsBlocked;
+        private final boolean mIsInContacts;
+        private final CallScreeningService.CallResponse mCallResponse;
+        private final ComponentName mCallScreeningComponent;
+
+        /**
+         * Constructor for {@link CallFilteringCompletionInfo}
+         *
+         * @param isBlocked Whether any part of the call filtering process indicated that this call
+         *                  should be blocked.
+         * @param isInContacts Whether the caller is in the user's contacts.
+         * @param callResponse The instance of {@link CallScreeningService.CallResponse} provided
+         *                     by the {@link CallScreeningService} that processed this call, or
+         *                     {@code null} if no call screening service ran.
+         * @param callScreeningComponent The component of the {@link CallScreeningService}
+         *                                 that processed this call, or {@link null} if no
+         *                                 call screening service ran.
+         */
+        public CallFilteringCompletionInfo(boolean isBlocked, boolean isInContacts,
+                @Nullable CallScreeningService.CallResponse callResponse,
+                @Nullable ComponentName callScreeningComponent) {
+            mIsBlocked = isBlocked;
+            mIsInContacts = isInContacts;
+            mCallResponse = callResponse;
+            mCallScreeningComponent = callScreeningComponent;
+        }
+
+        /** @hide */
+        protected CallFilteringCompletionInfo(Parcel in) {
+            mIsBlocked = in.readByte() != 0;
+            mIsInContacts = in.readByte() != 0;
+            CallScreeningService.ParcelableCallResponse response
+                    = in.readParcelable(CallScreeningService.class.getClassLoader());
+            mCallResponse = response == null ? null : response.toCallResponse();
+            mCallScreeningComponent = in.readParcelable(ComponentName.class.getClassLoader());
+        }
+
+        @NonNull
+        public static final Creator<CallFilteringCompletionInfo> CREATOR =
+                new Creator<CallFilteringCompletionInfo>() {
+                    @Override
+                    public CallFilteringCompletionInfo createFromParcel(Parcel in) {
+                        return new CallFilteringCompletionInfo(in);
+                    }
+
+                    @Override
+                    public CallFilteringCompletionInfo[] newArray(int size) {
+                        return new CallFilteringCompletionInfo[size];
+                    }
+                };
+
+        /**
+         * @return Whether any part of the call filtering process indicated that this call should be
+         *         blocked.
+         */
+        public boolean isBlocked() {
+            return mIsBlocked;
+        }
+
+        /**
+         * @return Whether the caller is in the user's contacts.
+         */
+        public boolean isInContacts() {
+            return mIsInContacts;
+        }
+
+        /**
+         * @return The instance of {@link CallScreeningService.CallResponse} provided
+         *         by the {@link CallScreeningService} that processed this
+         *         call, or {@code null} if no call screening service ran.
+         */
+        public @Nullable CallScreeningService.CallResponse getCallResponse() {
+            return mCallResponse;
+        }
+
+        /**
+         * @return The component of the {@link CallScreeningService}
+         *         that processed this call, or {@code null} if no call screening service ran.
+         */
+        public @Nullable ComponentName getCallScreeningComponent() {
+            return mCallScreeningComponent;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public String toString() {
+            return "CallFilteringCompletionInfo{" +
+                    "mIsBlocked=" + mIsBlocked +
+                    ", mIsInContacts=" + mIsInContacts +
+                    ", mCallResponse=" + mCallResponse +
+                    ", mCallScreeningPackageName='" + mCallScreeningComponent + '\'' +
+                    '}';
+        }
+
+        /** @hide */
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeByte((byte) (mIsBlocked ? 1 : 0));
+            dest.writeByte((byte) (mIsInContacts ? 1 : 0));
+            dest.writeParcelable(mCallResponse == null ? null : mCallResponse.toParcelable(), 0);
+            dest.writeParcelable(mCallScreeningComponent, 0);
+        }
+    }
+
+    /**
      * Indicates that call filtering in Telecom is complete
      *
      * This method is called for a connection created via
@@ -3386,24 +3504,13 @@
      * Telecom, including checking the blocked number db, per-contact settings, and custom call
      * filtering apps.
      *
-     * @param isBlocked {@code true} if the call was blocked, {@code false} otherwise. If this is
-     *                  {@code true}, {@link #onDisconnect()} will be called soon after
-     *                  this is called.
-     * @param isInContacts Indicates whether the caller is in the user's contacts list.
-     * @param callScreeningResponse The response that was returned from the
-     *                              {@link CallScreeningService} that handled this call. If no
-     *                              response was received from a call screening service,
-     *                              this will be {@code null}.
-     * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the
-     *                                  system dialer. If {@code callScreeningResponse} is
-     *                                  {@code null}, this will be {@code false}.
+     * @param callFilteringCompletionInfo Info provided by Telecom on the results of call filtering.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_CONTACTS)
-    public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
-            @Nullable CallScreeningService.CallResponse callScreeningResponse,
-            boolean isResponseFromSystemDialer) { }
+    public void onCallFilteringCompleted(
+            @NonNull CallFilteringCompletionInfo callFilteringCompletionInfo) { }
 
     static String toLogSafePhoneNumber(String number) {
         // For unknown number, log empty string.
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 966ece3..c189b19 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -758,19 +758,15 @@
         }
 
         @Override
-        public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
-                CallScreeningService.ParcelableCallResponse callScreeningResponse,
-                boolean isResponseFromSystemDialer,
+        public void onCallFilteringCompleted(String callId,
+                Connection.CallFilteringCompletionInfo completionInfo,
                 Session.Info sessionInfo) {
             Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED);
             try {
                 SomeArgs args = SomeArgs.obtain();
                 args.arg1 = callId;
-                args.arg2 = isBlocked;
-                args.arg3 = isInContacts;
-                args.arg4 = callScreeningResponse;
-                args.arg5 = isResponseFromSystemDialer;
-                args.arg6 = Log.createSubsession();
+                args.arg2 = completionInfo;
+                args.arg3 = Log.createSubsession();
                 mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget();
             } finally {
                 Log.endSession();
@@ -1441,16 +1437,12 @@
                 case MSG_ON_CALL_FILTERING_COMPLETED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        Log.continueSession((Session) args.arg6,
+                        Log.continueSession((Session) args.arg3,
                                 SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED);
                         String callId = (String) args.arg1;
-                        boolean isBlocked = (boolean) args.arg2;
-                        boolean isInContacts = (boolean) args.arg3;
-                        CallScreeningService.ParcelableCallResponse callScreeningResponse =
-                                (CallScreeningService.ParcelableCallResponse) args.arg4;
-                        boolean isResponseFromSystemDialer = (boolean) args.arg5;
-                        onCallFilteringCompleted(callId, isBlocked, isInContacts,
-                                callScreeningResponse, isResponseFromSystemDialer);
+                        Connection.CallFilteringCompletionInfo completionInfo =
+                                (Connection.CallFilteringCompletionInfo) args.arg2;
+                        onCallFilteringCompleted(callId, completionInfo);
                     } finally {
                         args.recycle();
                         Log.endSession();
@@ -2466,16 +2458,12 @@
         }
     }
 
-    private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
-            CallScreeningService.ParcelableCallResponse callScreeningResponse,
-            boolean isResponseFromSystemDialer) {
-        Log.i(this, "onCallFilteringCompleted(%s, %b, %b, %s, %b)", callId,
-                isBlocked, isInContacts, callScreeningResponse, isResponseFromSystemDialer);
+    private void onCallFilteringCompleted(String callId, Connection.CallFilteringCompletionInfo
+            callFilteringCompletionInfo) {
+        Log.i(this, "onCallFilteringCompleted(%s, %s)", callId, callFilteringCompletionInfo);
         Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted");
         if (connection != null) {
-            connection.onCallFilteringCompleted(isBlocked, isInContacts,
-                    callScreeningResponse == null ? null : callScreeningResponse.toCallResponse(),
-                    isResponseFromSystemDialer);
+            connection.onCallFilteringCompleted(callFilteringCompletionInfo);
         }
     }
 
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 6c6097a..7a6fddb 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -1202,27 +1202,18 @@
     /**
      * Notifies this {@link RemoteConnection} that call filtering has completed, as well as
      * the results of a contacts lookup for the remote party.
-     * @param isBlocked Whether call filtering indicates that the call should be blocked
-     * @param isInContacts Whether the remote party is in the user's contacts
-     * @param callScreeningResponse The response that was returned from the
-     *                              {@link CallScreeningService} that handled this call. If no
-     *                              response was received from a call screening service,
-     *                              this will be {@code null}.
-     * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the
-     *                                  system dialer. If {@code callScreeningResponse} is
-     *                                  {@code null}, this will be {@code false}.
+     *
+     * @param completionInfo Info provided by Telecom on the results of call filtering.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_CONTACTS)
-    public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
-            @Nullable CallScreeningService.CallResponse callScreeningResponse,
-            boolean isResponseFromSystemDialer) {
+    public void onCallFilteringCompleted(
+            @NonNull Connection.CallFilteringCompletionInfo completionInfo) {
         Log.startSession("RC.oCFC", getActiveOwnerInfo());
         try {
             if (mConnected) {
-                mConnectionService.onCallFilteringCompleted(mConnectionId, isBlocked, isInContacts,
-                        callScreeningResponse.toParcelable(), isResponseFromSystemDialer,
+                mConnectionService.onCallFilteringCompleted(mConnectionId, completionInfo,
                         null /*Session.Info*/);
             }
         } catch (RemoteException ignored) {
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 7599e18..d72f8aa 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -20,7 +20,7 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.telecom.CallAudioState;
-import android.telecom.CallScreeningService;
+import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
 import android.telecom.Logging.Session;
 import android.telecom.PhoneAccountHandle;
@@ -119,9 +119,9 @@
 
     void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo);
 
-    void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
-            in CallScreeningService.ParcelableCallResponse callScreeningResponse,
-            boolean isResponseFromSystemDialer, in Session.Info sessionInfo);
+    void onCallFilteringCompleted(String callId,
+            in Connection.CallFilteringCompletionInfo completionInfo,
+            in Session.Info sessionInfo);
 
     void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo);
 
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index c6757fb..4ae11b8 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -111,6 +111,7 @@
     public @interface NetworkType {
     }
 
+    // TODO(b/180542000): remove and replace references with @ApnSetting.ApnType
     @IntDef(flag = true, prefix = {"TYPE_"}, value = {
             ApnSetting.TYPE_DEFAULT,
             ApnSetting.TYPE_MMS,
@@ -124,6 +125,7 @@
             ApnSetting.TYPE_EMERGENCY,
             ApnSetting.TYPE_MCX,
             ApnSetting.TYPE_XCAP,
+            // ApnSetting.TYPE_ENTERPRISE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApnType {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8c68d85..4b57fcb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1717,8 +1717,14 @@
      * Configs used for APN setup.
      */
     public static final class Apn {
-        /** Prefix of all Apn.KEY_* constants. */
-        private static final String KEY_PREFIX = "apn.";
+        /**
+         * Prefix of all Apn.KEY_* constants.
+         *
+         * @deprecated Since KEY_PREFIX is unnecessary to public, it will modify to private
+         * next android generation.
+         */
+        @Deprecated
+        public static final String KEY_PREFIX = "apn.";
 
         /** IPv4 internet protocol */
         public static final String PROTOCOL_IPV4 = "IP";
@@ -5321,7 +5327,7 @@
         sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY,
                 new String[0]);
         sDefaults.putStringArray(KEY_APN_PRIORITY_STRING_ARRAY, new String[] {
-                "default:0", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2",
+                "default:0", "enterprise:1", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2",
                 "ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3"
         });
         sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index 99a77ae5..c8ed82c 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -917,6 +917,10 @@
     public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB;
     /** Data call fail due to the slice not being allowed for the data call. */
     public static final int SLICE_REJECTED = 0x8CC;
+    /** No matching rule available for the request, and match-all rule is not allowed for it. */
+    public static final int MATCH_ALL_RULE_NOT_ALLOWED = 0x8CD;
+    /** If connection failed for all matching URSP rules. */
+    public static final int ALL_MATCHING_RULES_FAILED = 0x8CE;
 
     //IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2).
 
diff --git a/telephony/java/android/telephony/RadioInterfaceCapabilities.java b/telephony/java/android/telephony/RadioInterfaceCapabilities.java
new file mode 100644
index 0000000..7c7eb9f
--- /dev/null
+++ b/telephony/java/android/telephony/RadioInterfaceCapabilities.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.util.ArraySet;
+
+/**
+ * Contains the set of supported capabilities that the Radio Interface supports on this device.
+ *
+ * @hide
+ */
+public class RadioInterfaceCapabilities {
+
+    private final ArraySet<String> mSupportedCapabilities;
+
+
+    public RadioInterfaceCapabilities() {
+        mSupportedCapabilities = new ArraySet<>();
+    }
+
+    /**
+     * Marks a capability as supported
+     *
+     * @param capabilityName the name of the capability
+     */
+    public void addSupportedCapability(
+            @TelephonyManager.RadioInterfaceCapability String capabilityName) {
+        mSupportedCapabilities.add(capabilityName);
+    }
+
+    /**
+     * Whether the capability is supported
+     *
+     * @param capabilityName the name of the capability
+     */
+    public boolean isSupported(String capabilityName) {
+        return mSupportedCapabilities.contains(capabilityName);
+    }
+}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 6c013df..4926687 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2649,6 +2649,37 @@
      */
     public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
             Bundle configOverrides, PendingIntent sentIntent) {
+        sendMultimediaMessage(context, contentUri, locationUrl, configOverrides, sentIntent,
+                0L /* messageId */);
+    }
+
+    /**
+     * Send an MMS message
+     *
+     * Same as {@link #sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
+     *           Bundle configOverrides, PendingIntent sentIntent)}, but adds an optional messageId.
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail sending the MMS message because no
+     * suitable default subscription could be found. In this case, if {@code sentIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+     * conditions where this operation may fail.
+     * </p>
+     *
+     * @param context application context
+     * @param contentUri the content Uri from which the message pdu will be read
+     * @param locationUrl the optional location url where message should be sent to
+     * @param configOverrides the carrier-specific messaging configuration values to override for
+     *  sending the message.
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed
+     * @param messageId an id that uniquely identifies the message requested to be sent.
+     * Used for logging and diagnostics purposes. The id may be 0.
+     * @throws IllegalArgumentException if contentUri is empty
+     */
+    public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri,
+            @Nullable String locationUrl, @Nullable Bundle configOverrides,
+            @Nullable PendingIntent sentIntent, long messageId) {
         if (contentUri == null) {
             throw new IllegalArgumentException("Uri contentUri null");
         }
@@ -2658,7 +2689,7 @@
                 @Override
                 public void onSuccess(int subId) {
                     m.sendMultimediaMessage(subId, contentUri, locationUrl, configOverrides,
-                            sentIntent, 0L /* messageId */);
+                            sentIntent, messageId);
                 }
 
                 @Override
@@ -2692,6 +2723,39 @@
      */
     public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
             Bundle configOverrides, PendingIntent downloadedIntent) {
+        downloadMultimediaMessage(context, locationUrl, contentUri, configOverrides,
+                downloadedIntent, 0L /* messageId */);
+    }
+
+    /**
+     * Download an MMS message from carrier by a given location URL
+     *
+     * Same as {@link #downloadMultimediaMessage(Context context, String locationUrl,
+     *      Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent)},
+     *      but adds an optional messageId.
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail downloading the MMS message because no
+     * suitable default subscription could be found. In this case, if {@code downloadedIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+     * conditions where this operation may fail.
+     * </p>
+     *
+     * @param context application context
+     * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
+     *  from the MMS WAP push notification
+     * @param contentUri the content uri to which the downloaded pdu will be written
+     * @param configOverrides the carrier-specific messaging configuration values to override for
+     *  downloading the message.
+     * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is downloaded, or the download is failed
+     * @param messageId an id that uniquely identifies the message requested to be downloaded.
+     * Used for logging and diagnostics purposes. The id may be 0.
+     * @throws IllegalArgumentException if locationUrl or contentUri is empty
+     */
+    public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl,
+            @NonNull Uri contentUri, @Nullable Bundle configOverrides,
+            @Nullable PendingIntent downloadedIntent, long messageId) {
         if (TextUtils.isEmpty(locationUrl)) {
             throw new IllegalArgumentException("Empty MMS location URL");
         }
@@ -2704,7 +2768,7 @@
                 @Override
                 public void onSuccess(int subId) {
                     m.downloadMultimediaMessage(subId, locationUrl, contentUri, configOverrides,
-                            downloadedIntent, 0L /* messageId */);
+                            downloadedIntent, messageId);
                 }
 
                 @Override
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f64f428..431fc48 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -131,10 +131,12 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -8497,11 +8499,6 @@
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
-     * <p>
-     * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
-     * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
-     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
-     * setPreferredNetworkTypesBitmap is used instead.
      *
      * @param subId the id of the subscription to set the preferred network type for.
      * @param networkType the preferred network type
@@ -8535,11 +8532,6 @@
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
-     * <p>
-     * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
-     * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
-     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
-     * setPreferredNetworkTypesBitmap is used instead.
      *
      * @param networkTypeBitmask The bitmask of preferred network types.
      * @return true on success; false on any failure.
@@ -8566,11 +8558,6 @@
      * Set the allowed network types of the device. This is for carrier or privileged apps to
      * enable/disable certain network types on the device. The user preferred network types should
      * be set through {@link #setPreferredNetworkTypeBitmask}.
-     * <p>
-     * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
-     * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
-     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
-     * setPreferredNetworkTypesBitmap is used instead.
      *
      * @param allowedNetworkTypes The bitmask of allowed network types.
      * @return true on success; false on any failure.
@@ -8655,12 +8642,12 @@
      * {@link #ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}
      * </ol>
      * This API will result in allowing an intersection of allowed network types for all reasons,
-     * including the configuration done through other reasons.
-     * <p>
-     * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
-     * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
-     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
-     * setPreferredNetworkTypesBitmap is used instead.
+     * including the configuration done through {@link setAllowedNetworkTypes}.
+     * While this API and {@link setAllowedNetworkTypes} is controlling allowed network types
+     * on device, user preference will still be set through {@link #setPreferredNetworkTypeBitmask}.
+     * Thus resultant network type configured on modem will be an intersection of the network types
+     * from setAllowedNetworkTypesForReason, {@link setAllowedNetworkTypes}
+     * and {@link #setPreferredNetworkTypeBitmask}.
      *
      * @param reason the reason the allowed network type change is taking place
      * @param allowedNetworkTypes The bitmask of allowed network types.
@@ -14894,24 +14881,10 @@
     public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE =
             "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
 
-    /**
-     * Indicates whether {@link #setPreferredNetworkType}, {@link
-     * #setPreferredNetworkTypeBitmask}, {@link #setAllowedNetworkTypes} and
-     * {@link #setAllowedNetworkTypesForReason} rely on
-     * setAllowedNetworkTypesBitmap instead of setPreferredNetworkTypesBitmap on the radio
-     * interface.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED =
-            "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
-
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @StringDef(prefix = "CAPABILITY_", value = {
             CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
-            CAPABILITY_ALLOWED_NETWORK_TYPES_USED,
     })
     public @interface RadioInterfaceCapability {}
 
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index d58fa912..b503733 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -28,8 +28,6 @@
 import android.os.Parcelable;
 import android.provider.Telephony;
 import android.provider.Telephony.Carriers;
-import android.telephony.Annotation;
-import android.telephony.Annotation.ApnType;
 import android.telephony.Annotation.NetworkType;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
@@ -116,6 +114,31 @@
     public static final int TYPE_MCX = ApnTypes.MCX;
     /** APN type for XCAP. */
     public static final int TYPE_XCAP = ApnTypes.XCAP;
+    /**
+     * APN type for ENTERPRISE.
+     * @hide
+     */
+    public static final int TYPE_ENTERPRISE = TYPE_XCAP << 1;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"TYPE_"}, value = {
+            TYPE_DEFAULT,
+            TYPE_MMS,
+            TYPE_SUPL,
+            TYPE_DUN,
+            TYPE_HIPRI,
+            TYPE_FOTA,
+            TYPE_IMS,
+            TYPE_CBS,
+            TYPE_IA,
+            TYPE_EMERGENCY,
+            TYPE_MCX,
+            TYPE_XCAP,
+            TYPE_ENTERPRISE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApnType {
+    }
 
     // Possible values for authentication types.
     /** No authentication type. */
@@ -151,6 +174,7 @@
             TYPE_MMS_STRING,
             TYPE_SUPL_STRING,
             TYPE_XCAP_STRING,
+            TYPE_ENTERPRISE_STRING,
     }, prefix = "TYPE_", suffix = "_STRING")
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApnTypeString {}
@@ -291,6 +315,12 @@
     @SystemApi
     public static final String TYPE_XCAP_STRING = "xcap";
 
+    /**
+     * APN type for ENTERPRISE traffic.
+     * @hide
+     */
+    public static final String TYPE_ENTERPRISE_STRING = "enterprise";
+
 
     /** @hide */
     @IntDef(prefix = { "AUTH_TYPE_" }, value = {
@@ -370,6 +400,7 @@
         APN_TYPE_STRING_MAP.put(TYPE_EMERGENCY_STRING, TYPE_EMERGENCY);
         APN_TYPE_STRING_MAP.put(TYPE_MCX_STRING, TYPE_MCX);
         APN_TYPE_STRING_MAP.put(TYPE_XCAP_STRING, TYPE_XCAP);
+        APN_TYPE_STRING_MAP.put(TYPE_ENTERPRISE_STRING, TYPE_ENTERPRISE);
 
         APN_TYPE_INT_MAP = new ArrayMap<>();
         APN_TYPE_INT_MAP.put(TYPE_DEFAULT, TYPE_DEFAULT_STRING);
@@ -384,6 +415,7 @@
         APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, TYPE_EMERGENCY_STRING);
         APN_TYPE_INT_MAP.put(TYPE_MCX, TYPE_MCX_STRING);
         APN_TYPE_INT_MAP.put(TYPE_XCAP, TYPE_XCAP_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_ENTERPRISE, TYPE_ENTERPRISE_STRING);
 
         PROTOCOL_STRING_MAP = new ArrayMap<>();
         PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
@@ -1490,7 +1522,7 @@
      * @hide
      */
     @SystemApi
-    public static @NonNull @ApnTypeString String getApnTypeString(@Annotation.ApnType int apnType) {
+    public static @NonNull @ApnTypeString String getApnTypeString(@ApnType int apnType) {
         if (apnType == TYPE_ALL) {
             return "*";
         }
@@ -1503,7 +1535,7 @@
      * when provided with an invalid int for compatibility purposes.
      * @hide
      */
-    public static @NonNull String getApnTypeStringInternal(@Annotation.ApnType int apnType) {
+    public static @NonNull String getApnTypeStringInternal(@ApnType int apnType) {
         String result = getApnTypeString(apnType);
         return TextUtils.isEmpty(result) ? "Unknown" : result;
     }
@@ -1517,7 +1549,7 @@
      * @hide
      */
     @SystemApi
-    public static @Annotation.ApnType int getApnTypeInt(@NonNull @ApnTypeString String apnType) {
+    public static @ApnType int getApnTypeInt(@NonNull @ApnTypeString String apnType) {
         return APN_TYPE_STRING_MAP.getOrDefault(apnType.toLowerCase(), 0);
     }
 
@@ -2162,7 +2194,7 @@
         public ApnSetting build() {
             if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI
                     | TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX
-                    | TYPE_XCAP)) == 0
+                    | TYPE_XCAP | TYPE_ENTERPRISE)) == 0
                 || TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) {
                 return null;
             }
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index bd4bf07..a764229 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -138,6 +138,7 @@
     private final Qos mDefaultQos;
     private final List<QosBearerSession> mQosBearerSessions;
     private final SliceInfo mSliceInfo;
+    private final List<TrafficDescriptor> mTrafficDescriptors;
 
     /**
      * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -189,6 +190,7 @@
         mDefaultQos = null;
         mQosBearerSessions = new ArrayList<>();
         mSliceInfo = null;
+        mTrafficDescriptors = new ArrayList<>();
     }
 
     private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@@ -198,7 +200,7 @@
             @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
             @HandoverFailureMode int handoverFailureMode, int pduSessionId,
             @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions,
-            @Nullable SliceInfo sliceInfo) {
+            @Nullable SliceInfo sliceInfo, @Nullable List<TrafficDescriptor> trafficDescriptors) {
         mCause = cause;
         mSuggestedRetryTime = suggestedRetryTime;
         mId = id;
@@ -219,8 +221,11 @@
         mHandoverFailureMode = handoverFailureMode;
         mPduSessionId = pduSessionId;
         mDefaultQos = defaultQos;
-        mQosBearerSessions = qosBearerSessions;
+        mQosBearerSessions = (qosBearerSessions == null)
+                ? new ArrayList<>() : new ArrayList<>(qosBearerSessions);
         mSliceInfo = sliceInfo;
+        mTrafficDescriptors = (trafficDescriptors == null)
+                ? new ArrayList<>() : new ArrayList<>(trafficDescriptors);
     }
 
     /** @hide */
@@ -249,6 +254,8 @@
         mQosBearerSessions = new ArrayList<>();
         source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader());
         mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader());
+        mTrafficDescriptors = new ArrayList<>();
+        source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader());
     }
 
     /**
@@ -381,7 +388,6 @@
      *
      * @hide
      */
-
     @Nullable
     public Qos getDefaultQos() {
         return mDefaultQos;
@@ -406,6 +412,14 @@
         return mSliceInfo;
     }
 
+    /**
+     * @return The traffic descriptors related to this data connection.
+     */
+    @NonNull
+    public List<TrafficDescriptor> getTrafficDescriptors() {
+        return mTrafficDescriptors;
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -429,6 +443,7 @@
            .append(" defaultQos=").append(mDefaultQos)
            .append(" qosBearerSessions=").append(mQosBearerSessions)
            .append(" sliceInfo=").append(mSliceInfo)
+           .append(" trafficDescriptors=").append(mTrafficDescriptors)
            .append("}");
         return sb.toString();
     }
@@ -443,15 +458,22 @@
 
         DataCallResponse other = (DataCallResponse) o;
 
-        final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) ?
-                mDefaultQos == other.mDefaultQos :
-                mDefaultQos.equals(other.mDefaultQos);
+        final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null)
+                ? mDefaultQos == other.mDefaultQos
+                : mDefaultQos.equals(other.mDefaultQos);
 
-        final boolean isQosBearerSessionsSame = (mQosBearerSessions == null || mQosBearerSessions == null) ?
-                mQosBearerSessions == other.mQosBearerSessions :
-                mQosBearerSessions.size() == other.mQosBearerSessions.size()
+        final boolean isQosBearerSessionsSame =
+                (mQosBearerSessions == null || other.mQosBearerSessions == null)
+                ? mQosBearerSessions == other.mQosBearerSessions
+                : mQosBearerSessions.size() == other.mQosBearerSessions.size()
                 && mQosBearerSessions.containsAll(other.mQosBearerSessions);
 
+        final boolean isTrafficDescriptorsSame =
+                (mTrafficDescriptors == null || other.mTrafficDescriptors == null)
+                ? mTrafficDescriptors == other.mTrafficDescriptors
+                : mTrafficDescriptors.size() == other.mTrafficDescriptors.size()
+                && mTrafficDescriptors.containsAll(other.mTrafficDescriptors);
+
         return mCause == other.mCause
                 && mSuggestedRetryTime == other.mSuggestedRetryTime
                 && mId == other.mId
@@ -473,7 +495,8 @@
                 && mPduSessionId == other.mPduSessionId
                 && isQosSame
                 && isQosBearerSessionsSame
-                && Objects.equals(mSliceInfo, other.mSliceInfo);
+                && Objects.equals(mSliceInfo, other.mSliceInfo)
+                && isTrafficDescriptorsSame;
     }
 
     @Override
@@ -481,7 +504,7 @@
         return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
                 mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
                 mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
-                mQosBearerSessions, mSliceInfo);
+                mQosBearerSessions, mSliceInfo, mTrafficDescriptors);
     }
 
     @Override
@@ -517,6 +540,7 @@
         }
         dest.writeList(mQosBearerSessions);
         dest.writeParcelable(mSliceInfo, flags);
+        dest.writeList(mTrafficDescriptors);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -602,6 +626,8 @@
 
         private SliceInfo mSliceInfo;
 
+        private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
+
         /**
          * Default constructor for Builder.
          */
@@ -841,6 +867,24 @@
         }
 
         /**
+         * The traffic descriptors for this data connection, as defined in 3GPP TS 24.526
+         * Section 5.2. They are used for URSP traffic matching as described in 3GPP TS 24.526
+         * Section 4.2.2. They includes an optional DNN, which, if present, must be used for traffic
+         * matching; it does not specify the end point to be used for the data call. The end point
+         * is specified by {@link DataProfile}, which must be used as the end point if one is not
+         * specified through URSP rules.
+         *
+         * @param trafficDescriptors the traffic descriptors for the data call.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setTrafficDescriptors(
+                @NonNull List<TrafficDescriptor> trafficDescriptors) {
+            mTrafficDescriptors = trafficDescriptors;
+            return this;
+        }
+
+        /**
          * Build the DataCallResponse.
          *
          * @return the DataCallResponse object.
@@ -849,7 +893,7 @@
             return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
                     mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
                     mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
-                    mDefaultQos, mQosBearerSessions, mSliceInfo);
+                    mDefaultQos, mQosBearerSessions, mSliceInfo, mTrafficDescriptors);
         }
     }
 }
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 484c318..f5f29c6 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -200,6 +200,17 @@
          *        handover is occurring from EPDG to 5G.  If the slice passed is rejected, then
          *        {@link DataCallResponse#getCause()} is
          *        {@link android.telephony.DataFailCause#SLICE_REJECTED}.
+         * @param trafficDescriptor {@link TrafficDescriptor} for which data connection needs to be
+         *        established. It is used for URSP traffic matching as described in 3GPP TS 24.526
+         *        Section 4.2.2. It includes an optional DNN which, if present, must be used for
+         *        traffic matching; it does not specify the end point to be used for the data call.
+         * @param matchAllRuleAllowed Indicates if using default match-all URSP rule for this
+         *        request is allowed. If false, this request must not use the match-all URSP rule
+         *        and if a non-match-all rule is not found (or if URSP rules are not available) then
+         *        {@link DataCallResponse#getCause()} is
+         *        {@link android.telephony.DataFailCause#MATCH_ALL_RULE_NOT_ALLOWED}. This is needed
+         *        as some requests need to have a hard failure if the intention cannot be met,
+         *        for example, a zero-rating slice.
          * @param callback The result callback for this request.
          */
         public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile,
@@ -207,6 +218,7 @@
                 @SetupDataReason int reason,
                 @Nullable LinkProperties linkProperties,
                 @IntRange(from = 0, to = 15) int pduSessionId, @Nullable SliceInfo sliceInfo,
+                @Nullable TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
                 @NonNull DataServiceCallback callback) {
             /* Call the old version since the new version isn't supported */
             setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason,
@@ -403,10 +415,13 @@
         public final LinkProperties linkProperties;
         public final int pduSessionId;
         public final SliceInfo sliceInfo;
+        public final TrafficDescriptor trafficDescriptor;
+        public final boolean matchAllRuleAllowed;
         public final IDataServiceCallback callback;
         SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
-                             boolean allowRoaming, int reason, LinkProperties linkProperties,
-                             int pduSessionId, SliceInfo sliceInfo, IDataServiceCallback callback) {
+                boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
+                SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+                boolean matchAllRuleAllowed, IDataServiceCallback callback) {
             this.accessNetworkType = accessNetworkType;
             this.dataProfile = dataProfile;
             this.isRoaming = isRoaming;
@@ -415,6 +430,8 @@
             this.reason = reason;
             this.pduSessionId = pduSessionId;
             this.sliceInfo = sliceInfo;
+            this.trafficDescriptor = trafficDescriptor;
+            this.matchAllRuleAllowed = matchAllRuleAllowed;
             this.callback = callback;
         }
     }
@@ -525,7 +542,8 @@
                             setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
                             setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
                             setupDataCallRequest.linkProperties, setupDataCallRequest.pduSessionId,
-                            setupDataCallRequest.sliceInfo,
+                            setupDataCallRequest.sliceInfo, setupDataCallRequest.trafficDescriptor,
+                            setupDataCallRequest.matchAllRuleAllowed,
                             (setupDataCallRequest.callback != null)
                                     ? new DataServiceCallback(setupDataCallRequest.callback)
                                     : null);
@@ -690,11 +708,12 @@
         public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile,
                 boolean isRoaming, boolean allowRoaming, int reason,
                 LinkProperties linkProperties, int pduSessionId, SliceInfo sliceInfo,
+                TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
                 IDataServiceCallback callback) {
             mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0,
                     new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
                             allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
-                            callback))
+                            trafficDescriptor, matchAllRuleAllowed, callback))
                     .sendToTarget();
         }
 
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index e0b9a1a..81f5fd3 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -20,6 +20,7 @@
 import android.telephony.data.DataProfile;
 import android.telephony.data.IDataServiceCallback;
 import android.telephony.data.SliceInfo;
+import android.telephony.data.TrafficDescriptor;
 
 /**
  * {@hide}
@@ -30,7 +31,9 @@
     void removeDataServiceProvider(int slotId);
     void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
                        boolean allowRoaming, int reason, in LinkProperties linkProperties,
-                       int pduSessionId, in SliceInfo sliceInfo, IDataServiceCallback callback);
+                       int pduSessionId, in SliceInfo sliceInfo,
+                       in TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
+                       IDataServiceCallback callback);
     void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback);
     void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming,
                              IDataServiceCallback callback);
diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.aidl b/telephony/java/android/telephony/data/TrafficDescriptor.aidl
new file mode 100644
index 0000000..a9c7604
--- /dev/null
+++ b/telephony/java/android/telephony/data/TrafficDescriptor.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** @hide */
+package android.telephony.data;
+
+parcelable TrafficDescriptor;
diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java
new file mode 100644
index 0000000..480379d
--- /dev/null
+++ b/telephony/java/android/telephony/data/TrafficDescriptor.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for URSP traffic
+ * matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an optional DNN, which,
+ * if present, must be used for traffic matching; it does not specify the end point to be used for
+ * the data call.
+ * @hide
+ */
+@SystemApi
+public final class TrafficDescriptor implements Parcelable {
+    private final String mDnn;
+    private final String mOsAppId;
+
+    private TrafficDescriptor(@NonNull Parcel in) {
+        mDnn = in.readString();
+        mOsAppId = in.readString();
+    }
+
+    /**
+     * Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2
+     * @param dnn optional DNN, which must be used for traffic matching, if present
+     * @param osAppId OsId + osAppId of the traffic descriptor
+     */
+    public TrafficDescriptor(@Nullable String dnn, @Nullable String osAppId) {
+        mDnn = dnn;
+        mOsAppId = osAppId;
+    }
+
+    /**
+     * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003.
+     * @return the DNN of this traffic descriptor.
+     */
+    public @Nullable String getDnn() {
+        return mDnn;
+    }
+
+    /**
+     * OsAppId represents the OsId + OsAppId as defined in 3GPP TS 24.526 Section 5.2.
+     * @return the OS App ID of this traffic descriptor.
+     */
+    public @Nullable String getOsAppId() {
+        return mOsAppId;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull @Override
+    public String toString() {
+        return "TrafficDescriptor={mDnn=" + mDnn + ", mOsAppId=" + mOsAppId + "}";
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mDnn);
+        dest.writeString(mOsAppId);
+    }
+
+    public static final @NonNull Parcelable.Creator<TrafficDescriptor> CREATOR =
+            new Parcelable.Creator<TrafficDescriptor>() {
+                @Override
+                public @NonNull TrafficDescriptor createFromParcel(@NonNull Parcel source) {
+                    return new TrafficDescriptor(source);
+                }
+
+                @Override
+                public @NonNull TrafficDescriptor[] newArray(int size) {
+                    return new TrafficDescriptor[size];
+                }
+            };
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        TrafficDescriptor that = (TrafficDescriptor) o;
+        return Objects.equals(mDnn, that.mDnn) && Objects.equals(mOsAppId, that.mOsAppId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDnn, mOsAppId);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 5f8e93d..8ad40ed 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -57,10 +57,10 @@
                 } else if (listener != null && mListener == null) {
                     mListener = listener;
                 } else {
-                    // Fail fast here instead of silently overwriting the listener to another
-                    // listener due to another connection connecting.
-                    throw new IllegalStateException("ImsEcbmImplBase: Listener already set by "
-                            + "another connection.");
+                    // Warn that the listener is being replaced while active
+                    Log.w(TAG, "setListener is being called when there is already an active "
+                            + "listener");
+                    mListener = listener;
                 }
             }
         }
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index 8e961ac..ec1c7b3 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -62,10 +62,10 @@
                 } else if (listener != null && mListener == null) {
                     mListener = listener;
                 } else {
-                    // Fail fast here instead of silently overwriting the listener to another
-                    // listener due to another connection connecting.
-                    throw new IllegalStateException("ImsMultiEndpointImplBase: Listener already"
-                            + " set by another connection.");
+                    // Warn that the listener is being replaced while active
+                    Log.w(TAG, "setListener is being called when there is already an active "
+                            + "listener");
+                    mListener = listener;
                 }
             }
         }
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index 83b89aa..eb3e8ed 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -224,11 +224,10 @@
                 } else if (listener != null && mUtListener == null) {
                     mUtListener = new ImsUtListener(listener);
                 } else {
-                    // This is a limitation of the current API surface, there can only be one
-                    // listener connected. Fail fast instead of silently overwriting the other
-                    // listener.
-                    throw new IllegalStateException("ImsUtImplBase#setListener: listener already "
-                            + "set by another connected interface!");
+                    // Warn that the listener is being replaced while active
+                    Log.w(TAG, "setListener is being called when there is already an active "
+                            + "listener");
+                    mUtListener = new ImsUtListener(listener);
                 }
             }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 5c25524..0048d53 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2055,6 +2055,11 @@
     int setImsProvisioningString(int subId, int key, String value);
 
     /**
+     * Start emergency callback mode for testing.
+     */
+    void startEmergencyCallbackMode();
+
+    /**
      * Update Emergency Number List for Test Mode.
      */
     void updateEmergencyNumberListTestMode(int action, in EmergencyNumber num);
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 151187c..3a99f0e0 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -126,6 +126,7 @@
      * connections.<br/>
      * APN_TYPE_ALL is a special type to indicate that this APN entry can
      * service all data connections.
+     * TODO: remove these and use the reference to ApnSetting.TYPE_XXX_STRING instead
      */
     public static final String APN_TYPE_ALL = ApnSetting.TYPE_ALL_STRING;
     /** APN type for default data traffic */
@@ -153,20 +154,8 @@
     public static final String APN_TYPE_MCX = ApnSetting.TYPE_MCX_STRING;
     /** APN type for XCAP */
     public static final String APN_TYPE_XCAP = ApnSetting.TYPE_XCAP_STRING;
-    /** Array of all APN types */
-    public static final String[] APN_TYPES = {APN_TYPE_DEFAULT,
-            APN_TYPE_MMS,
-            APN_TYPE_SUPL,
-            APN_TYPE_DUN,
-            APN_TYPE_HIPRI,
-            APN_TYPE_FOTA,
-            APN_TYPE_IMS,
-            APN_TYPE_CBS,
-            APN_TYPE_IA,
-            APN_TYPE_EMERGENCY,
-            APN_TYPE_MCX,
-            APN_TYPE_XCAP,
-    };
+    // /** APN type for enterprise */
+    // public static final String APN_TYPE_ENTERPRISE = ApnSetting.TYPE_ENTERPRISE_STRING;
 
     public static final int RIL_CARD_MAX_APPS    = 8;
 
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 0b7a398..9bd639b 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -49,6 +49,12 @@
 
     compile_dex: true,
     default_to_stubs: true,
+
+    // Additional hiddenapi annotations are provided in a separate module.
+    // TODO(b/180295980) - investigate whether this can be removed
+    hiddenapi_additional_annotations: [
+        "android.test.base-hiddenapi-annotations",
+    ],
 }
 
 // Build the android.test.base_static library
@@ -91,8 +97,9 @@
 // ===============================================
 // This contains the android.test classes from android.test.base plus
 // the com.android.internal.util.Predicate[s] classes. This is only
-// intended for inclusion in android.test.legacy and must not be used
-// elsewhere.
+// intended for inclusion in android.test.legacy and in
+// android.test.base-hiddenapi-annotations to avoid a dependency cycle and must
+// not be used elsewhere.
 java_library_static {
     name: "android.test.base-minus-junit",
 
diff --git a/test-base/hiddenapi/Android.bp b/test-base/hiddenapi/Android.bp
index d4f52d0..1466590 100644
--- a/test-base/hiddenapi/Android.bp
+++ b/test-base/hiddenapi/Android.bp
@@ -14,11 +14,6 @@
 // limitations under the License.
 //
 
-// Provided solely to contribute information about which hidden parts of the android.test.base
-// library are used by apps. The source files are stubs of the actual files in ../src which use the
-// UnsupportedAppUsage annotation to tag those methods that are accessible via the hiddenapi.
-// Relies on the convention that modules with name <x>-hiddenapi provide hiddenapi information for
-// module <x> that is on the bootclasspath.
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -28,14 +23,20 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+// Provided solely to contribute information about which hidden parts of the android.test.base
+// library are used by apps. The source files are stubs of the actual files in ../src which use the
+// UnsupportedAppUsage annotation to tag those methods that are accessible via the hiddenapi.
 java_library {
-    name: "android.test.base-hiddenapi",
+    name: "android.test.base-hiddenapi-annotations",
     compile_dex: true,
 
     srcs: ["src/**/*.java"],
 
     libs: [
-        "android.test.base",
+        // Use this instead of `android.test.base` to avoid a dependency cycle
+        // as `android.test.base` depends on this.
+        "android.test.base-minus-junit",
+        "junit",
         "unsupportedappusage",
     ],
 }
diff --git a/tests/BatteryStatsPerfTest/Android.bp b/tests/BatteryStatsPerfTest/Android.bp
index 58ccec7..5233a5b 100644
--- a/tests/BatteryStatsPerfTest/Android.bp
+++ b/tests/BatteryStatsPerfTest/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "BatteryStatsPerfTests",
     srcs: ["src/**/*.java"],
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 7d29cdd..a8b7b05 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -21,8 +21,6 @@
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_WINDOW_NAME
 
-const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider"
-const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
 const val WALLPAPER_TITLE = "Wallpaper"
 
 fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() {
@@ -64,9 +62,9 @@
 
 fun FlickerTestParameter.wallpaperWindowBecomesInvisible() {
     assertWm {
-        this.showsBelowAppWindow("Wallpaper")
+        this.showsBelowAppWindow(WALLPAPER_TITLE)
             .then()
-            .hidesBelowAppWindow("Wallpaper")
+            .hidesBelowAppWindow(WALLPAPER_TITLE)
     }
 }
 
@@ -103,19 +101,19 @@
     if (allStates) {
         assertLayers {
             if (startingBounds == endingBounds) {
-                this.coversAtLeastRegion(startingBounds)
+                this.coversAtLeast(startingBounds)
             } else {
-                this.coversAtLeastRegion(startingBounds)
+                this.coversAtLeast(startingBounds)
                     .then()
-                    .coversAtLeastRegion(endingBounds)
+                    .coversAtLeast(endingBounds)
             }
         }
     } else {
         assertLayersStart {
-            this.coversAtLeastRegion(startingBounds)
+            this.coversAtLeast(startingBounds)
         }
         assertLayersEnd {
-            this.coversAtLeastRegion(endingBounds)
+            this.coversAtLeast(endingBounds)
         }
     }
 }
@@ -124,15 +122,15 @@
 fun FlickerTestParameter.navBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
     if (rotatesScreen) {
         assertLayers {
-            this.showsLayer(NAV_BAR_LAYER_NAME)
+            this.isVisible(NAV_BAR_LAYER_NAME)
                 .then()
-                .hidesLayer(NAV_BAR_LAYER_NAME)
+                .isInvisible(NAV_BAR_LAYER_NAME)
                 .then()
-                .showsLayer(NAV_BAR_LAYER_NAME)
+                .isVisible(NAV_BAR_LAYER_NAME)
         }
     } else {
         assertLayers {
-            this.showsLayer(NAV_BAR_LAYER_NAME)
+            this.isVisible(NAV_BAR_LAYER_NAME)
         }
     }
 }
@@ -141,15 +139,15 @@
 fun FlickerTestParameter.statusBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
     if (rotatesScreen) {
         assertLayers {
-            this.showsLayer(STATUS_BAR_WINDOW_NAME)
+            this.isVisible(STATUS_BAR_WINDOW_NAME)
                 .then()
-            hidesLayer(STATUS_BAR_WINDOW_NAME)
+                .isInvisible(STATUS_BAR_WINDOW_NAME)
                 .then()
-                .showsLayer(STATUS_BAR_WINDOW_NAME)
+                .isVisible(STATUS_BAR_WINDOW_NAME)
         }
     } else {
         assertLayers {
-            this.showsLayer(STATUS_BAR_WINDOW_NAME)
+            this.isVisible(STATUS_BAR_WINDOW_NAME)
         }
     }
 }
@@ -163,10 +161,10 @@
     val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
 
     assertLayersStart {
-        this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
+        this.coversExactly(startingPos, NAV_BAR_LAYER_NAME)
     }
     assertLayersEnd {
-        this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos)
+        this.coversExactly(endingPos, NAV_BAR_LAYER_NAME)
     }
 }
 
@@ -179,10 +177,10 @@
     val endingPos = WindowUtils.getStatusBarPosition(endRotation)
 
     assertLayersStart {
-        this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, startingPos)
+        this.coversExactly(startingPos, STATUS_BAR_WINDOW_NAME)
     }
     assertLayersEnd {
-        this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, endingPos)
+        this.coversExactly(endingPos, STATUS_BAR_WINDOW_NAME)
     }
 }
 
@@ -197,39 +195,41 @@
 
 fun FlickerTestParameter.appLayerReplacesWallpaperLayer(appName: String) {
     assertLayers {
-        this.showsLayer("Wallpaper")
+        this.isVisible(WALLPAPER_TITLE)
             .then()
-            .replaceVisibleLayer("Wallpaper", appName)
+            .isInvisible(WALLPAPER_TITLE)
+            .isVisible(appName)
     }
 }
 
 fun FlickerTestParameter.wallpaperLayerReplacesAppLayer(testApp: IAppHelper) {
     assertLayers {
-        this.showsLayer(testApp.getPackage())
+        this.isVisible(testApp.getPackage())
             .then()
-            .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
+            .isInvisible(testApp.getPackage())
+            .isVisible(WALLPAPER_TITLE)
     }
 }
 
 fun FlickerTestParameter.layerAlwaysVisible(packageName: String) {
     assertLayers {
-        this.showsLayer(packageName)
+        this.isVisible(packageName)
     }
 }
 
 fun FlickerTestParameter.layerBecomesVisible(packageName: String) {
     assertLayers {
-        this.hidesLayer(packageName)
+        this.isInvisible(packageName)
             .then()
-            .showsLayer(packageName)
+            .isVisible(packageName)
     }
 }
 
 fun FlickerTestParameter.layerBecomesInvisible(packageName: String) {
     assertLayers {
-        this.showsLayer(packageName)
+        this.isVisible(packageName)
             .then()
-            .hidesLayer(packageName)
+            .isInvisible(packageName)
     }
 }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 38a88d3..26afb79 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -153,31 +153,11 @@
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerIsAlwaysVisible_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerIsAlwaysVisible()
-    }
+    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    fun statusBarLayerIsAlwaysVisible_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerIsAlwaysVisible()
-    }
+    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 476708c..2c4c627 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -17,9 +17,8 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -85,55 +84,55 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
         testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() =
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun visibleLayersShownMoreThanOneConsecutiveEntry() =
         testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index ac140f5..2bcdcd9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -91,63 +91,54 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
         testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
         Surface.ROTATION_0)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
+    fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
 
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() {
         Assume.assumeFalse(testSpec.isRotated)
@@ -171,7 +162,8 @@
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigNonRotationTests(repetitions = 5)
+                .getConfigNonRotationTests(repetitions = 5,
+                    supportedRotations = listOf(Surface.ROTATION_0))
         }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index 212644c..3dfa31d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -23,23 +23,23 @@
 
 fun FlickerTestParameter.imeLayerBecomesVisible() {
     assertLayers {
-        this.hidesLayer(IME_WINDOW_TITLE)
+        this.isInvisible(IME_WINDOW_TITLE)
             .then()
-            .showsLayer(IME_WINDOW_TITLE)
+            .isVisible(IME_WINDOW_TITLE)
     }
 }
 
 fun FlickerTestParameter.imeLayerBecomesInvisible() {
     assertLayers {
-        this.showsLayer(IME_WINDOW_TITLE)
+        this.isVisible(IME_WINDOW_TITLE)
             .then()
-            .hidesLayer(IME_WINDOW_TITLE)
+            .isInvisible(IME_WINDOW_TITLE)
     }
 }
 
 fun FlickerTestParameter.imeAppLayerIsAlwaysVisible(testApp: IAppHelper) {
     assertLayers {
-        this.showsLayer(testApp.getPackage())
+        this.isVisible(testApp.getPackage())
     }
 }
 
@@ -83,9 +83,8 @@
 
 fun FlickerTestParameter.imeAppLayerBecomesInvisible(testApp: IAppHelper) {
     assertLayers {
-        this.skipUntilFirstAssertion()
-            .showsLayer(testApp.getPackage())
+        this.isVisible(testApp.getPackage())
             .then()
-            .hidesLayer(testApp.getPackage())
+            .isInvisible(testApp.getPackage())
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index c7a5178..008ba2f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -89,43 +89,43 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() {
         Assume.assumeFalse(testSpec.isRotated)
@@ -139,7 +139,7 @@
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() {
         Assume.assumeFalse(testSpec.isRotated)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index e2a258a..18fac6a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.launch
 
 import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -148,17 +147,35 @@
     @Test
     fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+    fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+        Assume.assumeFalse(testSpec.isRotated)
         testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+    }
 
     @FlakyTest
     @Test
-    fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+    fun visibleWindowsShownMoreThanOneConsecutiveEntry_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+    }
 
-    @FlakyTest(bugId = 141361128)
+    @Presubmit
+    @Test
+    fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        Assume.assumeFalse(testSpec.isRotated)
+        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
+    @FlakyTest
+    @Test
+    fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
+    @Presubmit
     @Test
     fun noUncoveredRegions() = testSpec.noUncoveredRegions(Surface.ROTATION_0,
         testSpec.config.endRotation)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 3cc509f..20e4b88 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -16,13 +16,13 @@
 
 package com.android.server.wm.flicker.rotation
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusDoesNotChange
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -54,7 +54,15 @@
     testSpec: FlickerTestParameter
 ) : RotationTransition(testSpec) {
     override val testApp = SimpleAppHelper(instrumentation)
-    override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = emptyMap()
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
+            setup {
+                test {
+                    testApp.launchViaIntent(wmHelper)
+                }
+            }
+        }
 
     @Presubmit
     @Test
@@ -78,11 +86,11 @@
     @Test
     fun screenshotLayerBecomesInvisible() {
         testSpec.assertLayers {
-            this.showsLayer(testApp.getPackage())
+            this.isVisible(testApp.getPackage())
                 .then()
-                .showsLayer(SCREENSHOT_LAYER)
+                .isVisible(SCREENSHOT_LAYER)
                 .then()
-                .showsLayer(testApp.getPackage())
+                .isVisible(testApp.getPackage())
         }
     }
 
@@ -113,7 +121,7 @@
     @Test
     fun appLayerRotates_StartingPos() {
         testSpec.assertLayersStart {
-            this.hasVisibleRegion(testApp.getPackage(), startingPos)
+            this.coversExactly(startingPos, testApp.getPackage())
         }
     }
 
@@ -121,7 +129,7 @@
     @Test
     fun appLayerRotates_EndingPos() {
         testSpec.assertLayersEnd {
-            this.hasVisibleRegion(testApp.getPackage(), endingPos)
+            this.coversExactly(endingPos, testApp.getPackage())
         }
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 04ab84d..c391112 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.rotation
 
 import android.app.Instrumentation
-import android.os.Bundle
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -31,36 +30,37 @@
 import com.android.server.wm.flicker.startRotation
 
 abstract class RotationTransition(protected val testSpec: FlickerTestParameter) {
+    protected abstract val testApp: StandardAppHelper
+
     protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     protected val startingPos get() = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
     protected val endingPos get() = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
 
-    protected abstract val testApp: StandardAppHelper
-    protected abstract fun getAppLaunchParams(configuration: Bundle): Map<String, String>
+    protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+        withTestName { testSpec.name }
+        repeat { testSpec.config.repetitions }
+        setup {
+            test {
+                device.wakeUpAndGoToHomeScreen()
+            }
+            eachRun {
+                this.setRotation(testSpec.config.startRotation)
+            }
+        }
+        teardown {
+            test {
+                testApp.exit()
+            }
+        }
+        transitions {
+            this.setRotation(testSpec.config.endRotation)
+        }
+    }
 
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
         return FlickerBuilder(instrumentation).apply {
-            withTestName { testSpec.name }
-            repeat { testSpec.config.repetitions }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    val extras = getAppLaunchParams(testSpec.config)
-                    testApp.launchViaIntent(wmHelper, stringExtras = extras)
-                }
-                eachRun {
-                    this.setRotation(testSpec.config.startRotation)
-                }
-            }
-            teardown {
-                test {
-                    testApp.exit()
-                }
-            }
-            transitions {
-                this.setRotation(testSpec.config.endRotation)
-            }
+            transition(testSpec.config)
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index ef1aead..fc5bcc7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.rotation
 
-import android.os.Bundle
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -24,6 +23,7 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop
+import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusDoesNotChange
 import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
@@ -58,9 +58,18 @@
 ) : RotationTransition(testSpec) {
     override val testApp = SeamlessRotationAppHelper(instrumentation)
 
-    override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = mapOf(
-        ActivityOptions.EXTRA_STARVE_UI_THREAD to configuration.starveUiThread.toString()
-    )
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
+            setup {
+                test {
+                    testApp.launchViaIntent(wmHelper,
+                        stringExtras = mapOf(
+                            ActivityOptions.EXTRA_STARVE_UI_THREAD to it.starveUiThread.toString())
+                    )
+                }
+            }
+        }
 
     @Presubmit
     @Test
@@ -115,7 +124,7 @@
     @Test
     fun appLayerRotates() {
         testSpec.assertLayers {
-            this.hasVisibleRegion(testApp.`package`, startingPos)
+            this.coversExactly(startingPos, testApp.`package`)
         }
     }
 
@@ -126,12 +135,14 @@
     companion object {
         private val testFactory = FlickerTestParameterFactory.getInstance()
 
-        private val Bundle.starveUiThread
-            get() = this.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD, false)
+        private val Map<String, Any?>.starveUiThread
+            get() = this.getOrDefault(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) as Boolean
 
-        private fun FlickerTestParameter.createConfig(starveUiThread: Boolean): Bundle {
-            val config = this.config.deepCopy()
-            config.putBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD, starveUiThread)
+        private fun FlickerTestParameter.createConfig(
+            starveUiThread: Boolean
+        ): MutableMap<String, Any?> {
+            val config = this.config.toMutableMap()
+            config[ActivityOptions.EXTRA_STARVE_UI_THREAD] = starveUiThread
             return config
         }
 
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index a72b07c..335c8d0 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "InputTests",
     srcs: ["src/**/*.kt"],
diff --git a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
index f919a3e..c19e5cc 100644
--- a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
+++ b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
@@ -62,15 +62,12 @@
     fun testUpdateFrameInfoFromViewFrameInfo() {
         val frameInfo = FrameInfo()
         // By default, all values should be zero
-        assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(0)
-        assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(0)
+        // TODO(b/169866723): Use InputEventAssigner and assert INPUT_EVENT_ID
         assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(0)
         assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isEqualTo(0)
 
         // The values inside FrameInfo should match those from ViewFrameInfo after we update them
         mViewFrameInfo.populateFrameInfo(frameInfo)
-        assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(10)
-        assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(20)
         assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(
                 FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED)
         assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isGreaterThan(mTimeStarted)
diff --git a/tests/SilkFX/Android.bp b/tests/SilkFX/Android.bp
index 92e3efa..088d9a2 100644
--- a/tests/SilkFX/Android.bp
+++ b/tests/SilkFX/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "SilkFX",
     srcs: ["**/*.java", "**/*.kt"],
diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp
index 48031de..dc75f00 100644
--- a/tests/SurfaceViewBufferTests/Android.bp
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "SurfaceViewBufferTests",
     srcs: ["**/*.java","**/*.kt"],
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index 43a5078..ee24d48 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 java_test_host {
     name: "UpdatableSystemFontTest",
     srcs: ["src/**/*.java"],
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
index 1296699..f744d5d 100644
--- a/tests/UpdatableSystemFontTest/testdata/Android.bp
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 filegroup {
     name: "UpdatableSystemFontTestKeyPem",
     srcs: ["UpdatableSystemFontTestKey.pem"],
diff --git a/tests/benchmarks/internal/Android.bp b/tests/benchmarks/internal/Android.bp
index 9c34eaf..74ed7a3 100644
--- a/tests/benchmarks/internal/Android.bp
+++ b/tests/benchmarks/internal/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 android_test {
     name: "InternalBenchTests",
     srcs: ["src/**/*.java"],
@@ -23,4 +32,3 @@
     platform_apis: true,
     certificate: "platform"
 }
-
diff --git a/tests/net/common/java/android/net/NetworkStackTest.java b/tests/net/common/java/android/net/NetworkStackTest.java
index a99aa01..f8f9c72 100644
--- a/tests/net/common/java/android/net/NetworkStackTest.java
+++ b/tests/net/common/java/android/net/NetworkStackTest.java
@@ -15,20 +15,8 @@
  */
 package android.net;
 
-import static android.Manifest.permission.NETWORK_STACK;
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.net.NetworkStack.checkNetworkStackPermission;
-import static android.net.NetworkStack.checkNetworkStackPermissionOr;
-
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.when;
 
-import android.content.Context;
 import android.os.Build;
 import android.os.IBinder;
 
@@ -46,44 +34,15 @@
 
 @RunWith(AndroidJUnit4.class)
 public class NetworkStackTest {
-    private static final String [] OTHER_PERMISSION = {"otherpermission1", "otherpermission2"};
-
     @Rule
     public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
 
-    @Mock Context mCtx;
     @Mock private IBinder mConnectorBinder;
 
     @Before public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
     }
 
-    @Test
-    public void testCheckNetworkStackPermission() throws Exception {
-        when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_GRANTED);
-        when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK)))
-                .thenReturn(PERMISSION_DENIED);
-        checkNetworkStackPermission(mCtx);
-        checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION);
-
-        when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_DENIED);
-        when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK)))
-                .thenReturn(PERMISSION_GRANTED);
-        checkNetworkStackPermission(mCtx);
-        checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION);
-
-        when(mCtx.checkCallingOrSelfPermission(any())).thenReturn(PERMISSION_DENIED);
-
-        try {
-            checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION);
-        } catch (SecurityException e) {
-            // Expect to get a SecurityException
-            return;
-        }
-
-        fail("Expect fail but permission granted.");
-    }
-
     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testGetService() {
         NetworkStack.setServiceForTest(mConnectorBinder);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 4dbd020..11c10fa 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -53,6 +53,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_IA;
@@ -406,6 +407,8 @@
     private QosCallbackMockHelper mQosCallbackMockHelper;
     private QosCallbackTracker mQosCallbackTracker;
     private VpnManagerService mVpnManagerService;
+    private TestNetworkCallback mDefaultNetworkCallback;
+    private TestNetworkCallback mSystemDefaultNetworkCallback;
 
     // State variables required to emulate NetworkPolicyManagerService behaviour.
     private int mUidRules = RULE_NONE;
@@ -1546,6 +1549,7 @@
 
     @After
     public void tearDown() throws Exception {
+        unregisterDefaultNetworkCallbacks();
         setAlwaysOnNetworks(false);
         if (mCellNetworkAgent != null) {
             mCellNetworkAgent.disconnect();
@@ -2787,7 +2791,8 @@
         if (capability == NET_CAPABILITY_CBS || capability == NET_CAPABILITY_DUN ||
                 capability == NET_CAPABILITY_EIMS || capability == NET_CAPABILITY_FOTA ||
                 capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS ||
-                capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP) {
+                capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP
+                || capability == NET_CAPABILITY_ENTERPRISE) {
             assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
         } else {
             assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
@@ -2886,6 +2891,7 @@
         tryNetworkFactoryRequests(NET_CAPABILITY_IA);
         tryNetworkFactoryRequests(NET_CAPABILITY_RCS);
         tryNetworkFactoryRequests(NET_CAPABILITY_XCAP);
+        tryNetworkFactoryRequests(NET_CAPABILITY_ENTERPRISE);
         tryNetworkFactoryRequests(NET_CAPABILITY_EIMS);
         tryNetworkFactoryRequests(NET_CAPABILITY_NOT_METERED);
         tryNetworkFactoryRequests(NET_CAPABILITY_INTERNET);
@@ -6851,7 +6857,7 @@
         callback.expectCapabilitiesThat(mMockVpn, (caps)
                 -> caps.getUids().size() == 2
                 && caps.getUids().contains(new UidRange(uid, uid))
-                && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
+                && caps.getUids().contains(createUidRange(RESTRICTED_USER))
                 && caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_WIFI));
 
@@ -6861,7 +6867,7 @@
         callback.expectCapabilitiesThat(mMockVpn, (caps)
                 -> caps.getUids().size() == 2
                 && caps.getUids().contains(new UidRange(uid, uid))
-                && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER))
+                && caps.getUids().contains(createUidRange(RESTRICTED_USER))
                 && caps.hasTransport(TRANSPORT_VPN)
                 && !caps.hasTransport(TRANSPORT_WIFI));
 
@@ -7495,7 +7501,7 @@
         assertNotNull(underlying);
         mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY);
         // The legacy lockdown VPN only supports userId 0.
-        final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+        final Set<UidRange> ranges = Collections.singleton(createUidRange(PRIMARY_USER));
         mMockVpn.registerAgent(ranges);
         mMockVpn.setUnderlyingNetworks(new Network[]{underlying});
         mMockVpn.connect(true);
@@ -8414,7 +8420,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
         // The uid range needs to cover the test app so the network is visible to it.
-        final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+        final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
         mMockVpn.establish(lp, VPN_UID, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
 
@@ -8442,7 +8448,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
-        final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+        final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
         mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
 
@@ -8458,7 +8464,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0"));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
         // The uid range needs to cover the test app so the network is visible to it.
-        final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+        final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
         mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
 
@@ -8473,7 +8479,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
-        final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+        final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
         mMockVpn.establish(lp, VPN_UID, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
 
@@ -8525,7 +8531,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
-        final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER);
+        final UidRange vpnRange = createUidRange(PRIMARY_USER);
         final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
         mMockVpn.establish(lp, VPN_UID, vpnRanges);
         assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
@@ -8724,7 +8730,7 @@
 
     private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
             throws Exception {
-        final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+        final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
         mMockVpn.setVpnType(vpnType);
         mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
@@ -9283,7 +9289,7 @@
         lp.setInterfaceName("tun0");
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
-        final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER);
+        final UidRange vpnRange = createUidRange(PRIMARY_USER);
         Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
         mMockVpn.establish(lp, VPN_UID, vpnRanges);
         assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
@@ -9463,6 +9469,10 @@
         fail("TOO_MANY_REQUESTS never thrown");
     }
 
+    private UidRange createUidRange(int userId) {
+        return UidRange.createForUser(UserHandle.of(userId));
+    }
+
     private void mockGetApplicationInfo(@NonNull final String packageName, @NonNull final int uid)
             throws Exception {
         final ApplicationInfo applicationInfo = new ApplicationInfo();
@@ -9797,6 +9807,54 @@
             assertEquals(expectedPerAppNetwork, defaultNetwork);
             assertEquals(expectedOemRequestsSize, defaultRequest.mRequests.size());
         }
+        verifyMultipleDefaultCallbacks(expectedDefaultNetwork, expectedPerAppNetwork);
+    }
+
+    /**
+     * Verify default callbacks for 'available' fire as expected. This will only run if
+     * registerDefaultNetworkCallbacks() was executed prior and will only be different if the
+     * setOemNetworkPreference() per-app API was used for the current process.
+     * @param expectedSystemDefault the expected network for the system default.
+     * @param expectedPerAppDefault the expected network for the current process's default.
+     */
+    private void verifyMultipleDefaultCallbacks(
+            @NonNull final Network expectedSystemDefault,
+            @NonNull final Network expectedPerAppDefault) {
+        if (null != mSystemDefaultNetworkCallback && null != expectedSystemDefault
+                && mService.mNoServiceNetwork.network() != expectedSystemDefault) {
+            // getLastAvailableNetwork() is used as this method can be called successively with
+            // the same network to validate therefore expectAvailableThenValidatedCallbacks
+            // can't be used.
+            assertEquals(mSystemDefaultNetworkCallback.getLastAvailableNetwork(),
+                    expectedSystemDefault);
+        }
+        if (null != mDefaultNetworkCallback && null != expectedPerAppDefault
+                && mService.mNoServiceNetwork.network() != expectedPerAppDefault) {
+            assertEquals(mDefaultNetworkCallback.getLastAvailableNetwork(),
+                    expectedPerAppDefault);
+        }
+    }
+
+    private void registerDefaultNetworkCallbacks() {
+        // Using Manifest.permission.NETWORK_SETTINGS for registerSystemDefaultNetworkCallback()
+        mServiceContext.setPermission(
+                Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+        mSystemDefaultNetworkCallback = new TestNetworkCallback();
+        mDefaultNetworkCallback = new TestNetworkCallback();
+        mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback,
+                new Handler(ConnectivityThread.getInstanceLooper()));
+        mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback);
+        mServiceContext.setPermission(
+                Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED);
+    }
+
+    private void unregisterDefaultNetworkCallbacks() {
+        if (null != mDefaultNetworkCallback) {
+            mCm.unregisterNetworkCallback(mDefaultNetworkCallback);
+        }
+        if (null != mSystemDefaultNetworkCallback) {
+            mCm.unregisterNetworkCallback(mSystemDefaultNetworkCallback);
+        }
     }
 
     private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -9880,6 +9938,7 @@
         @OemNetworkPreferences.OemNetworkPreference final int networkPref =
                 OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
         final int expectedOemPrefRequestSize = 1;
+        registerDefaultNetworkCallbacks();
 
         // Setup the test process to use networkPref for their default network.
         setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
@@ -9894,6 +9953,7 @@
 
         // Verify that the active network is correct
         verifyActiveNetwork(TRANSPORT_ETHERNET);
+        // default NCs will be unregistered in tearDown
     }
 
     @Test
@@ -9901,6 +9961,7 @@
         @OemNetworkPreferences.OemNetworkPreference final int networkPref =
                 OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
         final int expectedOemPrefRequestSize = 1;
+        registerDefaultNetworkCallbacks();
 
         // Setup the test process to use networkPref for their default network.
         setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
@@ -9921,6 +9982,7 @@
                 mEthernetNetworkAgent.getNetwork());
 
         assertFalse(mCm.isActiveNetworkMetered());
+        // default NCs will be unregistered in tearDown
     }
 
     @Test
@@ -10077,7 +10139,6 @@
 
     /**
      * Test the tracked default requests clear previous OEM requests on setOemNetworkPreference().
-     * @throws Exception
      */
     @Test
     public void testSetOemNetworkPreferenceClearPreviousOemValues() throws Exception {
@@ -10105,9 +10166,8 @@
     }
 
     /**
-     * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID following in order:
+     * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order:
      * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback
-     * @throws Exception
      */
     @Test
     public void testMultilayerForPreferenceOemPaidEvaluatesCorrectly()
@@ -10173,9 +10233,8 @@
     }
 
     /**
-     * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK following in order:
+     * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK in the following order:
      * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID
-     * @throws Exception
      */
     @Test
     public void testMultilayerForPreferenceOemPaidNoFallbackEvaluatesCorrectly()
@@ -10236,10 +10295,9 @@
     }
 
     /**
-     * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY following in order:
+     * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY in the following order:
      * NET_CAPABILITY_OEM_PAID
      * This preference should only apply to OEM_PAID networks.
-     * @throws Exception
      */
     @Test
     public void testMultilayerForPreferenceOemPaidOnlyEvaluatesCorrectly()
@@ -10290,10 +10348,9 @@
     }
 
     /**
-     * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY following in order:
+     * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY in the following order:
      * NET_CAPABILITY_OEM_PRIVATE
      * This preference should only apply to OEM_PRIVATE networks.
-     * @throws Exception
      */
     @Test
     public void testMultilayerForPreferenceOemPrivateOnlyEvaluatesCorrectly()
@@ -10342,4 +10399,236 @@
                 mEthernetNetworkAgent.getNetwork().netId, 0 /* times */,
                 true /* shouldDestroyNetwork */);
     }
+
+    /**
+     * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order:
+     * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback
+     */
+    @Test
+    public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidCorrectly()
+            throws Exception {
+        @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+                OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
+        setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+        final int expectedDefaultRequestSize = 2;
+        final int expectedOemPrefRequestSize = 3;
+        registerDefaultNetworkCallbacks();
+
+        // The fallback as well as the OEM preference should now be tracked.
+        assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size());
+
+        // Test lowest to highest priority requests.
+        // Bring up metered cellular. This will satisfy the fallback network.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mCellNetworkAgent.getNetwork());
+
+        // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mEthernetNetworkAgent.getNetwork());
+
+        // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mWiFiNetworkAgent.getNetwork(),
+                mWiFiNetworkAgent.getNetwork());
+
+        // Disconnecting unmetered Wi-Fi will put the pref on OEM_PAID and fallback on cellular.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mEthernetNetworkAgent.getNetwork());
+
+        // Disconnecting cellular should keep OEM network on OEM_PAID and fallback will be null.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                null,
+                mEthernetNetworkAgent.getNetwork());
+
+        // Disconnecting OEM_PAID will put both on null as it is the last network.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                null,
+                null);
+
+        // default NCs will be unregistered in tearDown
+    }
+
+    /**
+     * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK in the following order:
+     * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID
+     */
+    @Test
+    public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidNoFallbackCorrectly()
+            throws Exception {
+        @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+                OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
+        setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+        final int expectedDefaultRequestSize = 2;
+        final int expectedOemPrefRequestSize = 2;
+        registerDefaultNetworkCallbacks();
+
+        // The fallback as well as the OEM preference should now be tracked.
+        assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size());
+
+        // Test lowest to highest priority requests.
+        // Bring up metered cellular. This will satisfy the fallback network but not the pref.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mService.mNoServiceNetwork.network());
+
+        // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mEthernetNetworkAgent.getNetwork());
+
+        // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mWiFiNetworkAgent.getNetwork(),
+                mWiFiNetworkAgent.getNetwork());
+
+        // Disconnecting unmetered Wi-Fi will put the OEM pref on OEM_PAID and fallback on cellular.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mEthernetNetworkAgent.getNetwork());
+
+        // Disconnecting cellular should keep OEM network on OEM_PAID and fallback will be null.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                null,
+                mEthernetNetworkAgent.getNetwork());
+
+        // Disconnecting OEM_PAID puts the fallback on null and the pref on the disconnected net.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                null,
+                mService.mNoServiceNetwork.network());
+
+        // default NCs will be unregistered in tearDown
+    }
+
+    /**
+     * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY in the following order:
+     * NET_CAPABILITY_OEM_PAID
+     * This preference should only apply to OEM_PAID networks.
+     */
+    @Test
+    public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidOnlyCorrectly()
+            throws Exception {
+        @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+                OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
+        setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+        final int expectedDefaultRequestSize = 2;
+        final int expectedOemPrefRequestSize = 1;
+        registerDefaultNetworkCallbacks();
+
+        // The fallback as well as the OEM preference should now be tracked.
+        assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size());
+
+        // Test lowest to highest priority requests.
+        // Bring up metered cellular. This will satisfy the fallback network.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mService.mNoServiceNetwork.network());
+
+        // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mEthernetNetworkAgent.getNetwork());
+
+        // Bring up unmetered Wi-Fi. The OEM network shouldn't change, the fallback will take Wi-Fi.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mWiFiNetworkAgent.getNetwork(),
+                mEthernetNetworkAgent.getNetwork());
+
+        // Disconnecting unmetered Wi-Fi shouldn't change the OEM network with fallback on cellular.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mEthernetNetworkAgent.getNetwork());
+
+        // Disconnecting OEM_PAID will keep the fallback on cellular and nothing for OEM_PAID.
+        // OEM_PAID_ONLY not supporting a fallback now uses the disconnected network.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mService.mNoServiceNetwork.network());
+
+        // Disconnecting cellular will put the fallback on null and the pref on disconnected.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                null,
+                mService.mNoServiceNetwork.network());
+
+        // default NCs will be unregistered in tearDown
+    }
+
+    /**
+     * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY in the following order:
+     * NET_CAPABILITY_OEM_PRIVATE
+     * This preference should only apply to OEM_PRIVATE networks.
+     */
+    @Test
+    public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPrivateOnlyCorrectly()
+            throws Exception {
+        @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+                OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+        setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+        final int expectedDefaultRequestSize = 2;
+        final int expectedOemPrefRequestSize = 1;
+        registerDefaultNetworkCallbacks();
+
+        // The fallback as well as the OEM preference should now be tracked.
+        assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size());
+
+        // Test lowest to highest priority requests.
+        // Bring up metered cellular. This will satisfy the fallback network.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mService.mNoServiceNetwork.network());
+
+        // Bring up ethernet with OEM_PRIVATE. This will satisfy NET_CAPABILITY_OEM_PRIVATE.
+        startOemManagedNetwork(false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mEthernetNetworkAgent.getNetwork());
+
+        // Bring up unmetered Wi-Fi. The OEM network shouldn't change, the fallback will take Wi-Fi.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mWiFiNetworkAgent.getNetwork(),
+                mEthernetNetworkAgent.getNetwork());
+
+        // Disconnecting unmetered Wi-Fi shouldn't change the OEM network with fallback on cellular.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mEthernetNetworkAgent.getNetwork());
+
+        // Disconnecting OEM_PRIVATE will keep the fallback on cellular.
+        // OEM_PRIVATE_ONLY not supporting a fallback now uses to the disconnected network.
+        stopOemManagedNetwork();
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                mCellNetworkAgent.getNetwork(),
+                mService.mNoServiceNetwork.network());
+
+        // Disconnecting cellular will put the fallback on null and pref on disconnected.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+        verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+                null,
+                mService.mNoServiceNetwork.network());
+
+        // default NCs will be unregistered in tearDown
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 6576daf..7489a0f 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -182,7 +182,8 @@
             mPackages.put(PKGS[i], PKG_UIDS[i]);
         }
     }
-    private static final UidRange PRI_USER_RANGE = UidRange.createForUser(primaryUser.id);
+    private static final UidRange PRI_USER_RANGE =
+            UidRange.createForUser(UserHandle.of(primaryUser.id));
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext;
     @Mock private UserManager mUserManager;
@@ -272,7 +273,7 @@
                 vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
 
         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
-                PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
+                PRI_USER_RANGE, UidRange.createForUser(UserHandle.of(restrictedProfileA.id))
         })), ranges);
     }
 
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index ce8a898..66590c9 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -16,6 +16,8 @@
 
 package android.net.vcn;
 
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
+
 import static androidx.test.InstrumentationRegistry.getContext;
 
 import static org.junit.Assert.assertEquals;
@@ -204,6 +206,9 @@
         cbBinder.onEnteredSafeMode();
         verify(mMockStatusCallback).onEnteredSafeMode();
 
+        cbBinder.onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
+        verify(mMockStatusCallback).onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
+
         cbBinder.onGatewayConnectionError(
                 UNDERLYING_NETWORK_CAPABILITIES,
                 VcnManager.VCN_ERROR_CODE_NETWORK_ERROR,
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 45b2381..9b500a7 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -43,7 +43,6 @@
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -59,6 +58,7 @@
 import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
 import android.net.vcn.VcnConfig;
 import android.net.vcn.VcnConfigTest;
+import android.net.vcn.VcnManager;
 import android.net.vcn.VcnUnderlyingNetworkPolicy;
 import android.net.wifi.WifiInfo;
 import android.os.IBinder;
@@ -783,7 +783,7 @@
                 true /* hasPermissionsforSubGroup */,
                 true /* hasLocationPermission */);
 
-        verify(mMockStatusCallback, times(1)).onEnteredSafeMode();
+        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
     }
 
     @Test
@@ -795,7 +795,8 @@
                 false /* hasPermissionsforSubGroup */,
                 true /* hasLocationPermission */);
 
-        verify(mMockStatusCallback, never()).onEnteredSafeMode();
+        verify(mMockStatusCallback, never())
+                .onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
     }
 
     @Test
@@ -807,7 +808,8 @@
                 true /* hasPermissionsforSubGroup */,
                 false /* hasLocationPermission */);
 
-        verify(mMockStatusCallback, never()).onEnteredSafeMode();
+        verify(mMockStatusCallback, never())
+                .onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
     }
 
     @Test
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index f0b7595..ebc0ec1 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -11,6 +11,12 @@
 
 EMOJI_VS = 0xFE0F
 
+#TODO(179952916): Rename CutiveMono and DancingScript
+CANONICAL_NAME_EXCEPTION_LIST = [
+    'CutiveMono.ttf',
+    'DancingScript-Regular.ttf',
+]
+
 LANG_TO_SCRIPT = {
     'as': 'Beng',
     'be': 'Cyrl',
@@ -665,6 +671,53 @@
             assert_font_supports_none_of_chars(record.font, cjk_punctuation, name)
 
 
+def getPostScriptName(font):
+  ttf = open_font(font)
+  nameTable = ttf['name']
+  for name in nameTable.names:
+    if name.nameID == 6 and name.platformID == 3 and name.platEncID == 1 and name.langID == 0x0409:
+      return str(name)
+
+
+def getSuffix(font):
+  file_path, index = font
+  with open(path.join(_fonts_dir, file_path), 'rb') as f:
+    tag = f.read(4)
+    isCollection = tag == b'ttcf'
+
+  ttf = open_font(font)
+  isType1 = ('CFF ' in ttf or 'CFF2' in ttf)
+
+  if isType1:
+    if isCollection:
+      return '.otc'
+    else:
+      return '.otf'
+  else:
+    if isCollection:
+      return '.ttc'
+    else:
+      return '.ttf'
+
+
+def check_canonical_name():
+  for record in _all_fonts:
+    file_name, index = record.font
+    if file_name in CANONICAL_NAME_EXCEPTION_LIST:
+      continue
+
+    if index and index != 0:
+      continue
+
+    psName = getPostScriptName(record.font)
+    assert psName, 'PostScript must be defined'
+
+    suffix = getSuffix(record.font)
+    canonicalName = '%s%s' % (psName, suffix)
+
+    assert file_name == canonicalName, (
+        '%s is not a canonical name. Must be %s' % (file_name, canonicalName))
+
 def main():
     global _fonts_dir
     target_out = sys.argv[1]
@@ -682,6 +735,8 @@
 
     check_cjk_punctuation()
 
+    check_canonical_name()
+
     check_emoji = sys.argv[2]
     if check_emoji == 'true':
         ucd_path = sys.argv[3]
diff --git a/tools/powerstats/Android.bp b/tools/powerstats/Android.bp
index af41144..9c58daf0 100644
--- a/tools/powerstats/Android.bp
+++ b/tools/powerstats/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 java_binary_host {
     name: "PowerStatsServiceProtoParser",
     manifest: "PowerStatsServiceProtoParser_manifest.txt",
diff --git a/tools/processors/intdef_mappings/Android.bp b/tools/processors/intdef_mappings/Android.bp
index e255f7c..82a5dac 100644
--- a/tools/processors/intdef_mappings/Android.bp
+++ b/tools/processors/intdef_mappings/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 java_plugin {
     name: "intdef-annotation-processor",
 
@@ -30,4 +39,4 @@
     ],
 
     test_suites: ["general-tests"],
-}
\ No newline at end of file
+}
diff --git a/tools/xmlpersistence/Android.bp b/tools/xmlpersistence/Android.bp
index d58d0dc..0b6dba6 100644
--- a/tools/xmlpersistence/Android.bp
+++ b/tools/xmlpersistence/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
 java_binary_host {
     name: "xmlpersistence_cli",
     manifest: "manifest.txt",