Merge "GameManager: Add Public API" into sc-dev
diff --git a/Android.bp b/Android.bp
index 7099291..72fc103 100644
--- a/Android.bp
+++ b/Android.bp
@@ -344,8 +344,8 @@
 genrule {
     name: "statslog-telephony-common-java-gen",
     tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common"
-        + " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog",
+    cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common" +
+        " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog",
     out: ["com/android/internal/telephony/TelephonyCommonStatsLog.java"],
 }
 
@@ -581,6 +581,7 @@
         "android.hardware.vibrator-V1.1-java",
         "android.hardware.vibrator-V1.2-java",
         "android.hardware.vibrator-V1.3-java",
+        "android.hardware.vibrator-V2-java",
         "android.security.apc-java",
         "android.security.authorization-java",
         "android.security.usermanager-java",
@@ -751,8 +752,8 @@
 }
 
 platform_compat_config {
-   name: "framework-platform-compat-config",
-   src: ":framework-minus-apex",
+    name: "framework-platform-compat-config",
+    src: ":framework-minus-apex",
 }
 
 // A temporary build target that is conditionally included on the bootclasspath if
@@ -773,7 +774,7 @@
     name: "statslog-framework-java-gen",
     tools: ["stats-log-api-gen"],
     cmd: "$(location stats-log-api-gen) --java $(out) --module framework" +
-         " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource",
+        " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource",
     out: ["com/android/internal/util/FrameworkStatsLog.java"],
 }
 
@@ -882,7 +883,7 @@
 
 java_library {
     name: "framework-annotations-lib",
-    srcs: [ ":framework-annotations" ],
+    srcs: [":framework-annotations"],
     sdk_version: "core_current",
 }
 
@@ -1160,7 +1161,6 @@
     },
 }
 
-
 // This is the full proto version of libplatformprotos. It may only
 // be used by test code that is not shipped on the device.
 cc_library {
@@ -1226,68 +1226,57 @@
     path: "core/java",
 }
 
-aidl_interface {
-    name: "libincremental_aidl",
-    unstable: true,
+cc_defaults {
+    name: "incremental_default",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wextra-semi",
+        "-Werror",
+        "-Wzero-as-null-pointer-constant",
+        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+    aidl: {
+        include_dirs: [
+            "frameworks/native/aidl/binder",
+        ],
+        export_aidl_headers: true,
+    },
+}
+
+cc_library {
+    name: "libincremental_aidl-cpp",
     srcs: [
         ":incremental_aidl",
     ],
-    backend: {
-        java: {
-            sdk_version: "28",
-        },
-        cpp: {
-            enabled: true,
-        },
-        ndk: {
-            enabled: true,
-        },
-    },
+    defaults: ["incremental_default"],
 }
 
-aidl_interface {
-    name: "libdataloader_aidl",
-    unstable: true,
+cc_library {
+    name: "libdataloader_aidl-cpp",
     srcs: [
         ":dataloader_aidl",
     ],
-    imports: [
-        "libincremental_aidl",
+    defaults: ["incremental_default"],
+    shared_libs: [
+        "libincremental_aidl-cpp",
     ],
-    backend: {
-        java: {
-            sdk_version: "28",
-        },
-        cpp: {
-            enabled: true,
-        },
-        ndk: {
-            enabled: false,
-        },
-    },
 }
 
-aidl_interface {
-    name: "libincremental_manager_aidl",
-    unstable: true,
+cc_library {
+    name: "libincremental_manager_aidl-cpp",
     srcs: [
         ":incremental_manager_aidl",
     ],
-    imports: [
-        "libincremental_aidl",
-        "libdataloader_aidl",
+    defaults: ["incremental_default"],
+    shared_libs: [
+        "libincremental_aidl-cpp",
+        "libdataloader_aidl-cpp",
     ],
-    backend: {
-        java: {
-            sdk_version: "28",
-        },
-        cpp: {
-            enabled: true,
-        },
-        ndk: {
-            enabled: false,
-        },
-    },
 }
 
 // TODO(b/77285514): remove this once the last few hidl interfaces have been
@@ -1316,7 +1305,7 @@
         "core/java/android/os/RemoteException.java",
         "core/java/android/util/AndroidException.java",
     ],
-    libs: [ "unsupportedappusage" ],
+    libs: ["unsupportedappusage"],
 
     dxflags: ["--core-library"],
     installable: false,
@@ -1535,4 +1524,5 @@
         ":protolog-common-src",
     ],
 }
+
 // protolog end
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 30ed7de..175fb38 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,6 @@
 [Builtin Hooks]
 clang_format = true
+bpfmt = true
 
 [Builtin Hooks Options]
 # Only turn on clang-format check for the following subfolders.
@@ -15,7 +16,7 @@
                services/incremental/
                tests/
                tools/
-
+bpfmt = -d
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
 
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
index e83c64c..5a45961 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
@@ -44,7 +44,7 @@
 @RunWith(AndroidJUnit4.class)
 public class TypefaceCreatePerfTest {
     // A font file name in asset directory.
-    private static final String TEST_FONT_NAME = "DancingScript-Regular.ttf";
+    private static final String TEST_FONT_NAME = "DancingScript.ttf";
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index 168c7c2..5ded446 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -207,7 +207,6 @@
 
   public static final class GetByUriRequest.Builder {
     ctor public GetByUriRequest.Builder();
-    method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
     method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
     method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...);
     method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
@@ -323,7 +322,6 @@
     method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.util.Collection<java.lang.String>);
     method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.lang.String...);
     method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.util.Collection<java.lang.String>);
-    method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
     method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
     method @NonNull public android.app.appsearch.SearchSpec build();
     method @NonNull public android.app.appsearch.SearchSpec.Builder setMaxSnippetSize(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_SNIPPET_SIZE_LIMIT) int);
@@ -338,7 +336,8 @@
   public final class SetSchemaRequest {
     method @NonNull public java.util.Map<java.lang.String,android.app.appsearch.AppSearchSchema.Migrator> getMigrators();
     method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
-    method @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi();
+    method @NonNull public java.util.Set<java.lang.String> getSchemasNotDisplayedBySystem();
+    method @Deprecated @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi();
     method @NonNull public java.util.Map<java.lang.String,java.util.Set<android.app.appsearch.PackageIdentifier>> getSchemasVisibleToPackages();
     method public boolean isForceOverride();
   }
@@ -350,8 +349,9 @@
     method @NonNull public android.app.appsearch.SetSchemaRequest build();
     method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean);
     method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.AppSearchSchema.Migrator);
+    method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeDisplayedBySystem(@NonNull String, boolean);
     method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier);
-    method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean);
+    method @Deprecated @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean);
   }
 
   public class SetSchemaResponse {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 6a5975e..da446bf 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -317,7 +317,7 @@
                     getPackageName(),
                     DEFAULT_DATABASE_NAME,
                     schemaBundles,
-                    new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
+                    new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
                     /*schemasPackageAccessible=*/ Collections.emptyMap(),
                     request.isForceOverride(),
                     mContext.getUserId(),
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 24cc60e..f379739 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -146,7 +146,7 @@
                     mPackageName,
                     mDatabaseName,
                     schemaBundles,
-                    new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
+                    new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
                     schemasPackageAccessibleBundles,
                     request.isForceOverride(),
                     mUserId,
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
index 8dd9dc1..fb63e16 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -96,7 +96,7 @@
      * SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} when building a schema.
      *
      * <p>Document access can also be granted to system UIs by specifying {@link
-     * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} when building a schema.
+     * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} when building a schema.
      *
      * <p>See {@link AppSearchSession#search} for a detailed explanation on forming a query string.
      *
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index 68ae23f..ba27762 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -33,7 +33,7 @@
      * @param packageName The name of the package that owns this schema.
      * @param databaseName  The name of the database where this schema lives.
      * @param schemaBundles List of {@link AppSearchSchema} bundles.
-     * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
+     * @param schemasNotDisplayedBySystem Schema types that should not be surfaced on platform
      *     surfaces.
      * @param schemasPackageAccessibleBundles Schema types that are visible to the specified
      *     packages. The value List contains PackageIdentifier Bundles.
@@ -47,7 +47,7 @@
         in String packageName,
         in String databaseName,
         in List<Bundle> schemaBundles,
-        in List<String> schemasNotPlatformSurfaceable,
+        in List<String> schemasNotDisplayedBySystem,
         in Map<String, List<Bundle>> schemasPackageAccessibleBundles,
         boolean forceOverride,
         in int userId,
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
index c927e34..17266f8 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -166,29 +166,7 @@
          * all results, excepting any types that have their own, specific property paths set.
          *
          * @throws IllegalStateException if the builder has already been used.
-         *     <p>{@see SearchSpec.Builder#addProjection(String, String...)}
-         */
-        @NonNull
-        public Builder addProjection(@NonNull String schemaType, @NonNull String... propertyPaths) {
-            Preconditions.checkNotNull(propertyPaths);
-            return addProjection(schemaType, Arrays.asList(propertyPaths));
-        }
-
-        /**
-         * Adds property paths for the specified type to be used for projection. If property paths
-         * are added for a type, then only the properties referred to will be retrieved for results
-         * of that type. If a property path that is specified isn't present in a result, it will be
-         * ignored for that result. Property paths cannot be null.
-         *
-         * <p>If no property paths are added for a particular type, then all properties of results
-         * of that type will be retrieved.
-         *
-         * <p>If property path is added for the {@link
-         * GetByUriRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will apply to
-         * all results, excepting any types that have their own, specific property paths set.
-         *
-         * @throws IllegalStateException if the builder has already been used.
-         *     <p>{@see SearchSpec.Builder#addProjection(String, String...)}
+         * @see SearchSpec.Builder#addProjection
          */
         @NonNull
         public Builder addProjection(
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
index d792f45..f34034b 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -202,14 +202,6 @@
          */
         public static final String PROPERTY_PATH_FIELD = "propertyPath";
 
-        /**
-         * The index of matching value in its property. A property may have multiple values. This
-         * index indicates which value is the match.
-         *
-         * @hide
-         */
-        public static final String VALUES_INDEX_FIELD = "valuesIndex";
-
         /** @hide */
         public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower";
 
@@ -232,8 +224,7 @@
             mBundle = Preconditions.checkNotNull(bundle);
             Preconditions.checkNotNull(document);
             mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD));
-            mFullText =
-                    getPropertyValues(document, mPropertyPath, mBundle.getInt(VALUES_INDEX_FIELD));
+            mFullText = getPropertyValues(document, mPropertyPath);
         }
 
         /**
@@ -326,8 +317,7 @@
         }
 
         /** Extracts the matching string from the document. */
-        private static String getPropertyValues(
-                GenericDocument document, String propertyName, int valueIndex) {
+        private static String getPropertyValues(GenericDocument document, String propertyName) {
             // In IcingLib snippeting is available for only 3 data types i.e String, double and
             // long, so we need to check which of these three are requested.
             // TODO (tytytyww): getPropertyStringArray takes property name, handle for property
@@ -337,7 +327,9 @@
             if (values == null) {
                 throw new IllegalStateException("No content found for requested property path!");
             }
-            return values[valueIndex];
+
+            // TODO(b/175146044): Return the proper match based on the index in the propertyName.
+            return values[0];
         }
     }
 
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
index f72f8e1..7888c8d 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
@@ -535,28 +535,6 @@
          */
         @NonNull
         public SearchSpec.Builder addProjection(
-                @NonNull String schema, @NonNull String... propertyPaths) {
-            Preconditions.checkNotNull(propertyPaths);
-            return addProjection(schema, Arrays.asList(propertyPaths));
-        }
-
-        /**
-         * Adds property paths for the specified type to be used for projection. If property paths
-         * are added for a type, then only the properties referred to will be retrieved for results
-         * of that type. If a property path that is specified isn't present in a result, it will be
-         * ignored for that result. Property paths cannot be null.
-         *
-         * <p>If no property paths are added for a particular type, then all properties of results
-         * of that type will be retrieved.
-         *
-         * <p>If property path is added for the {@link SearchSpec#PROJECTION_SCHEMA_TYPE_WILDCARD},
-         * then those property paths will apply to all results, excepting any types that have their
-         * own, specific property paths set.
-         *
-         * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
-         */
-        @NonNull
-        public SearchSpec.Builder addProjection(
                 @NonNull String schema, @NonNull Collection<String> propertyPaths) {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             Preconditions.checkNotNull(schema);
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 426a903..c054063 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -71,19 +71,19 @@
  */
 public final class SetSchemaRequest {
     private final Set<AppSearchSchema> mSchemas;
-    private final Set<String> mSchemasNotVisibleToSystemUi;
+    private final Set<String> mSchemasNotDisplayedBySystem;
     private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages;
     private final Map<String, AppSearchSchema.Migrator> mMigrators;
     private final boolean mForceOverride;
 
     SetSchemaRequest(
             @NonNull Set<AppSearchSchema> schemas,
-            @NonNull Set<String> schemasNotVisibleToSystemUi,
+            @NonNull Set<String> schemasNotDisplayedBySystem,
             @NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages,
             @NonNull Map<String, AppSearchSchema.Migrator> migrators,
             boolean forceOverride) {
         mSchemas = Preconditions.checkNotNull(schemas);
-        mSchemasNotVisibleToSystemUi = Preconditions.checkNotNull(schemasNotVisibleToSystemUi);
+        mSchemasNotDisplayedBySystem = Preconditions.checkNotNull(schemasNotDisplayedBySystem);
         mSchemasVisibleToPackages = Preconditions.checkNotNull(schemasVisibleToPackages);
         mMigrators = Preconditions.checkNotNull(migrators);
         mForceOverride = forceOverride;
@@ -96,12 +96,22 @@
     }
 
     /**
+     * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+     * @deprecated This method exists only for dogfooder transition and must be removed.
+     */
+    @Deprecated
+    @NonNull
+    public Set<String> getSchemasNotVisibleToSystemUi() {
+        return getSchemasNotDisplayedBySystem();
+    }
+
+    /**
      * Returns all the schema types that are opted out of being displayed and visible on any system
      * UI surface.
      */
     @NonNull
-    public Set<String> getSchemasNotVisibleToSystemUi() {
-        return Collections.unmodifiableSet(mSchemasNotVisibleToSystemUi);
+    public Set<String> getSchemasNotDisplayedBySystem() {
+        return Collections.unmodifiableSet(mSchemasNotDisplayedBySystem);
     }
 
     /**
@@ -151,7 +161,7 @@
      */
     public static final class Builder {
         private final Set<AppSearchSchema> mSchemas = new ArraySet<>();
-        private final Set<String> mSchemasNotVisibleToSystemUi = new ArraySet<>();
+        private final Set<String> mSchemasNotDisplayedBySystem = new ArraySet<>();
         private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages =
                 new ArrayMap<>();
         private final Map<String, AppSearchSchema.Migrator> mMigrators = new ArrayMap<>();
@@ -163,6 +173,8 @@
          *
          * <p>An {@link AppSearchSchema} object represents one type of structured data.
          *
+         * <p>Any documents of these types will be displayed on system UI surfaces by default.
+         *
          * @throws IllegalStateException if the builder has already been used.
          */
         @NonNull
@@ -187,30 +199,43 @@
         }
 
         /**
+         * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+         * @deprecated This method exists only for dogfooder transition and must be removed.
+         */
+        @Deprecated
+        @NonNull
+        public Builder setSchemaTypeVisibilityForSystemUi(
+                @NonNull String schemaType, boolean displayed) {
+            return setSchemaTypeDisplayedBySystem(schemaType, displayed);
+        }
+
+        /**
          * 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.
+         * <p>The default behavior, if this method is not called, is to allow types to be displayed
+         * on system UI surfaces.
          *
-         * @param schemaType The schema type to set visibility on.
-         * @param visible Whether the {@code schemaType} will be visible or not.
+         * @param schemaType The name of an {@link AppSearchSchema} within the same {@link
+         *     SetSchemaRequest}, which will be configured.
+         * @param displayed Whether documents of this type will be displayed on system UI surfaces.
          * @throws IllegalStateException if the builder has already been used.
          */
-        // Merged list available from getSchemasNotVisibleToSystemUi
+        // Merged list available from getSchemasNotDisplayedBySystem
         @SuppressLint("MissingGetterMatchingBuilder")
         @NonNull
-        public Builder setSchemaTypeVisibilityForSystemUi(
-                @NonNull String schemaType, boolean visible) {
+        public Builder setSchemaTypeDisplayedBySystem(
+                @NonNull String schemaType, boolean displayed) {
             Preconditions.checkNotNull(schemaType);
             Preconditions.checkState(!mBuilt, "Builder has already been used");
 
-            if (visible) {
-                mSchemasNotVisibleToSystemUi.remove(schemaType);
+            if (displayed) {
+                mSchemasNotDisplayedBySystem.remove(schemaType);
             } else {
-                mSchemasNotVisibleToSystemUi.add(schemaType);
+                mSchemasNotDisplayedBySystem.add(schemaType);
             }
             return this;
         }
@@ -315,9 +340,10 @@
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             mBuilt = true;
 
-            // Verify that any schema types with visibility settings refer to a real schema.
+            // Verify that any schema types with display or visibility settings refer to a real
+            // schema.
             // Create a copy because we're going to remove from the set for verification purposes.
-            Set<String> referencedSchemas = new ArraySet<>(mSchemasNotVisibleToSystemUi);
+            Set<String> referencedSchemas = new ArraySet<>(mSchemasNotDisplayedBySystem);
             referencedSchemas.addAll(mSchemasVisibleToPackages.keySet());
 
             for (AppSearchSchema schema : mSchemas) {
@@ -332,7 +358,7 @@
 
             return new SetSchemaRequest(
                     mSchemas,
-                    mSchemasNotVisibleToSystemUi,
+                    mSchemasNotDisplayedBySystem,
                     mSchemasVisibleToPackages,
                     mMigrators,
                     mForceOverride);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index a45fa39..27c9ccb 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -89,7 +89,7 @@
                 @NonNull String packageName,
                 @NonNull String databaseName,
                 @NonNull List<Bundle> schemaBundles,
-                @NonNull List<String> schemasNotPlatformSurfaceable,
+                @NonNull List<String> schemasNotDisplayedBySystem,
                 @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
                 boolean forceOverride,
                 @UserIdInt int userId,
@@ -119,13 +119,12 @@
                     }
                     schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
                 }
-                AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId);
                 impl.setSchema(
                         packageName,
                         databaseName,
                         schemas,
-                        schemasNotPlatformSurfaceable,
+                        schemasNotDisplayedBySystem,
                         schemasPackageAccessible,
                         forceOverride);
                 invokeCallbackOnResult(
@@ -153,7 +152,7 @@
                 verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
                 List<AppSearchSchema> schemas = impl.getSchema(packageName, databaseName);
                 List<Bundle> schemaBundles = new ArrayList<>(schemas.size());
                 for (int i = 0; i < schemas.size(); i++) {
@@ -188,7 +187,7 @@
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
                 for (int i = 0; i < documentBundles.size(); i++) {
                     GenericDocument document = new GenericDocument(documentBundles.get(i));
                     try {
@@ -231,7 +230,7 @@
                 AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
@@ -276,7 +275,7 @@
                 verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
                 SearchResultPage searchResultPage =
                         impl.query(
                                 packageName,
@@ -311,7 +310,7 @@
                 verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
                 SearchResultPage searchResultPage =
                         impl.globalQuery(
                                 queryExpression,
@@ -342,7 +341,7 @@
             try {
                 verifyUserUnlocked(callingUserId);
                 AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
                 SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
                 invokeCallbackOnResult(
                         callback,
@@ -362,7 +361,7 @@
             try {
                 verifyUserUnlocked(callingUserId);
                 AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
                 impl.invalidateNextPageToken(nextPageToken);
             } catch (Throwable t) {
                 Log.e(TAG, "Unable to invalidate the query page token", t);
@@ -390,7 +389,7 @@
             try {
                 verifyUserUnlocked(callingUserId);
                 AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
                 impl.reportUsage(packageName, databaseName, namespace, uri, usageTimeMillis);
                 invokeCallbackOnResult(
                         callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
@@ -422,7 +421,7 @@
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
                 AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
@@ -460,7 +459,7 @@
                 verifyUserUnlocked(callingUserId);
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
                 impl.removeByQuery(
                         packageName,
                         databaseName,
@@ -482,7 +481,7 @@
             try {
                 verifyUserUnlocked(callingUserId);
                 AppSearchImpl impl =
-                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
                 impl.persistToDisk();
             } catch (Throwable t) {
                 Log.e(TAG, "Unable to persist the data to disk", t);
@@ -499,7 +498,7 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyUserUnlocked(callingUserId);
-                mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                mImplInstanceManager.getOrCreateAppSearchImpl(getContext(), callingUserId);
                 invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
@@ -571,6 +570,7 @@
         private void invokeCallbackOnError(
                 IAppSearchBatchResultCallback callback, Throwable throwable) {
             try {
+                //TODO(b/175067650) verify ParcelableException could propagate throwable correctly.
                 callback.onSystemError(new ParcelableException(throwable));
             } catch (RemoteException e) {
                 Log.e(TAG, "Unable to send error to the callback", e);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index 82319d4..8c953d1 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -74,7 +74,7 @@
     }
 
     /**
-     * Gets an instance of AppSearchImpl for the given user.
+     * Gets an instance of AppSearchImpl for the given user, or creates one if none exists.
      *
      * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and
      * one will be created.
@@ -84,7 +84,7 @@
      * @return An initialized {@link AppSearchImpl} for this user
      */
     @NonNull
-    public AppSearchImpl getAppSearchImpl(
+    public AppSearchImpl getOrCreateAppSearchImpl(
             @NonNull Context context, @UserIdInt int userId) throws AppSearchException {
         synchronized (mInstancesLocked) {
             AppSearchImpl instance = mInstancesLocked.get(userId);
@@ -96,6 +96,32 @@
         }
     }
 
+
+    /**
+     * Gets an instance of AppSearchImpl for the given user.
+     *
+     * <p>This method should only be called by an initialized SearchSession, which has been already
+     * created the AppSearchImpl instance for the given user.
+     *
+     * @param userId The multi-user userId of the device user calling AppSearch
+     * @return An initialized {@link AppSearchImpl} for this user
+     * @throws IllegalStateException if {@link AppSearchImpl} haven't created for the given user.
+     */
+    @NonNull
+    public AppSearchImpl getAppSearchImpl(@UserIdInt int userId) {
+        synchronized (mInstancesLocked) {
+            AppSearchImpl instance = mInstancesLocked.get(userId);
+            if (instance == null) {
+                // Impossible scenario, user cannot call an uninitialized SearchSession,
+                // getInstance should always find the instance for the given user and never try to
+                // create an instance for this user again.
+                throw new IllegalStateException(
+                        "AppSearchImpl has never been created for this user: " + userId);
+            }
+            return instance;
+        }
+    }
+
     private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
             throws AppSearchException {
         File appSearchDir = getAppSearchDir(context, userId);
@@ -104,6 +130,7 @@
 
     private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
         // See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs
+        //TODO(b/177685938):Switch from getDataUserCePackageDirectory to getDataSystemCeDirectory
         File userCeDir =
                 Environment.getDataUserCePackageDirectory(
                         StorageManager.UUID_PRIVATE_INTERNAL, userId, context.getPackageName());
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
index f422ebc..e9852aa 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
@@ -113,8 +113,6 @@
         Bundle bundle = new Bundle();
         bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, propertyPath);
         bundle.putInt(
-                SearchResult.MatchInfo.VALUES_INDEX_FIELD, snippetMatchProto.getValuesIndex());
-        bundle.putInt(
                 SearchResult.MatchInfo.EXACT_MATCH_POSITION_LOWER_FIELD,
                 snippetMatchProto.getExactMatchPosition());
         bundle.putInt(
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 2c9477a..41c70f0 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I593dfd22279739e5f578e07d36a55cf02ee942c5
+I42b89416968565ceb6483b400894f5b49524208c
diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp
index eb072af..3c5082e 100644
--- a/apex/appsearch/testing/Android.bp
+++ b/apex/appsearch/testing/Android.bp
@@ -30,5 +30,8 @@
         "guava",
         "truth-prebuilt",
     ],
-    visibility: ["//cts/tests/appsearch"],
+    visibility: [
+        "//cts/tests/appsearch",
+        "//vendor:__subpackages__",
+    ],
 }
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
index d912c08..440050f 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
@@ -34,8 +34,8 @@
      * SetSchemaRequest.Builder#setDocumentClassVisibilityForPackage} when building a schema.
      *
      * <p>Document access can also be granted to system UIs by specifying {@link
-     * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi}, or {@link
-     * SetSchemaRequest.Builder#setDocumentClassVisibilityForSystemUi} when building a schema.
+     * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem}, or {@link
+     * SetSchemaRequest.Builder#setDocumentClassDisplayedBySystem} when building a schema.
      *
      * <p>See {@link AppSearchSessionShim#search} for a detailed explanation on forming a query
      * string.
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 5e5717d11..0dde546 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -32,12 +32,8 @@
 
     void exitIdle(String reason);
 
-    // duration in milliseconds
     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,
+            long durationMs, int userId, boolean sync, @ReasonCode int reasonCode,
             @Nullable String reason);
 
     /**
@@ -49,10 +45,11 @@
      * @param sync
      * @param reasonCode one of {@link ReasonCode}
      * @param reason
+     * @param callingUid UID of app who added this temp-allowlist.
      */
     void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
             @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode,
-            @Nullable String reason);
+            @Nullable String reason, int callingUid);
 
     // 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 ac28e82..84fb39b 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -1943,19 +1943,11 @@
         }
 
         // duration in milliseconds
-        @Deprecated
         @Override
         public void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
-                long duration, int userId, boolean sync, @Nullable String reason) {
-            addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration,
-                    userId, sync, REASON_UNKNOWN, reason);
-        }
-
-        @Override
-        public void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
-                long duration, int userId, boolean sync, @ReasonCode int reasonCode,
+                long durationMs, int userId, boolean sync, @ReasonCode int reasonCode,
                 @Nullable String reason) {
-            addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration,
+            addPowerSaveTempAllowlistAppInternal(callingUid, packageName, durationMs,
                     userId, sync, reasonCode, reason);
         }
 
@@ -1963,8 +1955,8 @@
         @Override
         public void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
                 @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode,
-                @Nullable String reason) {
-            addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync,
+                @Nullable String reason, int callingUid) {
+            addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, type, sync,
                     reasonCode, reason);
         }
 
@@ -2741,6 +2733,16 @@
     void addPowerSaveTempAllowlistAppInternal(int callingUid, String packageName,
             long duration, int userId, boolean sync, @ReasonCode int reasonCode,
             @Nullable String reason) {
+        synchronized (this) {
+            int callingAppId = UserHandle.getAppId(callingUid);
+            if (callingAppId >= Process.FIRST_APPLICATION_UID) {
+                if (!mPowerSaveWhitelistSystemAppIds.get(callingAppId)) {
+                    throw new SecurityException(
+                            "Calling app " + UserHandle.formatUid(callingUid)
+                                    + " is not on whitelist");
+                }
+            }
+        }
         try {
             int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId);
             addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration,
@@ -2760,13 +2762,6 @@
         boolean informWhitelistChanged = false;
         int appId = UserHandle.getAppId(uid);
         synchronized (this) {
-            int callingAppId = UserHandle.getAppId(callingUid);
-            if (callingAppId >= Process.FIRST_APPLICATION_UID) {
-                if (!mPowerSaveWhitelistSystemAppIds.get(callingAppId)) {
-                    throw new SecurityException("Calling app " + UserHandle.formatUid(callingUid)
-                            + " is not on whitelist");
-                }
-            }
             duration = Math.min(duration, mConstants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS);
             Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.get(appId);
             final boolean newEntry = entry == null;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java b/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java
index 34ba753b3..862d8b7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java
@@ -25,7 +25,9 @@
 public interface JobCompletedListener {
     /**
      * Callback for when a job is completed.
+     *
+     * @param stopReason      The stop reason provided to JobParameters.
      * @param needsReschedule Whether the implementing class should reschedule this job.
      */
-    void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule);
+    void onJobCompletedLocked(JobStatus jobStatus, int stopReason, boolean needsReschedule);
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 0308d68..b958c3f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -25,6 +25,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.UserSwitchObserver;
 import android.app.job.JobInfo;
+import android.app.job.JobParameters;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -102,13 +103,18 @@
      */
     static final int WORK_TYPE_BG = 1 << 3;
     /**
+     * The job is for an app in a {@link ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE} or higher
+     * state, or is allowed to run as an expedited job, but is for a completely background user.
+     */
+    static final int WORK_TYPE_BGUSER_IMPORTANT = 1 << 4;
+    /**
      * The job does not satisfy any of the conditions for {@link #WORK_TYPE_TOP},
      * {@link #WORK_TYPE_FGS}, or {@link #WORK_TYPE_EJ}, but is for a completely background user,
      * so can run as a background user job.
      */
-    static final int WORK_TYPE_BGUSER = 1 << 4;
+    static final int WORK_TYPE_BGUSER = 1 << 5;
     @VisibleForTesting
-    static final int NUM_WORK_TYPES = 5;
+    static final int NUM_WORK_TYPES = 6;
     private static final int ALL_WORK_TYPES = (1 << NUM_WORK_TYPES) - 1;
 
     @IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
@@ -117,6 +123,7 @@
             WORK_TYPE_FGS,
             WORK_TYPE_EJ,
             WORK_TYPE_BG,
+            WORK_TYPE_BGUSER_IMPORTANT,
             WORK_TYPE_BGUSER
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -138,6 +145,8 @@
                 return "BG";
             case WORK_TYPE_BGUSER:
                 return "BGUSER";
+            case WORK_TYPE_BGUSER_IMPORTANT:
+                return "BGUSER_IMPORTANT";
             default:
                 return "WORK(" + workType + ")";
         }
@@ -163,30 +172,40 @@
                     new WorkTypeConfig("screen_on_normal", 11,
                             // defaultMin
                             List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_FGS, 1),
-                                    Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)),
+                                    Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+                            List.of(Pair.create(WORK_TYPE_BG, 6),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 2),
+                                    Pair.create(WORK_TYPE_BGUSER, 3))
                     ),
                     new WorkTypeConfig("screen_on_moderate", 9,
                             // defaultMin
                             List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
-                                    Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
+                                    Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+                            List.of(Pair.create(WORK_TYPE_BG, 4),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
+                                    Pair.create(WORK_TYPE_BGUSER, 1))
                     ),
                     new WorkTypeConfig("screen_on_low", 6,
                             // defaultMin
                             List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
                                     Pair.create(WORK_TYPE_EJ, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                            List.of(Pair.create(WORK_TYPE_BG, 1),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
+                                    Pair.create(WORK_TYPE_BGUSER, 1))
                     ),
                     new WorkTypeConfig("screen_on_critical", 6,
                             // defaultMin
                             List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
                                     Pair.create(WORK_TYPE_EJ, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                            List.of(Pair.create(WORK_TYPE_BG, 1),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
+                                    Pair.create(WORK_TYPE_BGUSER, 1))
                     )
             );
     private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
@@ -194,30 +213,40 @@
                     new WorkTypeConfig("screen_off_normal", 15,
                             // defaultMin
                             List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 2),
-                                    Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)),
+                                    Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+                            List.of(Pair.create(WORK_TYPE_BG, 6),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 2),
+                                    Pair.create(WORK_TYPE_BGUSER, 3))
                     ),
                     new WorkTypeConfig("screen_off_moderate", 15,
                             // defaultMin
                             List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_FGS, 2),
-                                    Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)),
+                                    Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+                            List.of(Pair.create(WORK_TYPE_BG, 4),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
+                                    Pair.create(WORK_TYPE_BGUSER, 1))
                     ),
                     new WorkTypeConfig("screen_off_low", 9,
                             // defaultMin
                             List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
                                     Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                            List.of(Pair.create(WORK_TYPE_BG, 1),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
+                                    Pair.create(WORK_TYPE_BGUSER, 1))
                     ),
                     new WorkTypeConfig("screen_off_critical", 6,
                             // defaultMin
                             List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
                                     Pair.create(WORK_TYPE_EJ, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                            List.of(Pair.create(WORK_TYPE_BG, 1),
+                                    Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
+                                    Pair.create(WORK_TYPE_BGUSER, 1))
                     )
             );
 
@@ -288,6 +317,8 @@
 
         final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+        filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
         mContext.registerReceiver(mReceiver, filter);
         try {
             ActivityManager.getService().registerUserSwitchObserver(mGracePeriodObserver, TAG);
@@ -311,6 +342,20 @@
                 case Intent.ACTION_SCREEN_OFF:
                     onInteractiveStateChanged(false);
                     break;
+                case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+                    if (mPowerManager != null && mPowerManager.isDeviceIdleMode()) {
+                        synchronized (mLock) {
+                            stopLongRunningJobsLocked("deep doze");
+                        }
+                    }
+                    break;
+                case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
+                    if (mPowerManager != null && mPowerManager.isPowerSaveMode()) {
+                        synchronized (mLock) {
+                            stopLongRunningJobsLocked("battery saver");
+                        }
+                    }
+                    break;
             }
         }
     };
@@ -469,7 +514,7 @@
         // Update the priorities of jobs that aren't running, and also count the pending work types.
         // Do this before the following loop to hopefully reduce the cost of
         // shouldStopRunningJobLocked().
-        updateNonRunningPriorities(pendingJobs, true);
+        updateNonRunningPrioritiesLocked(pendingJobs, true);
 
         for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
             final JobServiceContext js = activeServices.get(i);
@@ -585,7 +630,8 @@
                                 + activeServices.get(i).getRunningJobLocked());
                     }
                     // preferredUid will be set to uid of currently running job.
-                    activeServices.get(i).preemptExecutingJobLocked(preemptReasonForContext[i]);
+                    activeServices.get(i).cancelExecutingJobLocked(
+                            JobParameters.REASON_PREEMPT, preemptReasonForContext[i]);
                     preservePreferredUid = true;
                 } else {
                     final JobStatus pendingJob = contextIdToJobMap[i];
@@ -604,13 +650,26 @@
         noteConcurrency();
     }
 
+    @GuardedBy("mLock")
+    private void stopLongRunningJobsLocked(@NonNull String debugReason) {
+        for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; ++i) {
+            final JobServiceContext jsc = mService.mActiveServices.get(i);
+            final JobStatus jobStatus = jsc.getRunningJobLocked();
+
+            if (jobStatus != null && !jsc.isWithinExecutionGuaranteeTime()) {
+                jsc.cancelExecutingJobLocked(JobParameters.REASON_TIMEOUT, debugReason);
+            }
+        }
+    }
+
     private void noteConcurrency() {
         mService.mJobPackageTracker.noteConcurrency(mRunningJobs.size(),
                 // TODO: log per type instead of only TOP
                 mWorkCountTracker.getRunningJobCount(WORK_TYPE_TOP));
     }
 
-    private void updateNonRunningPriorities(@NonNull final List<JobStatus> pendingJobs,
+    @GuardedBy("mLock")
+    private void updateNonRunningPrioritiesLocked(@NonNull final List<JobStatus> pendingJobs,
             boolean updateCounter) {
         for (int i = 0; i < pendingJobs.size(); i++) {
             final JobStatus pending = pendingJobs.get(i);
@@ -628,6 +687,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void startJobLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
             @WorkType final int workType) {
         final List<StateController> controllers = mService.mControllers;
@@ -647,6 +707,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     void onJobCompletedLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
             @WorkType final int workType) {
         mWorkCountTracker.onJobFinished(workType);
@@ -655,7 +716,7 @@
         if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) {
             updateCounterConfigLocked();
             // Preemption case needs special care.
-            updateNonRunningPriorities(pendingJobs, false);
+            updateNonRunningPrioritiesLocked(pendingJobs, false);
 
             JobStatus highestPriorityJob = null;
             int highPriWorkType = workType;
@@ -726,7 +787,7 @@
             }
         } else if (pendingJobs.size() > 0) {
             updateCounterConfigLocked();
-            updateNonRunningPriorities(pendingJobs, false);
+            updateNonRunningPrioritiesLocked(pendingJobs, false);
 
             // This slot is now free and we have pending jobs. Start the highest priority job we
             // find.
@@ -775,6 +836,7 @@
      * another job to run, or if system state suggests the job should stop.
      */
     @Nullable
+    @GuardedBy("mLock")
     String shouldStopRunningJobLocked(@NonNull JobServiceContext context) {
         final JobStatus js = context.getRunningJobLocked();
         if (js == null) {
@@ -817,17 +879,22 @@
         // Only expedited jobs can replace expedited jobs.
         if (js.shouldTreatAsExpeditedJob()) {
             // Keep fg/bg user distinction.
-            if (workType == WORK_TYPE_BGUSER) {
-                // For now, let any bg user job replace a bg user expedited job.
-                // TODO: limit to ej once we have dedicated bg user ej slots.
-                if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_BGUSER) > 0) {
-                    return "blocking " + workTypeToString(workType) + " queue";
+            if (workType == WORK_TYPE_BGUSER_IMPORTANT || workType == WORK_TYPE_BGUSER) {
+                // Let any important bg user job replace a bg user expedited job.
+                if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_BGUSER_IMPORTANT) > 0) {
+                    return "blocking " + workTypeToString(WORK_TYPE_BGUSER_IMPORTANT) + " queue";
                 }
-            } else {
-                if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0) {
-                    return "blocking " + workTypeToString(workType) + " queue";
+                // Let a fg user EJ preempt a bg user EJ (if able), but not the other way around.
+                if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0
+                        && mWorkCountTracker.canJobStart(WORK_TYPE_EJ, workType)
+                        != WORK_TYPE_NONE) {
+                    return "blocking " + workTypeToString(WORK_TYPE_EJ) + " queue";
                 }
+            } else if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0) {
+                return "blocking " + workTypeToString(WORK_TYPE_EJ) + " queue";
             }
+            // No other pending EJs. Return null so we don't let regular jobs preempt an EJ.
+            return null;
         }
 
         // Easy check. If there are pending jobs of the same work type, then we know that
@@ -881,6 +948,7 @@
         return s.toString();
     }
 
+    @GuardedBy("mLock")
     void updateConfigLocked() {
         DeviceConfig.Properties properties =
                 DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
@@ -1010,6 +1078,7 @@
 
     int getJobWorkTypes(@NonNull JobStatus js) {
         int classification = 0;
+
         if (shouldRunAsFgUserJob(js)) {
             if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
                 classification |= WORK_TYPE_TOP;
@@ -1023,7 +1092,11 @@
                 classification |= WORK_TYPE_EJ;
             }
         } else {
-            // TODO(171305774): create dedicated slots for EJs of bg user
+            if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_FOREGROUND_SERVICE
+                    || js.shouldTreatAsExpeditedJob()) {
+                classification |= WORK_TYPE_BGUSER_IMPORTANT;
+            }
+            // BGUSER_IMPORTANT jobs can also run as BGUSER jobs, so not an 'else' here.
             classification |= WORK_TYPE_BGUSER;
         }
 
@@ -1040,12 +1113,16 @@
         private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
         private static final String KEY_PREFIX_MAX_BGUSER =
                 CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_";
+        private static final String KEY_PREFIX_MAX_BGUSER_IMPORTANT =
+                CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_important_";
         private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_";
         private static final String KEY_PREFIX_MIN_FGS = CONFIG_KEY_PREFIX_CONCURRENCY + "min_fgs_";
         private static final String KEY_PREFIX_MIN_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "min_ej_";
         private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
         private static final String KEY_PREFIX_MIN_BGUSER =
                 CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_";
+        private static final String KEY_PREFIX_MIN_BGUSER_IMPORTANT =
+                CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_important_";
         private final String mConfigIdentifier;
 
         private int mMaxTotal;
@@ -1101,6 +1178,10 @@
                     properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier,
                             mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal))));
             mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg);
+            final int maxBgUserImp = Math.max(1, Math.min(mMaxTotal,
+                    properties.getInt(KEY_PREFIX_MAX_BGUSER_IMPORTANT + mConfigIdentifier,
+                            mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER_IMPORTANT, mMaxTotal))));
+            mMaxAllowedSlots.put(WORK_TYPE_BGUSER_IMPORTANT, maxBgUserImp);
             final int maxBgUser = Math.max(1, Math.min(mMaxTotal,
                     properties.getInt(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
                             mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER, mMaxTotal))));
@@ -1132,6 +1213,11 @@
                             mDefaultMinReservedSlots.get(WORK_TYPE_BG))));
             mMinReservedSlots.put(WORK_TYPE_BG, minBg);
             remaining -= minBg;
+            // Ensure bg user imp is in the range [0, min(maxBgUserImp, remaining)]
+            final int minBgUserImp = Math.max(0, Math.min(Math.min(maxBgUserImp, remaining),
+                    properties.getInt(KEY_PREFIX_MIN_BGUSER_IMPORTANT + mConfigIdentifier,
+                            mDefaultMinReservedSlots.get(WORK_TYPE_BGUSER_IMPORTANT, 0))));
+            mMinReservedSlots.put(WORK_TYPE_BGUSER_IMPORTANT, minBgUserImp);
             // Ensure bg user is in the range [0, min(maxBgUser, remaining)]
             final int minBgUser = Math.max(0, Math.min(Math.min(maxBgUser, remaining),
                     properties.getInt(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
@@ -1170,6 +1256,10 @@
             pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG))
                     .println();
             pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
+                    mMinReservedSlots.get(WORK_TYPE_BGUSER_IMPORTANT)).println();
+            pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
+                    mMaxAllowedSlots.get(WORK_TYPE_BGUSER_IMPORTANT)).println();
+            pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
                     mMinReservedSlots.get(WORK_TYPE_BGUSER)).println();
             pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
                     mMaxAllowedSlots.get(WORK_TYPE_BGUSER)).println();
@@ -1269,19 +1359,10 @@
 
         void setConfig(@NonNull WorkTypeConfig workTypeConfig) {
             mConfigMaxTotal = workTypeConfig.getMaxTotal();
-            mConfigNumReservedSlots.put(WORK_TYPE_TOP,
-                    workTypeConfig.getMinReserved(WORK_TYPE_TOP));
-            mConfigNumReservedSlots.put(WORK_TYPE_FGS,
-                    workTypeConfig.getMinReserved(WORK_TYPE_FGS));
-            mConfigNumReservedSlots.put(WORK_TYPE_EJ, workTypeConfig.getMinReserved(WORK_TYPE_EJ));
-            mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG));
-            mConfigNumReservedSlots.put(WORK_TYPE_BGUSER,
-                    workTypeConfig.getMinReserved(WORK_TYPE_BGUSER));
-            mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP));
-            mConfigAbsoluteMaxSlots.put(WORK_TYPE_FGS, workTypeConfig.getMax(WORK_TYPE_FGS));
-            mConfigAbsoluteMaxSlots.put(WORK_TYPE_EJ, workTypeConfig.getMax(WORK_TYPE_EJ));
-            mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG));
-            mConfigAbsoluteMaxSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMax(WORK_TYPE_BGUSER));
+            for (int workType = 1; workType < ALL_WORK_TYPES; workType <<= 1) {
+                mConfigNumReservedSlots.put(workType, workTypeConfig.getMinReserved(workType));
+                mConfigAbsoluteMaxSlots.put(workType, workTypeConfig.getMax(workType));
+            }
 
             mNumUnspecializedRemaining = mConfigMaxTotal;
             for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
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 8bb03e9..515cb74 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1745,11 +1745,12 @@
      * A job just finished executing. We fetch the
      * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
      * whether we want to reschedule we re-add it to the controllers.
-     * @param jobStatus Completed job.
+     *
+     * @param jobStatus       Completed job.
      * @param needsReschedule Whether the implementing class should reschedule this job.
      */
     @Override
-    public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
+    public void onJobCompletedLocked(JobStatus jobStatus, int stopReason, boolean needsReschedule) {
         if (DEBUG) {
             Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
         }
@@ -1767,6 +1768,11 @@
         // we stop it.
         final JobStatus rescheduledJob = needsReschedule
                 ? getRescheduleJobForFailureLocked(jobStatus) : null;
+        if (rescheduledJob != null
+                && (stopReason == JobParameters.REASON_TIMEOUT
+                || stopReason == JobParameters.REASON_PREEMPT)) {
+            rescheduledJob.disallowRunInBatterySaverAndDoze();
+        }
 
         // Do not write back immediately if this is a periodic job. The job may get lost if system
         // shuts down before it is added back.
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 2a23d60..6802b6b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -269,14 +269,14 @@
             try {
                 final int bindFlags;
                 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 | Context.BIND_NOT_FOREGROUND
                             | Context.BIND_ALMOST_PERCEPTIBLE
-                            | Context.BIND_ALLOW_NETWORK_ACCESS;
+                            | Context.BIND_ALLOW_NETWORK_ACCESS
+                            | Context.BIND_NOT_APP_COMPONENT_USAGE;
                 } else {
                     bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
-                            | Context.BIND_NOT_PERCEPTIBLE;
+                            | Context.BIND_NOT_PERCEPTIBLE
+                            | Context.BIND_NOT_APP_COMPONENT_USAGE;
                 }
                 binding = mContext.bindServiceAsUser(intent, this, bindFlags,
                         UserHandle.of(job.getUserId()));
@@ -353,15 +353,10 @@
 
     /** Called externally when a job that was scheduled for execution should be cancelled. */
     @GuardedBy("mLock")
-    void cancelExecutingJobLocked(int reason, String debugReason) {
+    void cancelExecutingJobLocked(int reason, @NonNull String debugReason) {
         doCancelLocked(reason, debugReason);
     }
 
-    @GuardedBy("mLock")
-    void preemptExecutingJobLocked(@NonNull String reason) {
-        doCancelLocked(JobParameters.REASON_PREEMPT, reason);
-    }
-
     int getPreferredUid() {
         return mPreferredUid;
     }
@@ -379,8 +374,8 @@
     }
 
     boolean isWithinExecutionGuaranteeTime() {
-        return mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis
-                < sElapsedRealtimeClock.millis();
+        return sElapsedRealtimeClock.millis()
+                < mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis;
     }
 
     @GuardedBy("mLock")
@@ -618,7 +613,7 @@
     }
 
     @GuardedBy("mLock")
-    private void doCancelLocked(int stopReasonCode, String debugReason) {
+    private void doCancelLocked(int stopReasonCode, @Nullable String debugReason) {
         if (mVerb == VERB_FINISHED) {
             if (DEBUG) {
                 Slog.d(TAG,
@@ -731,7 +726,7 @@
      *     _ENDING     -> No point in doing anything here, so we ignore.
      */
     @GuardedBy("mLock")
-    private void handleCancelLocked(String reason) {
+    private void handleCancelLocked(@Nullable String reason) {
         if (JobSchedulerService.DEBUG) {
             Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
                     + VERB_STRINGS[mVerb]);
@@ -815,7 +810,7 @@
      * VERB_STOPPING.
      */
     @GuardedBy("mLock")
-    private void sendStopMessageLocked(String reason) {
+    private void sendStopMessageLocked(@Nullable String reason) {
         removeOpTimeOutLocked();
         if (mVerb != VERB_EXECUTING) {
             Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
@@ -841,18 +836,19 @@
      * we want to clean up internally.
      */
     @GuardedBy("mLock")
-    private void closeAndCleanupJobLocked(boolean reschedule, String reason) {
+    private void closeAndCleanupJobLocked(boolean reschedule, @Nullable String reason) {
         final JobStatus completedJob;
         if (mVerb == VERB_FINISHED) {
             return;
         }
         applyStoppedReasonLocked(reason);
         completedJob = mRunningJob;
-        mJobPackageTracker.noteInactive(completedJob, mParams.getStopReason(), reason);
+        final int stopReason = mParams.getStopReason();
+        mJobPackageTracker.noteInactive(completedJob, stopReason, reason);
         FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
                 completedJob.getSourceUid(), null, completedJob.getBatteryName(),
                 FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
-                mParams.getStopReason(), completedJob.getStandbyBucket(), completedJob.getJobId(),
+                stopReason, completedJob.getStandbyBucket(), completedJob.getJobId(),
                 completedJob.hasChargingConstraint(),
                 completedJob.hasBatteryNotLowConstraint(),
                 completedJob.hasStorageNotLowConstraint(),
@@ -863,7 +859,7 @@
                 completedJob.hasContentTriggerConstraint());
         try {
             mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
-                    mParams.getStopReason());
+                    stopReason);
         } catch (RemoteException e) {
             // Whatever.
         }
@@ -882,11 +878,11 @@
         service = null;
         mAvailable = true;
         removeOpTimeOutLocked();
-        mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
+        mCompletedListener.onJobCompletedLocked(completedJob, stopReason, reschedule);
         mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType);
     }
 
-    private void applyStoppedReasonLocked(String reason) {
+    private void applyStoppedReasonLocked(@Nullable String reason) {
         if (reason != null && mStoppedReason == null) {
             mStoppedReason = reason;
             mStoppedTime = sElapsedRealtimeClock.millis();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index eaf8f4d..aa8d98c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -954,7 +954,7 @@
                     appBucket, sourceTag,
                     elapsedRuntimes.first, elapsedRuntimes.second,
                     lastSuccessfulRunTime, lastFailedRunTime,
-                    (rtcIsGood) ? null : rtcRuntimes, internalFlags);
+                    (rtcIsGood) ? null : rtcRuntimes, internalFlags, /* dynamicConstraints */ 0);
             return js;
         }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index 192f5e6..79ef321 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -64,7 +64,7 @@
      * when the app is temp whitelisted or in the foreground.
      */
     private final ArraySet<JobStatus> mAllowInIdleJobs;
-    private final SparseBooleanArray mForegroundUids;
+    private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
     private final DeviceIdleUpdateFunctor mDeviceIdleUpdateFunctor;
     private final DeviceIdleJobsDelayHandler mHandler;
     private final PowerManager mPowerManager;
@@ -77,7 +77,6 @@
     private int[] mDeviceIdleWhitelistAppIds;
     private int[] mPowerSaveTempWhitelistAppIds;
 
-    // onReceive
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -120,6 +119,10 @@
         }
     };
 
+    /** Criteria for whether or not we should a job's rush evaluation when the device exits Doze. */
+    private final Predicate<JobStatus> mShouldRushEvaluation = (jobStatus) ->
+            jobStatus.isRequestedExpeditedJob() || mForegroundUids.get(jobStatus.getSourceUid());
+
     public DeviceIdleJobsController(JobSchedulerService service) {
         super(service);
 
@@ -133,7 +136,6 @@
                 mLocalDeviceIdleController.getPowerSaveTempWhitelistAppIds();
         mDeviceIdleUpdateFunctor = new DeviceIdleUpdateFunctor();
         mAllowInIdleJobs = new ArraySet<>();
-        mForegroundUids = new SparseBooleanArray();
         final IntentFilter filter = new IntentFilter();
         filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
         filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
@@ -156,14 +158,9 @@
                 mHandler.removeMessages(PROCESS_BACKGROUND_JOBS);
                 mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
             } else {
-                // When coming out of doze, process all foreground uids immediately, while others
-                // will be processed after a delay of 3 seconds.
-                for (int i = 0; i < mForegroundUids.size(); i++) {
-                    if (mForegroundUids.valueAt(i)) {
-                        mService.getJobStore().forEachJobForSourceUid(
-                                mForegroundUids.keyAt(i), mDeviceIdleUpdateFunctor);
-                    }
-                }
+                // When coming out of doze, process all foreground uids and EJs immediately,
+                // while others will be processed after a delay of 3 seconds.
+                mService.getJobStore().forEachJob(mShouldRushEvaluation, mDeviceIdleUpdateFunctor);
                 mHandler.sendEmptyMessageDelayed(PROCESS_BACKGROUND_JOBS, BACKGROUND_JOBS_DELAY);
             }
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 5bdeb38..bad8dc1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -91,6 +91,12 @@
     static final int CONSTRAINT_WITHIN_EXPEDITED_QUOTA = 1 << 23;    // Implicit constraint
     static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
 
+    // The following set of dynamic constraints are for specific use cases (as explained in their
+    // relative naming and comments). Right now, they apply different constraints, which is fine,
+    // but if in the future, we have overlapping dynamic constraint sets, removing one constraint
+    // set may accidentally remove a constraint applied by another dynamic set.
+    // TODO: properly handle overlapping dynamic constraint sets
+
     /**
      * The additional set of dynamic constraints that must be met if the job's effective bucket is
      * {@link JobSchedulerService#RESTRICTED_INDEX}. Connectivity can be ignored if the job doesn't
@@ -103,6 +109,13 @@
                     | CONSTRAINT_IDLE;
 
     /**
+     * The additional set of dynamic constraints that must be met if this is an expedited job that
+     * had a long enough run while the device was Dozing or in battery saver.
+     */
+    private static final int DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS =
+            CONSTRAINT_DEVICE_NOT_DOZING | CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
+
+    /**
      * Standard media URIs that contain the media files that might be important to the user.
      * @see #mHasMediaBackupExemption
      */
@@ -426,7 +439,8 @@
     private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
             int sourceUserId, int standbyBucket, String tag, int numFailures,
             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
-            long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags) {
+            long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags,
+            int dynamicConstraints) {
         this.job = job;
         this.callingUid = callingUid;
         this.standbyBucket = standbyBucket;
@@ -487,6 +501,7 @@
         }
         this.requiredConstraints = requiredConstraints;
         mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
+        addDynamicConstraints(dynamicConstraints);
         mReadyNotDozing = canRunInDoze();
         if (standbyBucket == RESTRICTED_INDEX) {
             addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
@@ -521,7 +536,7 @@
                 jobStatus.getSourceTag(), jobStatus.getNumFailures(),
                 jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
                 jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
-                jobStatus.getInternalFlags());
+                jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints);
         mPersistedUtcTimes = jobStatus.mPersistedUtcTimes;
         if (jobStatus.mPersistedUtcTimes != null) {
             if (DEBUG) {
@@ -543,12 +558,12 @@
             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
             long lastSuccessfulRunTime, long lastFailedRunTime,
             Pair<Long, Long> persistedExecutionTimesUTC,
-            int innerFlags) {
+            int innerFlags, int dynamicConstraints) {
         this(job, callingUid, sourcePkgName, sourceUserId,
                 standbyBucket,
                 sourceTag, 0,
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
-                lastSuccessfulRunTime, lastFailedRunTime, innerFlags);
+                lastSuccessfulRunTime, lastFailedRunTime, innerFlags, dynamicConstraints);
 
         // Only during initial inflation do we record the UTC-timebase execution bounds
         // read from the persistent store.  If we ever have to recreate the JobStatus on
@@ -572,7 +587,8 @@
                 rescheduling.getStandbyBucket(),
                 rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
                 newLatestRuntimeElapsedMillis,
-                lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags());
+                lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags(),
+                rescheduling.mDynamicConstraints);
     }
 
     /**
@@ -609,7 +625,7 @@
                 standbyBucket, tag, 0,
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
                 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
-                /*innerFlags=*/ 0);
+                /*innerFlags=*/ 0, /* dynamicConstraints */ 0);
     }
 
     public void enqueueWorkLocked(JobWorkItem work) {
@@ -1083,12 +1099,15 @@
      * in Doze.
      */
     public boolean canRunInDoze() {
-        return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 || shouldTreatAsExpeditedJob();
+        return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
+                || (shouldTreatAsExpeditedJob()
+                && (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
     }
 
     boolean canRunInBatterySaver() {
         return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0
-                || shouldTreatAsExpeditedJob();
+                || (shouldTreatAsExpeditedJob()
+                && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
     }
 
     boolean shouldIgnoreNetworkBlocking() {
@@ -1245,6 +1264,14 @@
     }
 
     /**
+     * Add additional constraints to prevent this job from running when doze or battery saver are
+     * active.
+     */
+    public void disallowRunInBatterySaverAndDoze() {
+        addDynamicConstraints(DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS);
+    }
+
+    /**
      * Indicates that this job cannot run without the specified constraints. This is evaluated
      * separately from the job's explicitly requested constraints and MUST be satisfied before
      * the job can run if the app doesn't have quota.
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index b18a22b..1d6f20d 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -47,6 +47,7 @@
     static_libs: [
         "exoplayer2-extractor",
         "mediatranscoding_aidl_interface-java",
+        "modules-annotation-minsdk",
         "modules-utils-build",
     ],
     jarjar_rules: "jarjar_rules.txt",
@@ -108,7 +109,7 @@
 filegroup {
     name: "mediaparser-srcs",
     srcs: [
-        "java/android/media/MediaParser.java"
+        "java/android/media/MediaParser.java",
     ],
     path: "java",
 }
diff --git a/apex/media/framework/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt
index eb71fdd..91489dc 100644
--- a/apex/media/framework/jarjar_rules.txt
+++ b/apex/media/framework/jarjar_rules.txt
@@ -1,2 +1,2 @@
-rule com.android.modules.utils.** android.media.internal.utils.@1
+rule com.android.modules.** android.media.internal.@1
 rule com.google.android.exoplayer2.** android.media.internal.exo.@1
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
index 9ec25fe..f39bcfb 100644
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -27,12 +27,14 @@
 import android.content.Context;
 import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
+import android.os.Build;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.media.MediaBrowserService;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.modules.annotation.MinSdk;
 import com.android.modules.utils.build.SdkLevel;
 
 import java.util.Collections;
@@ -45,6 +47,7 @@
  * Provides support for interacting with {@link android.media.MediaSession2 MediaSession2s}
  * that applications have published to express their ongoing media playback state.
  */
+@MinSdk(Build.VERSION_CODES.S)
 @SystemService(Context.MEDIA_COMMUNICATION_SERVICE)
 public class MediaCommunicationManager {
     private static final String TAG = "MediaCommunicationManager";
diff --git a/apex/media/framework/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java
index 6397ba3..7697359 100644
--- a/apex/media/framework/java/android/media/MediaSession2.java
+++ b/apex/media/framework/java/android/media/MediaSession2.java
@@ -32,6 +32,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.media.session.MediaSessionManager;
 import android.media.session.MediaSessionManager.RemoteUserInfo;
 import android.os.BadParcelableException;
 import android.os.Bundle;
@@ -43,6 +44,8 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -86,6 +89,7 @@
     private final String mSessionId;
     private final PendingIntent mSessionActivity;
     private final Session2Token mSessionToken;
+    private final MediaSessionManager mMediaSessionManager;
     private final MediaCommunicationManager mCommunicationManager;
     private final Handler mResultHandler;
 
@@ -114,7 +118,13 @@
         mSessionStub = new Session2Link(this);
         mSessionToken = new Session2Token(Process.myUid(), TYPE_SESSION, context.getPackageName(),
                 mSessionStub, tokenExtras);
-        mCommunicationManager = mContext.getSystemService(MediaCommunicationManager.class);
+        if (SdkLevel.isAtLeastS()) {
+            mCommunicationManager = mContext.getSystemService(MediaCommunicationManager.class);
+            mMediaSessionManager = null;
+        } else {
+            mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
+            mCommunicationManager = null;
+        }
         // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
         mResultHandler = new Handler(context.getMainLooper());
         mClosed = false;
@@ -315,6 +325,14 @@
         return mCallback;
     }
 
+    boolean isTrustedForMediaControl(RemoteUserInfo remoteUserInfo) {
+        if (SdkLevel.isAtLeastS()) {
+            return mCommunicationManager.isTrustedForMediaControl(remoteUserInfo);
+        } else {
+            return mMediaSessionManager.isTrustedForMediaControl(remoteUserInfo);
+        }
+    }
+
     void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) {
         synchronized (mLock) {
             if (mForegroundServiceEventCallback == callback) {
@@ -350,7 +368,7 @@
 
         final ControllerInfo controllerInfo = new ControllerInfo(
                 remoteUserInfo,
-                mCommunicationManager.isTrustedForMediaControl(remoteUserInfo),
+                isTrustedForMediaControl(remoteUserInfo),
                 controller,
                 connectionHints);
         mCallbackExecutor.execute(() -> {
@@ -606,9 +624,15 @@
             // Notify framework about the newly create session after the constructor is finished.
             // Otherwise, framework may access the session before the initialization is finished.
             try {
-                MediaCommunicationManager manager =
-                        mContext.getSystemService(MediaCommunicationManager.class);
-                manager.notifySession2Created(session2.getToken());
+                if (SdkLevel.isAtLeastS()) {
+                    MediaCommunicationManager manager =
+                            mContext.getSystemService(MediaCommunicationManager.class);
+                    manager.notifySession2Created(session2.getToken());
+                } else {
+                    MediaSessionManager manager =
+                            mContext.getSystemService(MediaSessionManager.class);
+                    manager.notifySession2Created(session2.getToken());
+                }
             } catch (Exception e) {
                 session2.close();
                 throw e;
diff --git a/core/api/current.txt b/core/api/current.txt
index a5d6444..6070cc7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1072,6 +1072,7 @@
     field public static final int panelTextAppearance = 16842850; // 0x1010062
     field public static final int parentActivityName = 16843687; // 0x10103a7
     field @Deprecated public static final int password = 16843100; // 0x101015c
+    field public static final int passwordsActivity = 16844351; // 0x101063f
     field public static final int path = 16842794; // 0x101002a
     field public static final int pathAdvancedPattern = 16844320; // 0x1010620
     field public static final int pathData = 16843781; // 0x1010405
@@ -8379,7 +8380,6 @@
   public class AppWidgetHostView extends android.widget.FrameLayout {
     ctor public AppWidgetHostView(android.content.Context);
     ctor public AppWidgetHostView(android.content.Context, int, int);
-    method public void clearCurrentSize();
     method public int getAppWidgetId();
     method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo();
     method public static android.graphics.Rect getDefaultPaddingForWidget(android.content.Context, android.content.ComponentName, android.graphics.Rect);
@@ -8389,13 +8389,12 @@
     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);
     method public void updateAppWidget(android.widget.RemoteViews);
     method public void updateAppWidgetOptions(android.os.Bundle);
     method @Deprecated public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
-    method public void updateAppWidgetSize(@NonNull android.os.Bundle, @NonNull java.util.List<android.graphics.PointF>);
+    method public void updateAppWidgetSize(@NonNull android.os.Bundle, @NonNull java.util.List<android.util.SizeF>);
   }
 
   public class AppWidgetManager {
@@ -9724,7 +9723,7 @@
   }
 
   public final class CompanionDeviceManager {
-    method public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler);
+    method @RequiresPermission(value=android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler);
     method public void disassociate(@NonNull String);
     method @NonNull public java.util.List<java.lang.String> getAssociations();
     method public boolean hasNotificationAccess(android.content.ComponentName);
@@ -10334,6 +10333,7 @@
     method @Deprecated public abstract void clearWallpaper() throws java.io.IOException;
     method @NonNull public android.content.Context createAttributionContext(@Nullable String);
     method public abstract android.content.Context createConfigurationContext(@NonNull android.content.res.Configuration);
+    method @NonNull public android.content.Context createContext(@NonNull android.content.ContextParams);
     method public abstract android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.Context createDeviceProtectedStorageContext();
     method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
@@ -10556,6 +10556,19 @@
     field public static final String WINDOW_SERVICE = "window";
   }
 
+  public final class ContextParams {
+    method @Nullable public String getAttributionTag();
+    method @Nullable public String getReceiverAttributionTag();
+    method @Nullable public String getReceiverPackage();
+  }
+
+  public static final class ContextParams.Builder {
+    ctor public ContextParams.Builder();
+    method @NonNull public android.content.ContextParams build();
+    method @NonNull public android.content.ContextParams.Builder setAttributionTag(@NonNull String);
+    method @NonNull public android.content.ContextParams.Builder setReceiverPackage(@NonNull String, @Nullable String);
+  }
+
   public class ContextWrapper extends android.content.Context {
     ctor public ContextWrapper(android.content.Context);
     method protected void attachBaseContext(android.content.Context);
@@ -11777,6 +11790,7 @@
     method public boolean isResourceOverlay();
     method public boolean isVirtualPreload();
     method public CharSequence loadDescription(android.content.pm.PackageManager);
+    field public static final int CATEGORY_ACCESSIBILITY = 8; // 0x8
     field public static final int CATEGORY_AUDIO = 1; // 0x1
     field public static final int CATEGORY_GAME = 0; // 0x0
     field public static final int CATEGORY_IMAGE = 3; // 0x3
@@ -12890,18 +12904,18 @@
   }
 
   public class ShortcutManager {
-    method public boolean addDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
-    method public android.content.Intent createShortcutResultIntent(@NonNull android.content.pm.ShortcutInfo);
+    method @WorkerThread public boolean addDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
+    method @WorkerThread public android.content.Intent createShortcutResultIntent(@NonNull android.content.pm.ShortcutInfo);
     method public void disableShortcuts(@NonNull java.util.List<java.lang.String>);
     method public void disableShortcuts(@NonNull java.util.List<java.lang.String>, CharSequence);
     method public void enableShortcuts(@NonNull java.util.List<java.lang.String>);
-    method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
+    method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
     method public int getIconMaxHeight();
     method public int getIconMaxWidth();
-    method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
+    method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
     method public int getMaxShortcutCountPerActivity();
-    method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
-    method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(int);
+    method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
+    method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(int);
     method public boolean isRateLimitingActive();
     method public boolean isRequestPinShortcutSupported();
     method public void pushDynamicShortcut(@NonNull android.content.pm.ShortcutInfo);
@@ -12909,10 +12923,10 @@
     method public void removeDynamicShortcuts(@NonNull java.util.List<java.lang.String>);
     method public void removeLongLivedShortcuts(@NonNull java.util.List<java.lang.String>);
     method public void reportShortcutUsed(String);
-    method public boolean requestPinShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.content.IntentSender);
-    method public boolean setDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
+    method @WorkerThread public boolean requestPinShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.content.IntentSender);
+    method @WorkerThread public boolean setDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
     method public void updateShortcutVisibility(@NonNull String, @Nullable byte[], boolean);
-    method public boolean updateShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
+    method @WorkerThread public boolean updateShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
     field public static final int FLAG_MATCH_CACHED = 8; // 0x8
     field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
     field public static final int FLAG_MATCH_MANIFEST = 1; // 0x1
@@ -12956,6 +12970,27 @@
 
 }
 
+package android.content.pm.verify.domain {
+
+  public final class DomainVerificationManager {
+    method @Nullable public android.content.pm.verify.domain.DomainVerificationUserState getDomainVerificationUserState(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+  }
+
+  public final class DomainVerificationUserState implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getHostToStateMap();
+    method @NonNull public String getPackageName();
+    method @NonNull public android.os.UserHandle getUser();
+    method @NonNull public boolean isLinkHandlingAllowed();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationUserState> CREATOR;
+    field public static final int DOMAIN_STATE_NONE = 0; // 0x0
+    field public static final int DOMAIN_STATE_SELECTED = 1; // 0x1
+    field public static final int DOMAIN_STATE_VERIFIED = 2; // 0x2
+  }
+
+}
+
 package android.content.res {
 
   public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -17544,6 +17579,9 @@
   public class BiometricManager {
     method @Deprecated @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate();
     method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate(int);
+    method @Nullable @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public CharSequence getButtonLabel(int);
+    method @Nullable @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public CharSequence getPromptMessage(int);
+    method @Nullable @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public CharSequence getSettingName(int);
     field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
     field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
     field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
@@ -17755,6 +17793,7 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_BLACK_LEVEL_PATTERN;
@@ -18359,9 +18398,20 @@
     field public static final int MAX_THUMBNAIL_DIMENSION = 256; // 0x100
   }
 
+  public class MultiResolutionImageReader implements java.lang.AutoCloseable {
+    method public void close();
+    method protected void finalize();
+    method public void flush();
+    method @NonNull public android.hardware.camera2.params.MultiResolutionStreamInfo getStreamInfoForImageReader(@NonNull android.media.ImageReader);
+    method @NonNull public android.view.Surface getSurface();
+    method @NonNull public static android.hardware.camera2.MultiResolutionImageReader newInstance(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int, @IntRange(from=1) int);
+    method public void setOnImageAvailableListener(@Nullable android.media.ImageReader.OnImageAvailableListener, @Nullable java.util.concurrent.Executor);
+  }
+
   public final class TotalCaptureResult extends android.hardware.camera2.CaptureResult {
     method @NonNull public java.util.List<android.hardware.camera2.CaptureResult> getPartialResults();
-    method public java.util.Map<java.lang.String,android.hardware.camera2.CaptureResult> getPhysicalCameraResults();
+    method @Deprecated public java.util.Map<java.lang.String,android.hardware.camera2.CaptureResult> getPhysicalCameraResults();
+    method @NonNull public java.util.Map<java.lang.String,android.hardware.camera2.TotalCaptureResult> getPhysicalCameraTotalResults();
   }
 
 }
@@ -18410,9 +18460,11 @@
 
   public final class InputConfiguration {
     ctor public InputConfiguration(int, int, int);
+    ctor public InputConfiguration(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int);
     method public int getFormat();
     method public int getHeight();
     method public int getWidth();
+    method public boolean isMultiResolution();
   }
 
   public final class LensShadingMap {
@@ -18455,6 +18507,20 @@
     field public static final int METERING_WEIGHT_MIN = 0; // 0x0
   }
 
+  public final class MultiResolutionStreamConfigurationMap {
+    method @NonNull public int[] getInputFormats();
+    method @NonNull public java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo> getInputInfo(int);
+    method @NonNull public int[] getOutputFormats();
+    method @NonNull public java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo> getOutputInfo(int);
+  }
+
+  public class MultiResolutionStreamInfo {
+    ctor public MultiResolutionStreamInfo(int, int, @NonNull String);
+    method public int getHeight();
+    method @NonNull public String getPhysicalCameraId();
+    method public int getWidth();
+  }
+
   public final class OisSample {
     ctor public OisSample(long, float, float);
     method public long getTimestamp();
@@ -18467,6 +18533,7 @@
     ctor public OutputConfiguration(int, @NonNull android.view.Surface);
     ctor public OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>);
     method public void addSurface(@NonNull android.view.Surface);
+    method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
     method public int describeContents();
     method public void enableSurfaceSharing();
     method public int getMaxSharedSurfaceCount();
@@ -26732,7 +26799,22 @@
 
   public class VcnManager {
     method @RequiresPermission("carrier privileges") public void clearVcnConfig(@NonNull android.os.ParcelUuid) throws java.io.IOException;
+    method public void registerVcnStatusCallback(@NonNull android.os.ParcelUuid, @NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnStatusCallback);
     method @RequiresPermission("carrier privileges") public void setVcnConfig(@NonNull android.os.ParcelUuid, @NonNull android.net.vcn.VcnConfig) throws java.io.IOException;
+    method public void unregisterVcnStatusCallback(@NonNull android.net.vcn.VcnManager.VcnStatusCallback);
+    field public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1; // 0x1
+    field public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0; // 0x0
+    field public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2; // 0x2
+    field public static final int VCN_STATUS_CODE_ACTIVE = 2; // 0x2
+    field public static final int VCN_STATUS_CODE_INACTIVE = 1; // 0x1
+    field public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0; // 0x0
+    field public static final int VCN_STATUS_CODE_SAFE_MODE = 3; // 0x3
+  }
+
+  public abstract static class VcnManager.VcnStatusCallback {
+    ctor public VcnManager.VcnStatusCallback();
+    method public abstract void onGatewayConnectionError(@NonNull int[], int, @Nullable Throwable);
+    method public abstract void onVcnStatusChanged(int);
   }
 
 }
@@ -30282,6 +30364,7 @@
     field public static final String HARDWARE;
     field public static final String HOST;
     field public static final String ID;
+    field public static final boolean IS_DEBUGGABLE;
     field public static final String MANUFACTURER;
     field public static final String MODEL;
     field @NonNull public static final String ODM_SKU;
@@ -30440,19 +30523,10 @@
   public abstract class CombinedVibrationEffect implements android.os.Parcelable {
     method @NonNull public static android.os.CombinedVibrationEffect createSynced(@NonNull android.os.VibrationEffect);
     method public int describeContents();
-    method @NonNull public static android.os.CombinedVibrationEffect.SequentialCombination startSequential();
     method @NonNull public static android.os.CombinedVibrationEffect.SyncedCombination startSynced();
     field @NonNull public static final android.os.Parcelable.Creator<android.os.CombinedVibrationEffect> CREATOR;
   }
 
-  public static final class CombinedVibrationEffect.SequentialCombination {
-    method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(int, @NonNull android.os.VibrationEffect);
-    method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(int, @NonNull android.os.VibrationEffect, int);
-    method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(@NonNull android.os.CombinedVibrationEffect);
-    method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(@NonNull android.os.CombinedVibrationEffect, int);
-    method @NonNull public android.os.CombinedVibrationEffect combine();
-  }
-
   public static final class CombinedVibrationEffect.SyncedCombination {
     method @NonNull public android.os.CombinedVibrationEffect.SyncedCombination addVibrator(int, @NonNull android.os.VibrationEffect);
     method @NonNull public android.os.CombinedVibrationEffect combine();
@@ -41489,29 +41563,29 @@
     field public static final char WILD = 78; // 0x004e 'N'
   }
 
-  public class PhoneStateListener {
-    ctor public PhoneStateListener();
+  @Deprecated public class PhoneStateListener {
+    ctor @Deprecated public PhoneStateListener();
     ctor @Deprecated public PhoneStateListener(@NonNull java.util.concurrent.Executor);
-    method public void onActiveDataSubscriptionIdChanged(int);
-    method public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
-    method public void onCallForwardingIndicatorChanged(boolean);
-    method public void onCallStateChanged(int, String);
-    method public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
-    method public void onCellLocationChanged(android.telephony.CellLocation);
-    method public void onDataActivity(int);
-    method public void onDataConnectionStateChanged(int);
-    method public void onDataConnectionStateChanged(int, int);
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
-    method public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
-    method public void onMessageWaitingIndicatorChanged(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
-    method public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
-    method public void onServiceStateChanged(android.telephony.ServiceState);
+    method @Deprecated public void onActiveDataSubscriptionIdChanged(int);
+    method @Deprecated public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
+    method @Deprecated public void onCallForwardingIndicatorChanged(boolean);
+    method @Deprecated public void onCallStateChanged(int, String);
+    method @Deprecated public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
+    method @Deprecated public void onCellLocationChanged(android.telephony.CellLocation);
+    method @Deprecated public void onDataActivity(int);
+    method @Deprecated public void onDataConnectionStateChanged(int);
+    method @Deprecated public void onDataConnectionStateChanged(int, int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
+    method @Deprecated public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
+    method @Deprecated public void onMessageWaitingIndicatorChanged(boolean);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
+    method @Deprecated public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
+    method @Deprecated public void onServiceStateChanged(android.telephony.ServiceState);
     method @Deprecated public void onSignalStrengthChanged(int);
-    method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
-    method public void onUserMobileDataStateChanged(boolean);
+    method @Deprecated public void onSignalStrengthsChanged(android.telephony.SignalStrength);
+    method @Deprecated public void onUserMobileDataStateChanged(boolean);
     field @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
@@ -41525,7 +41599,7 @@
     field @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
     field @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
-    field public static final int LISTEN_NONE = 0; // 0x0
+    field @Deprecated public static final int LISTEN_NONE = 0; // 0x0
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
     field @Deprecated public static final int LISTEN_SERVICE_STATE = 1; // 0x1
@@ -41534,90 +41608,6 @@
     field @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000
   }
 
-  public static interface PhoneStateListener.ActiveDataSubscriptionIdChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int);
-  }
-
-  public static interface PhoneStateListener.AlwaysReportedSignalStrengthChangedListener {
-    method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
-  }
-
-  public static interface PhoneStateListener.BarringInfoChangedListener {
-    method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
-  }
-
-  public static interface PhoneStateListener.CallDisconnectCauseChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
-  }
-
-  public static interface PhoneStateListener.CallForwardingIndicatorChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean);
-  }
-
-  public static interface PhoneStateListener.CallStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String);
-  }
-
-  public static interface PhoneStateListener.CarrierNetworkChangeListener {
-    method public void onCarrierNetworkChange(boolean);
-  }
-
-  public static interface PhoneStateListener.CellInfoChangedListener {
-    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>);
-  }
-
-  public static interface PhoneStateListener.CellLocationChangedListener {
-    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(@NonNull android.telephony.CellLocation);
-  }
-
-  public static interface PhoneStateListener.DataActivationStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int);
-  }
-
-  public static interface PhoneStateListener.DataActivityListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int);
-  }
-
-  public static interface PhoneStateListener.DataConnectionStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int);
-  }
-
-  public static interface PhoneStateListener.DisplayInfoChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
-  }
-
-  public static interface PhoneStateListener.EmergencyNumberListChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
-  }
-
-  public static interface PhoneStateListener.ImsCallDisconnectCauseChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
-  }
-
-  public static interface PhoneStateListener.MessageWaitingIndicatorChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
-  }
-
-  public static interface PhoneStateListener.PreciseDataConnectionStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
-  }
-
-  public static interface PhoneStateListener.RegistrationFailedListener {
-    method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
-  }
-
-  public static interface PhoneStateListener.ServiceStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState);
-  }
-
-  public static interface PhoneStateListener.SignalStrengthsChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
-  }
-
-  public static interface PhoneStateListener.UserMobileDataStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean);
-  }
-
   public final class PhysicalChannelConfig implements android.os.Parcelable {
     method public int describeContents();
     method @IntRange(from=1, to=261) public int getBand();
@@ -42092,6 +42082,94 @@
     method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence);
   }
 
+  public class TelephonyCallback {
+    ctor public TelephonyCallback();
+  }
+
+  public static interface TelephonyCallback.ActiveDataSubscriptionIdListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int);
+  }
+
+  public static interface TelephonyCallback.AlwaysReportedSignalStrengthListener {
+    method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
+  }
+
+  public static interface TelephonyCallback.BarringInfoListener {
+    method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
+  }
+
+  public static interface TelephonyCallback.CallDisconnectCauseListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
+  }
+
+  public static interface TelephonyCallback.CallForwardingIndicatorListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean);
+  }
+
+  public static interface TelephonyCallback.CallStateListener {
+    method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String);
+  }
+
+  public static interface TelephonyCallback.CarrierNetworkListener {
+    method public void onCarrierNetworkChange(boolean);
+  }
+
+  public static interface TelephonyCallback.CellInfoListener {
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>);
+  }
+
+  public static interface TelephonyCallback.CellLocationListener {
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(@NonNull android.telephony.CellLocation);
+  }
+
+  public static interface TelephonyCallback.DataActivationStateListener {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int);
+  }
+
+  public static interface TelephonyCallback.DataActivityListener {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int);
+  }
+
+  public static interface TelephonyCallback.DataConnectionStateListener {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int);
+  }
+
+  public static interface TelephonyCallback.DisplayInfoListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
+  }
+
+  public static interface TelephonyCallback.EmergencyNumberListListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
+  }
+
+  public static interface TelephonyCallback.ImsCallDisconnectCauseListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
+  }
+
+  public static interface TelephonyCallback.MessageWaitingIndicatorListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
+  }
+
+  public static interface TelephonyCallback.PreciseDataConnectionStateListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
+  }
+
+  public static interface TelephonyCallback.RegistrationFailedListener {
+    method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
+  }
+
+  public static interface TelephonyCallback.ServiceStateListener {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState);
+  }
+
+  public static interface TelephonyCallback.SignalStrengthsListener {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
+  }
+
+  public static interface TelephonyCallback.UserMobileDataStateListener {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean);
+  }
+
   public final class TelephonyDisplayInfo implements android.os.Parcelable {
     method public int describeContents();
     method public int getNetworkType();
@@ -42204,7 +42282,7 @@
     method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
     method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
-    method public void registerPhoneStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.PhoneStateListener);
+    method public void registerTelephonyCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyCallback);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
     method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
     method public void sendDialerSpecialCode(String);
@@ -42228,7 +42306,7 @@
     method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
     method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int);
-    method public void unregisterPhoneStateListener(@NonNull android.telephony.PhoneStateListener);
+    method public void unregisterTelephonyCallback(@NonNull android.telephony.TelephonyCallback);
     method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
     method public void uploadCallComposerPicture(@NonNull java.nio.file.Path, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>);
     method public void uploadCallComposerPicture(@NonNull java.io.InputStream, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>);
@@ -45891,11 +45969,14 @@
     method public static android.util.Size parseSize(String) throws java.lang.NumberFormatException;
   }
 
-  public final class SizeF {
+  public final class SizeF implements android.os.Parcelable {
     ctor public SizeF(float, float);
+    method public int describeContents();
     method public float getHeight();
     method public float getWidth();
     method public static android.util.SizeF parseSizeF(String) throws java.lang.NumberFormatException;
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.util.SizeF> CREATOR;
   }
 
   public class SparseArray<E> implements java.lang.Cloneable {
@@ -50992,6 +51073,7 @@
     method public void onDisplayHashError(int);
     method public void onDisplayHashResult(@NonNull android.view.displayhash.DisplayHash);
     field public static final int DISPLAY_HASH_ERROR_INVALID_BOUNDS = -2; // 0xfffffffe
+    field public static final int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5; // 0xfffffffb
     field public static final int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3; // 0xfffffffd
     field public static final int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4; // 0xfffffffc
     field public static final int DISPLAY_HASH_ERROR_UNKNOWN = -1; // 0xffffffff
@@ -54786,7 +54868,7 @@
   public class RemoteViews implements android.view.LayoutInflater.Filter android.os.Parcelable {
     ctor public RemoteViews(String, int);
     ctor public RemoteViews(android.widget.RemoteViews, android.widget.RemoteViews);
-    ctor public RemoteViews(@NonNull java.util.Map<android.graphics.PointF,android.widget.RemoteViews>);
+    ctor public RemoteViews(@NonNull java.util.Map<android.util.SizeF,android.widget.RemoteViews>);
     ctor public RemoteViews(android.widget.RemoteViews);
     ctor public RemoteViews(android.os.Parcel);
     method public void addView(@IdRes int, android.widget.RemoteViews);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index d6786f8..ad2942a 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -35,7 +35,7 @@
   }
 
   public final class PendingIntent implements android.os.Parcelable {
-    method @Nullable @RequiresPermission(android.Manifest.permission.GET_INTENT_SENDER_INTENT) public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.GET_INTENT_SENDER_INTENT) public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
   }
 
   public class StatusBarManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 998b114..3a238e2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -358,6 +358,7 @@
     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
+    field public static final int config_systemWifiCoexManager = 17039407; // 0x104002f
   }
 
   public static final class R.style {
@@ -2702,7 +2703,7 @@
   }
 
   public class ShortcutManager {
-    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS) public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS) @WorkerThread public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter);
     method public boolean hasShareTargets(@NonNull String);
   }
 
@@ -2789,13 +2790,12 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationInfo> CREATOR;
   }
 
-  public interface DomainVerificationManager {
+  public final class DomainVerificationManager {
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @Nullable @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public android.content.pm.verify.domain.DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public java.util.List<android.content.pm.verify.domain.DomainOwner> getOwnersForDomain(@NonNull String);
-    method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> getValidVerificationPackageNames();
     method public static boolean isStateModifiable(int);
     method public static boolean isStateVerified(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> queryValidVerificationPackageNames();
     method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
     method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public void setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -2812,18 +2812,8 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationRequest> CREATOR;
   }
 
-  public final class DomainVerificationUserSelection implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getHostToStateMap();
+  public final class DomainVerificationUserState implements android.os.Parcelable {
     method @NonNull public java.util.UUID getIdentifier();
-    method @NonNull public String getPackageName();
-    method @NonNull public android.os.UserHandle getUser();
-    method @NonNull public boolean isLinkHandlingAllowed();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationUserSelection> CREATOR;
-    field public static final int DOMAIN_STATE_NONE = 0; // 0x0
-    field public static final int DOMAIN_STATE_SELECTED = 1; // 0x1
-    field public static final int DOMAIN_STATE_VERIFIED = 2; // 0x2
   }
 
 }
@@ -3460,6 +3450,10 @@
     field @Deprecated public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
   }
 
+  public static final class LightsRequest.Builder {
+    method @Deprecated @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState);
+  }
+
 }
 
 package android.hardware.location {
@@ -8845,6 +8839,8 @@
     field public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
     field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
     field public static final String NAMESPACE_SCHEDULER = "scheduler";
+    field public static final String NAMESPACE_STATSD_JAVA = "statsd_java";
+    field public static final String NAMESPACE_STATSD_JAVA_BOOT = "statsd_java_boot";
     field public static final String NAMESPACE_STATSD_NATIVE = "statsd_native";
     field public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
     field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
@@ -11081,51 +11077,16 @@
     method public static boolean isVoiceMailNumber(@NonNull android.content.Context, int, @Nullable String);
   }
 
-  public class PhoneStateListener {
-    method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
+  @Deprecated public class PhoneStateListener {
+    method @Deprecated public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
     method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
-    method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
+    method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
     method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
-    method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
-    method public void onRadioPowerStateChanged(int);
-    method public void onSrvccStateChanged(int);
-    method public void onVoiceActivationStateChanged(int);
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17
-    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35; // 0x23
-    field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa
-    field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
-    field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
-    field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
-    field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
-    field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
-    field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13
-    field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; // 0xe
-    field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22
-    field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3
-    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf
-    field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d
-    field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; // 0x1e
-    field public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; // 0x16
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; // 0x21
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; // 0xc
-    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd
-    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18
-    field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f
-    field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1
-    field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9
-    field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2
-    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10
-    field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14
-    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12
+    method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
+    method @Deprecated public void onRadioPowerStateChanged(int);
+    method @Deprecated public void onSrvccStateChanged(int);
+    method @Deprecated public void onVoiceActivationStateChanged(int);
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
@@ -11135,50 +11096,6 @@
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
   }
 
-  public static interface PhoneStateListener.AllowedNetworkTypesChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(@NonNull java.util.Map<java.lang.Integer,java.lang.Long>);
-  }
-
-  public static interface PhoneStateListener.CallAttributesChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
-  }
-
-  public static interface PhoneStateListener.DataEnabledChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int);
-  }
-
-  public static interface PhoneStateListener.OutgoingEmergencyCallListener {
-    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
-  }
-
-  public static interface PhoneStateListener.OutgoingEmergencySmsListener {
-    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
-  }
-
-  public static interface PhoneStateListener.PhoneCapabilityChangedListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
-  }
-
-  public static interface PhoneStateListener.PhysicalChannelConfigChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
-  }
-
-  public static interface PhoneStateListener.PreciseCallStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
-  }
-
-  public static interface PhoneStateListener.RadioPowerStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int);
-  }
-
-  public static interface PhoneStateListener.SrvccStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int);
-  }
-
-  public static interface PhoneStateListener.VoiceActivationStateChangedListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int);
-  }
-
   public final class PinResult implements android.os.Parcelable {
     method public int describeContents();
     method public int getAttemptsRemaining();
@@ -11516,6 +11433,88 @@
     method @Deprecated public static android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime);
   }
 
+  public class TelephonyCallback {
+    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17
+    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35; // 0x23
+    field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa
+    field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a
+    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
+    field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
+    field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
+    field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
+    field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
+    field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13
+    field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8
+    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; // 0xe
+    field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22
+    field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15
+    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c
+    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3
+    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf
+    field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d
+    field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; // 0x1e
+    field public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; // 0x16
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; // 0x21
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; // 0xc
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd
+    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18
+    field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f
+    field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1
+    field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9
+    field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2
+    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10
+    field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14
+    field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12
+  }
+
+  public static interface TelephonyCallback.AllowedNetworkTypesListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(@NonNull java.util.Map<java.lang.Integer,java.lang.Long>);
+  }
+
+  public static interface TelephonyCallback.CallAttributesListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
+  }
+
+  public static interface TelephonyCallback.DataEnabledListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int);
+  }
+
+  public static interface TelephonyCallback.OutgoingEmergencyCallListener {
+    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
+  }
+
+  public static interface TelephonyCallback.OutgoingEmergencySmsListener {
+    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
+  }
+
+  public static interface TelephonyCallback.PhoneCapabilityListener {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
+  }
+
+  public static interface TelephonyCallback.PhysicalChannelConfigListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
+  }
+
+  public static interface TelephonyCallback.PreciseCallStateListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
+  }
+
+  public static interface TelephonyCallback.RadioPowerStateListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int);
+  }
+
+  public static interface TelephonyCallback.SrvccStateListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int);
+  }
+
+  public static interface TelephonyCallback.VoiceActivationStateListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int);
+  }
+
   public final class TelephonyHistogram implements android.os.Parcelable {
     ctor public TelephonyHistogram(int, int, int);
     ctor public TelephonyHistogram(android.telephony.TelephonyHistogram);
@@ -13059,7 +13058,7 @@
     method @NonNull public String getServiceId();
     method @NonNull public String getServiceVersion();
     method @NonNull public String getStatus();
-    method @Nullable public String getTimestamp();
+    method @Nullable public java.time.Instant getTime();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple> CREATOR;
     field public static final String SERVICE_ID_CALL_COMPOSER = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer";
@@ -13086,7 +13085,7 @@
     method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setContactUri(@NonNull android.net.Uri);
     method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceCapabilities(@NonNull android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities);
     method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceDescription(@NonNull String);
-    method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTimestamp(@NonNull String);
+    method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTime(@NonNull java.time.Instant);
   }
 
   public static final class RcsContactPresenceTuple.ServiceCapabilities implements android.os.Parcelable {
@@ -13142,7 +13141,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
-    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
     field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
     field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
@@ -13305,10 +13304,12 @@
   public final class SipMessage implements android.os.Parcelable {
     ctor public SipMessage(@NonNull String, @NonNull String, @NonNull byte[]);
     method public int describeContents();
+    method @Nullable public String getCallIdParameter();
     method @NonNull public byte[] getContent();
     method @NonNull public byte[] getEncodedMessage();
     method @NonNull public String getHeaderSection();
     method @NonNull public String getStartLine();
+    method @Nullable public String getViaBranchParameter();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR;
   }
@@ -13618,7 +13619,7 @@
     ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
     method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
     method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.List<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback);
-    method public void subscribeForCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
+    method public void subscribeForCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
     field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3
     field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1
     field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 61c8314..a57d824 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -302,7 +302,7 @@
   }
 
   public final class PendingIntent implements android.os.Parcelable {
-    method @Nullable @RequiresPermission("android.permission.GET_INTENT_SENDER_INTENT") public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
+    method @NonNull @RequiresPermission("android.permission.GET_INTENT_SENDER_INTENT") public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
     field @Deprecated public static final int FLAG_MUTABLE_UNAUDITED = 33554432; // 0x2000000
   }
 
@@ -1493,6 +1493,7 @@
 
   public abstract class CombinedVibrationEffect implements android.os.Parcelable {
     method public abstract long getDuration();
+    method @NonNull public static android.os.CombinedVibrationEffect.SequentialCombination startSequential();
   }
 
   public static final class CombinedVibrationEffect.Mono extends android.os.CombinedVibrationEffect {
@@ -1510,6 +1511,14 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.os.CombinedVibrationEffect.Sequential> CREATOR;
   }
 
+  public static final class CombinedVibrationEffect.SequentialCombination {
+    method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(int, @NonNull android.os.VibrationEffect);
+    method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(int, @NonNull android.os.VibrationEffect, int);
+    method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(@NonNull android.os.CombinedVibrationEffect);
+    method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(@NonNull android.os.CombinedVibrationEffect, int);
+    method @NonNull public android.os.CombinedVibrationEffect combine();
+  }
+
   public static final class CombinedVibrationEffect.Stereo extends android.os.CombinedVibrationEffect {
     method public long getDuration();
     method @NonNull public android.util.SparseArray<android.os.VibrationEffect> getEffects();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 730fce9..e7751b8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -176,6 +176,8 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.autofill.AutofillId;
+import android.view.contentcapture.IContentCaptureManager;
+import android.view.contentcapture.IContentCaptureOptionsCallback;
 import android.view.translation.TranslationSpec;
 import android.webkit.WebView;
 import android.window.SplashScreen;
@@ -512,6 +514,8 @@
 
     boolean mHasImeComponent = false;
 
+    private IContentCaptureOptionsCallback.Stub mContentCaptureOptionsCallback = null;
+
     /** Activity client record, used for bookkeeping for the real {@link Activity} instance. */
     public static final class ActivityClientRecord {
         @UnsupportedAppUsage
@@ -1939,6 +1943,7 @@
         public static final int PURGE_RESOURCES = 161;
         public static final int ATTACH_STARTUP_AGENTS = 162;
         public static final int UPDATE_UI_TRANSLATION_STATE = 163;
+        public static final int SET_CONTENT_CAPTURE_OPTIONS_CALLBACK = 164;
 
         public static final int INSTRUMENT_WITHOUT_RESTART = 170;
         public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171;
@@ -1988,6 +1993,8 @@
                     case PURGE_RESOURCES: return "PURGE_RESOURCES";
                     case ATTACH_STARTUP_AGENTS: return "ATTACH_STARTUP_AGENTS";
                     case UPDATE_UI_TRANSLATION_STATE: return "UPDATE_UI_TRANSLATION_STATE";
+                    case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK:
+                        return "SET_CONTENT_CAPTURE_OPTIONS_CALLBACK";
                     case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART";
                     case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
                         return "FINISH_INSTRUMENTATION_WITHOUT_RESTART";
@@ -2180,6 +2187,9 @@
                             (TranslationSpec) args.arg3, (TranslationSpec) args.arg4,
                             (List<AutofillId>) args.arg5);
                     break;
+                case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK:
+                    handleSetContentCaptureOptionsCallback((String) msg.obj);
+                    break;
                 case INSTRUMENT_WITHOUT_RESTART:
                     handleInstrumentWithoutRestart((AppBindData) msg.obj);
                     break;
@@ -6795,6 +6805,7 @@
 
             // Propagate Content Capture options
             app.setContentCaptureOptions(data.contentCaptureOptions);
+            sendMessage(H.SET_CONTENT_CAPTURE_OPTIONS_CALLBACK, data.appInfo.packageName);
 
             mInitialApplication = app;
 
@@ -6856,6 +6867,36 @@
         }
     }
 
+    private void handleSetContentCaptureOptionsCallback(String packageName) {
+        if (mContentCaptureOptionsCallback != null) {
+            return;
+        }
+
+        IBinder b = ServiceManager.getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
+        if (b == null) {
+            return;
+        }
+
+        IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b);
+        mContentCaptureOptionsCallback = new IContentCaptureOptionsCallback.Stub() {
+            @Override
+            public void setContentCaptureOptions(ContentCaptureOptions options)
+                    throws RemoteException {
+                if (mInitialApplication != null) {
+                    mInitialApplication.setContentCaptureOptions(options);
+                }
+            }
+        };
+        try {
+            service.registerContentCaptureOptionsCallback(packageName,
+                    mContentCaptureOptionsCallback);
+        } catch (RemoteException e)  {
+            Slog.w(TAG, "registerContentCaptureOptionsCallback() failed: "
+                    + packageName, e);
+            mContentCaptureOptionsCallback = null;
+        }
+    }
+
     private void handleInstrumentWithoutRestart(AppBindData data) {
         try {
             data.compatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index bc79813..fd56c44 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -24,6 +24,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContextParams;
 import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -2606,6 +2607,12 @@
                 compatInfo, mClassLoader, loaders);
     }
 
+    @NonNull
+    @Override
+    public Context createContext(@NonNull ContextParams contextParams) {
+        return this;
+    }
+
     @Override
     public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
         return new ContextImpl(this, mMainThread, mPackageInfo, attributionTag, mSplitName,
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 3a8172e..ef0dcab 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -706,7 +706,7 @@
     boolean stopProfile(int userId);
 
     /** Called by PendingIntent.queryIntentComponents() */
-    List<ResolveInfo> queryIntentComponentsForIntentSender(in IIntentSender sender, int matchFlags);
+    ParceledListSlice queryIntentComponentsForIntentSender(in IIntentSender sender, int matchFlags);
 
     int getUidProcessCapabilities(int uid, in String callingPackage);
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ec2336f..bc24e97 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5303,14 +5303,18 @@
         }
 
         private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) {
-            contentView.setInt(
-                    R.id.expand_button, "setDefaultTextColor", getPrimaryTextColor(p));
-            contentView.setInt(
-                    R.id.expand_button, "setDefaultPillColor", getProtectionColor(p));
-            contentView.setInt(
-                    R.id.expand_button, "setHighlightTextColor", getBackgroundColor(p));
-            contentView.setInt(
-                    R.id.expand_button, "setHighlightPillColor", getAccentColor(p));
+            // set default colors
+            int textColor = getPrimaryTextColor(p);
+            int pillColor = getProtectionColor(p);
+            contentView.setInt(R.id.expand_button, "setDefaultTextColor", textColor);
+            contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor);
+            // Use different highlighted colors except when low-priority mode prevents that
+            if (!p.forceDefaultColor) {
+                textColor = getBackgroundColor(p);
+                pillColor = getAccentColor(p);
+            }
+            contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor);
+            contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
         }
 
         private void bindHeaderChronometerAndTime(RemoteViews contentView,
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 549bd4b..009c936 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -26,7 +26,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemApi.Client;
 import android.annotation.TestApi;
@@ -41,6 +40,7 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
 import android.os.Bundle;
@@ -60,6 +60,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -1239,14 +1240,17 @@
      * @param flags MATCH_* flags from {@link android.content.pm.PackageManager}.
      * @hide
      */
-    @SuppressLint("NullableCollection")
     @RequiresPermission(permission.GET_INTENT_SENDER_INTENT)
     @SystemApi(client = Client.MODULE_LIBRARIES)
     @TestApi
-    public @Nullable List<ResolveInfo> queryIntentComponents(@ResolveInfoFlags int flags) {
+    public @NonNull List<ResolveInfo> queryIntentComponents(@ResolveInfoFlags int flags) {
         try {
-            return ActivityManager.getService()
+            ParceledListSlice<ResolveInfo> parceledList = ActivityManager.getService()
                     .queryIntentComponentsForIntentSender(mTarget, flags);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e16e40b..43c14a9 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -71,7 +71,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutManager;
 import android.content.pm.verify.domain.DomainVerificationManager;
-import android.content.pm.verify.domain.DomainVerificationManagerImpl;
 import android.content.pm.verify.domain.IDomainVerificationManager;
 import android.content.res.Resources;
 import android.content.rollback.RollbackManagerFrameworkInitializer;
@@ -1422,7 +1421,6 @@
                     }
                 });
 
-        // TODO(b/159952358): Only register this service for the domain verification agent?
         registerService(Context.DOMAIN_VERIFICATION_SERVICE, DomainVerificationManager.class,
                 new CachedServiceFetcher<DomainVerificationManager>() {
                     @Override
@@ -1432,7 +1430,7 @@
                                 Context.DOMAIN_VERIFICATION_SERVICE);
                         IDomainVerificationManager service =
                                 IDomainVerificationManager.Stub.asInterface(binder);
-                        return new DomainVerificationManagerImpl(context, service);
+                        return new DomainVerificationManager(context, service);
                     }
                 });
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b919bfc..0635bd0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2997,6 +2997,7 @@
      */
     // TODO(b/173541467): should it throw SecurityException if caller is not admin?
     public boolean isSafeOperation(@OperationSafetyReason int reason) {
+        throwIfParentInstance("isSafeOperation");
         if (mService == null) return false;
 
         try {
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 22492cc..94a4fde0 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -403,7 +403,7 @@
     public void onFullBackup(FullBackupDataOutput data) throws IOException {
         FullBackup.BackupScheme backupScheme = FullBackup.getBackupScheme(this,
                 mOperationType);
-        if (!isDeviceToDeviceMigration() && !backupScheme.isFullBackupContentEnabled()) {
+        if (!backupScheme.isFullBackupEnabled(data.getTransportFlags())) {
             return;
         }
 
@@ -911,7 +911,7 @@
         }
 
         FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this, mOperationType);
-        if (!bs.isFullBackupContentEnabled()) {
+        if (!bs.isFullRestoreEnabled()) {
             if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
                 Log.v(FullBackup.TAG_XML_PARSER,
                         "onRestoreFile \"" + destination.getCanonicalPath()
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 829b6cd..9b543b5 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -99,6 +99,8 @@
     public static final String FLAG_REQUIRED_DEVICE_TO_DEVICE_TRANSFER = "deviceToDeviceTransfer";
     public static final String FLAG_REQUIRED_FAKE_CLIENT_SIDE_ENCRYPTION =
             "fakeClientSideEncryption";
+    private static final String FLAG_DISABLE_IF_NO_ENCRYPTION_CAPABILITIES
+            = "disableIfNoEncryptionCapabilities";
 
     /**
      * When  this change is enabled, include / exclude rules specified via
@@ -307,6 +309,10 @@
         // lazy initialized, only when needed
         private StorageVolume[] mVolumes = null;
 
+        // Properties the transport must have (e.g. encryption) for the operation to go ahead.
+        @Nullable private Integer mRequiredTransportFlags;
+        @Nullable private Boolean mIsUsingNewScheme;
+
         /**
          * Parse out the semantic domains into the correct physical location.
          */
@@ -453,6 +459,35 @@
             }
         }
 
+        boolean isFullBackupEnabled(int transportFlags) {
+            try {
+                if (isUsingNewScheme()) {
+                    int requiredTransportFlags = getRequiredTransportFlags();
+                    // All bits that are set in requiredTransportFlags must be set in
+                    // transportFlags.
+                    return (transportFlags & requiredTransportFlags) == requiredTransportFlags;
+                }
+            } catch (IOException | XmlPullParserException e) {
+                Slog.w(TAG, "Failed to interpret the backup scheme: " + e);
+                return false;
+            }
+
+            return isFullBackupContentEnabled();
+        }
+
+        boolean isFullRestoreEnabled() {
+            try {
+                if (isUsingNewScheme()) {
+                    return true;
+                }
+            } catch (IOException | XmlPullParserException e) {
+                Slog.w(TAG, "Failed to interpret the backup scheme: " + e);
+                return false;
+            }
+
+            return isFullBackupContentEnabled();
+        }
+
         boolean isFullBackupContentEnabled() {
             if (mFullBackupContent < 0) {
                 // android:fullBackupContent="false", bail.
@@ -491,10 +526,30 @@
             return mExcludes;
         }
 
+        private synchronized int getRequiredTransportFlags()
+                throws IOException, XmlPullParserException {
+            if (mRequiredTransportFlags == null) {
+                maybeParseBackupSchemeLocked();
+            }
+
+            return mRequiredTransportFlags;
+        }
+
+        private synchronized boolean isUsingNewScheme()
+                throws IOException, XmlPullParserException {
+            if (mIsUsingNewScheme == null) {
+                maybeParseBackupSchemeLocked();
+            }
+
+            return mIsUsingNewScheme;
+        }
+
         private void maybeParseBackupSchemeLocked() throws IOException, XmlPullParserException {
             // This not being null is how we know that we've tried to parse the xml already.
             mIncludes = new ArrayMap<String, Set<PathWithRequiredFlags>>();
             mExcludes = new ArraySet<PathWithRequiredFlags>();
+            mRequiredTransportFlags = 0;
+            mIsUsingNewScheme = false;
 
             if (mFullBackupContent == 0 && mDataExtractionRules == 0) {
                 // No scheme specified via either new or legacy config, will copy everything.
@@ -535,12 +590,14 @@
                 }
                 if (!mExcludes.isEmpty() || !mIncludes.isEmpty()) {
                     // Found configuration in the new config, we will use it.
+                    mIsUsingNewScheme = true;
                     return;
                 }
             }
 
             if (operationType == OperationType.MIGRATION
                     && CompatChanges.isChangeEnabled(IGNORE_FULL_BACKUP_CONTENT_IN_D2D)) {
+                mIsUsingNewScheme = true;
                 return;
             }
 
@@ -584,13 +641,24 @@
                     continue;
                 }
 
-                // TODO(b/180523028): Parse required attributes for rules (e.g. encryption).
+                parseRequiredTransportFlags(parser, configSection);
                 parseRules(parser, excludes, includes, Optional.of(0), configSection);
             }
 
             logParsingResults(excludes, includes);
         }
 
+        private void parseRequiredTransportFlags(XmlPullParser parser,
+                @ConfigSection String configSection) {
+            if (ConfigSection.CLOUD_BACKUP.equals(configSection)) {
+                String encryptionAttribute = parser.getAttributeValue(/* namespace */ null,
+                        FLAG_DISABLE_IF_NO_ENCRYPTION_CAPABILITIES);
+                if ("true".equals(encryptionAttribute)) {
+                    mRequiredTransportFlags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
+                }
+            }
+        }
+
         @VisibleForTesting
         public void parseBackupSchemeFromXmlLocked(XmlPullParser parser,
                                                    Set<PathWithRequiredFlags> excludes,
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 081f4fd..e6a4656 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -334,10 +334,19 @@
         public static final int LOCUS_ID_SET = 30;
 
         /**
+         * An event type denoting that a component in the package has been used (e.g. broadcast
+         * receiver, service, content provider). This generally matches up with usage that would
+         * cause an app to leave force stop. The component itself is not provided as we are only
+         * interested in whether the package is used, not the component itself.
+         * @hide
+         */
+        public static final int APP_COMPONENT_USED = 31;
+
+        /**
          * Keep in sync with the greatest event type value.
          * @hide
          */
-        public static final int MAX_EVENT_TYPE = 30;
+        public static final int MAX_EVENT_TYPE = 31;
 
         /** @hide */
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index fc54c71..d79fac5 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -39,6 +39,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
+import android.util.SizeF;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.view.Gravity;
@@ -56,7 +57,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.Executor;
 
 /**
@@ -93,7 +93,7 @@
     int mLayoutId = -1;
     private InteractionHandler mInteractionHandler;
     private boolean mOnLightBackground;
-    private PointF mCurrentSize = null;
+    private SizeF mCurrentSize = null;
     private RemoteViews.ColorResources mColorResources = null;
     // Stores the last remote views last inflated.
     private RemoteViews mLastInflatedRemoteViews = null;
@@ -253,9 +253,33 @@
         }
     }
 
+    private SizeF computeSizeFromLayout(int left, int top, int right, int bottom) {
+        Rect padding = getDefaultPadding();
+        float density = getResources().getDisplayMetrics().density;
+        return new SizeF(
+                (right - left - padding.right - padding.left) / density,
+                (bottom - top - padding.bottom - padding.top) / density
+        );
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         try {
+            SizeF oldSize = mCurrentSize;
+            SizeF newSize = computeSizeFromLayout(left, top, right, bottom);
+            mCurrentSize = newSize;
+            if (mLastInflatedRemoteViews != null) {
+                RemoteViews toApply = mLastInflatedRemoteViews.getRemoteViewsToApplyIfDifferent(
+                        oldSize, newSize);
+                if (toApply != null) {
+                    applyRemoteViews(toApply, false);
+                    measureChildWithMargins(mView,
+                            MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
+                            0 /* widthUsed */,
+                            MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY),
+                            0 /* heightUsed */);
+                }
+            }
             super.onLayout(changed, left, top, right, bottom);
         } catch (final RuntimeException e) {
             Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e);
@@ -309,7 +333,7 @@
      *              again. Typically, this will be size of the widget in landscape and portrait.
      *              On some foldables, this might include the size on the outer and inner screens.
      */
-    public void updateAppWidgetSize(@NonNull Bundle newOptions, @NonNull List<PointF> sizes) {
+    public void updateAppWidgetSize(@NonNull Bundle newOptions, @NonNull List<SizeF> sizes) {
         AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext);
 
         Rect padding = getDefaultPadding();
@@ -318,20 +342,20 @@
         float xPaddingDips = (padding.left + padding.right) / density;
         float yPaddingDips = (padding.top + padding.bottom) / density;
 
-        ArrayList<PointF> paddedSizes = new ArrayList<>(sizes.size());
+        ArrayList<SizeF> paddedSizes = new ArrayList<>(sizes.size());
         float minWidth = Float.MAX_VALUE;
         float maxWidth = 0;
         float minHeight = Float.MAX_VALUE;
         float maxHeight = 0;
         for (int i = 0; i < sizes.size(); i++) {
-            PointF size = sizes.get(i);
-            PointF paddedPoint = new PointF(Math.max(0.f, size.x - xPaddingDips),
-                    Math.max(0.f, size.y - yPaddingDips));
-            paddedSizes.add(paddedPoint);
-            minWidth = Math.min(minWidth, paddedPoint.x);
-            maxWidth = Math.max(maxWidth, paddedPoint.x);
-            minHeight = Math.min(minHeight, paddedPoint.y);
-            maxHeight = Math.max(maxHeight, paddedPoint.y);
+            SizeF size = sizes.get(i);
+            SizeF paddedSize = new SizeF(Math.max(0.f, size.getWidth() - xPaddingDips),
+                    Math.max(0.f, size.getHeight() - yPaddingDips));
+            paddedSizes.add(paddedSize);
+            minWidth = Math.min(minWidth, paddedSize.getWidth());
+            maxWidth = Math.max(maxWidth, paddedSize.getWidth());
+            minHeight = Math.min(minHeight, paddedSize.getHeight());
+            maxHeight = Math.max(maxHeight, paddedSize.getHeight());
         }
         if (paddedSizes.equals(
                 widgetManager.getAppWidgetOptions(mAppWidgetId).<PointF>getParcelableArrayList(
@@ -348,35 +372,6 @@
     }
 
     /**
-     * Set the current size of the widget. This should be the full area the AppWidgetHostView is
-     * given. Padding added by the framework will be accounted for automatically.
-     *
-     * This size will be used to choose the appropriate layout the next time the {@link RemoteViews}
-     * is re-inflated, if it was created with {@link RemoteViews#RemoteViews(Map)} .
-     */
-    public void setCurrentSize(@NonNull PointF size) {
-        Rect padding = getDefaultPadding();
-        float density = getResources().getDisplayMetrics().density;
-        float xPaddingDips = (padding.left + padding.right) / density;
-        float yPaddingDips = (padding.top + padding.bottom) / density;
-        PointF newSize = new PointF(size.x - xPaddingDips, size.y - yPaddingDips);
-        if (!newSize.equals(mCurrentSize)) {
-            mCurrentSize = newSize;
-            reapplyLastRemoteViews();
-        }
-    }
-
-    /**
-     * Clear the current size, indicating it is not currently known.
-     */
-    public void clearCurrentSize() {
-        if (mCurrentSize != null) {
-            mCurrentSize = null;
-            reapplyLastRemoteViews();
-        }
-    }
-
-    /**
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -514,23 +509,22 @@
             mLayoutId = -1;
             mViewMode = VIEW_MODE_DEFAULT;
         } else {
+            // Select the remote view we are actually going to apply.
+            RemoteViews rvToApply = remoteViews.getRemoteViewsToApply(mContext, mCurrentSize);
             if (mOnLightBackground) {
-                remoteViews = remoteViews.getDarkTextViews();
+                rvToApply = rvToApply.getDarkTextViews();
             }
 
             if (mAsyncExecutor != null && useAsyncIfPossible) {
-                inflateAsync(remoteViews);
+                inflateAsync(rvToApply);
                 return;
             }
-            // Prepare a local reference to the remote Context so we're ready to
-            // inflate any requested LayoutParams.
-            mRemoteContext = getRemoteContext();
-            int layoutId = remoteViews.getLayoutId();
+            int layoutId = rvToApply.getLayoutId();
             // If our stale view has been prepared to match active, and the new
             // layout matches, try recycling it
             if (content == null && layoutId == mLayoutId) {
                 try {
-                    remoteViews.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
+                    rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
                             mColorResources);
                     content = mView;
                     recycled = true;
@@ -543,7 +537,7 @@
             // Try normal RemoteView inflation
             if (content == null) {
                 try {
-                    content = remoteViews.apply(mContext, this, mInteractionHandler,
+                    content = rvToApply.apply(mContext, this, mInteractionHandler,
                             mCurrentSize, mColorResources);
                     if (LOGD) Log.d(TAG, "had to inflate new layout");
                 } catch (RuntimeException e) {
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 38919f6..a88aed7 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -219,7 +219,7 @@
     public static final String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight";
 
     /**
-     * A bundle extra ({@code List<PointF>}) that contains the list of possible sizes, in dips, a
+     * A bundle extra ({@code List<SizeF>}) that contains the list of possible sizes, in dips, a
      * widget instance can take.
      */
     public static final String OPTION_APPWIDGET_SIZES = "appWidgetSizes";
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 102c98f..b07d6d5 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -60,10 +60,16 @@
     /**
      * Device profile: watch.
      *
+     * If specified, the current request may have a modified UI to highlight that the device being
+     * set up is a specific kind of device, and some extra permissions may be granted to the app
+     * as a result.
+     *
+     * Using it requires declaring uses-permission
+     * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH} in the manifest.
+     *
      * @see AssociationRequest.Builder#setDeviceProfile
      */
-    public static final String DEVICE_PROFILE_WATCH =
-            "android.app.role.COMPANION_DEVICE_WATCH";
+    public static final String DEVICE_PROFILE_WATCH = "android.app.role.COMPANION_DEVICE_WATCH";
 
     /** @hide */
     @StringDef(value = { DEVICE_PROFILE_WATCH })
@@ -107,6 +113,17 @@
      */
     private @Nullable String mDeviceProfilePrivilegesDescription = null;
 
+    /**
+     * The time at which his request was created
+     *
+     * @hide
+     */
+    private long mCreationTime;
+
+    private void onConstructed() {
+        mCreationTime = System.currentTimeMillis();
+    }
+
     /** @hide */
     public void setCallingPackage(@NonNull String pkg) {
         mCallingPackage = pkg;
@@ -187,7 +204,7 @@
             markUsed();
             return new AssociationRequest(
                     mSingleDevice, emptyIfNull(mDeviceFilters),
-                    mDeviceProfile, null, null);
+                    mDeviceProfile, null, null, -1L);
         }
     }
 
@@ -228,6 +245,8 @@
      *   The user-readable description of the device profile's privileges.
      *
      *   Populated by the system.
+     * @param creationTime
+     *   The time at which his request was created
      * @hide
      */
     @DataClass.Generated.Member
@@ -236,7 +255,8 @@
             @NonNull List<DeviceFilter<?>> deviceFilters,
             @Nullable @DeviceProfile String deviceProfile,
             @Nullable String callingPackage,
-            @Nullable String deviceProfilePrivilegesDescription) {
+            @Nullable String deviceProfilePrivilegesDescription,
+            long creationTime) {
         this.mSingleDevice = singleDevice;
         this.mDeviceFilters = deviceFilters;
         com.android.internal.util.AnnotationValidations.validate(
@@ -246,8 +266,9 @@
                 DeviceProfile.class, null, mDeviceProfile);
         this.mCallingPackage = callingPackage;
         this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
+        this.mCreationTime = creationTime;
 
-        // onConstructed(); // You can define this method to get a callback
+        onConstructed();
     }
 
     /**
@@ -284,6 +305,16 @@
         return mDeviceProfilePrivilegesDescription;
     }
 
+    /**
+     * The time at which his request was created
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public long getCreationTime() {
+        return mCreationTime;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -295,7 +326,8 @@
                 "deviceFilters = " + mDeviceFilters + ", " +
                 "deviceProfile = " + mDeviceProfile + ", " +
                 "callingPackage = " + mCallingPackage + ", " +
-                "deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription +
+                "deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription + ", " +
+                "creationTime = " + mCreationTime +
         " }";
     }
 
@@ -316,7 +348,8 @@
                 && Objects.equals(mDeviceFilters, that.mDeviceFilters)
                 && Objects.equals(mDeviceProfile, that.mDeviceProfile)
                 && Objects.equals(mCallingPackage, that.mCallingPackage)
-                && Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription);
+                && Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription)
+                && mCreationTime == that.mCreationTime;
     }
 
     @Override
@@ -331,6 +364,7 @@
         _hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
         _hash = 31 * _hash + Objects.hashCode(mCallingPackage);
         _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription);
+        _hash = 31 * _hash + Long.hashCode(mCreationTime);
         return _hash;
     }
 
@@ -350,6 +384,7 @@
         if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
         if (mCallingPackage != null) dest.writeString(mCallingPackage);
         if (mDeviceProfilePrivilegesDescription != null) dest.writeString(mDeviceProfilePrivilegesDescription);
+        dest.writeLong(mCreationTime);
     }
 
     @Override
@@ -370,6 +405,7 @@
         String deviceProfile = (flg & 0x4) == 0 ? null : in.readString();
         String callingPackage = (flg & 0x8) == 0 ? null : in.readString();
         String deviceProfilePrivilegesDescription = (flg & 0x10) == 0 ? null : in.readString();
+        long creationTime = in.readLong();
 
         this.mSingleDevice = singleDevice;
         this.mDeviceFilters = deviceFilters;
@@ -380,8 +416,9 @@
                 DeviceProfile.class, null, mDeviceProfile);
         this.mCallingPackage = callingPackage;
         this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
+        this.mCreationTime = creationTime;
 
-        // onConstructed(); // You can define this method to get a callback
+        onConstructed();
     }
 
     @DataClass.Generated.Member
@@ -399,10 +436,10 @@
     };
 
     @DataClass.Generated(
-            time = 1611692924843L,
+            time = 1614976943652L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
-            inputSignatures = "private static final  java.lang.String LOG_TAG\npublic static final  java.lang.String DEVICE_PROFILE_WATCH\nprivate  boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\npublic  void setCallingPackage(java.lang.String)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
+            inputSignatures = "private static final  java.lang.String LOG_TAG\npublic static final  java.lang.String DEVICE_PROFILE_WATCH\nprivate  boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate  long mCreationTime\nprivate  void onConstructed()\npublic  void setCallingPackage(java.lang.String)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 59646f10..b441b36 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -16,6 +16,7 @@
 
 package android.companion;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -141,6 +142,10 @@
      * <p>Calling this API requires a uses-feature
      * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
      *
+     * <p>When using {@link AssociationRequest#DEVICE_PROFILE_WATCH watch}
+     * {@link AssociationRequest.Builder#setDeviceProfile profile}, caller must also hold
+     * {@link Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH}</p>
+     *
      * @param request specific details about this request
      * @param callback will be called once there's at least one device found for user to choose from
      * @param handler A handler to control which thread the callback will be delivered on, or null,
@@ -148,6 +153,9 @@
      *
      * @see AssociationRequest
      */
+    @RequiresPermission(
+            value = Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH,
+            conditional = true)
     public void associate(
             @NonNull AssociationRequest request,
             @NonNull Callback callback,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ea6040d..30b3d43 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -370,6 +370,15 @@
     /***********    Hidden flags below this line ***********/
 
     /**
+     * Flag for {@link #bindService}: This flag is only intended to be used by the system to
+     * indicate that a service binding is not considered as real package component usage and should
+     * not generate a {@link android.app.usage.UsageEvents.Event#APP_COMPONENT_USED} event in usage
+     * stats.
+     * @hide
+     */
+    public static final int BIND_NOT_APP_COMPONENT_USAGE = 0x00008000;
+
+    /**
      * 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
@@ -6366,6 +6375,19 @@
     }
 
     /**
+     * Creates a context with specific properties and behaviors.
+     *
+     * @param contextParams Parameters for how the new context should behave.
+     * @return A context with the specified behaviors.
+     *
+     * @see ContextParams
+     */
+    @NonNull
+    public Context createContext(@NonNull ContextParams contextParams) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * Return a new Context object for the current Context but attribute to a different tag.
      * In complex apps attribution tagging can be used to distinguish between separate logical
      * parts.
diff --git a/core/java/android/content/ContextParams.java b/core/java/android/content/ContextParams.java
new file mode 100644
index 0000000..16128a6
--- /dev/null
+++ b/core/java/android/content/ContextParams.java
@@ -0,0 +1,121 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * This class represents rules around how a context being created via
+ * {@link Context#createContext} should behave.
+ *
+ * <p>One of the dimensions to customize is how permissions should behave.
+ * For example, you can specify how permission accesses from a context should
+ * be attributed in the platform's permission tracking system.
+ *
+ * <p>The two main types of attribution are: against an attribution tag which
+ * is an arbitrary string your app specifies for the purposes of tracking permission
+ * accesses from a given portion of your app; against another package and optionally
+ * its attribution tag if you are accessing the data on behalf of another app and
+ * you will be passing that data to this app. Both attributions are not mutually
+ * exclusive.
+ *
+ * <p>For example if you have a feature "foo" in your app which accesses
+ * permissions on behalf of app "foo.bar.baz" with feature "bar" you need to
+ * create a context like this:
+ *
+ * <pre class="prettyprint">
+ * context.createContext(new ContextParams.Builder()
+ *     .setAttributionTag("foo")
+ *     .setReceiverPackage("foo.bar.baz", "bar")
+ *     .build())
+ * </pre>
+ *
+ * @see Context#createContext(ContextParams)
+ */
+public final class ContextParams {
+
+    private ContextParams() {
+        /* hide ctor */
+    }
+
+    /**
+     * @return The attribution tag.
+     */
+    @Nullable
+    public String getAttributionTag() {
+        return null;
+    }
+
+    /**
+     * @return The receiving package.
+     */
+    @Nullable
+    public String getReceiverPackage() {
+        return null;
+    }
+
+    /**
+     * @return The receiving package's attribution tag.
+     */
+    @Nullable
+    public String getReceiverAttributionTag() {
+        return null;
+    }
+
+    /**
+     * Builder for creating a {@link ContextParams}.
+     */
+    public static final class Builder {
+
+        /**
+         * Sets an attribution tag against which to track permission accesses.
+         *
+         * @param attributionTag The attribution tag.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setAttributionTag(@NonNull String attributionTag) {
+            return this;
+        }
+
+        /**
+         * Sets the package and its optional attribution tag that would be receiving
+         * the permission protected data.
+         *
+         * @param packageName The package name receiving the permission protected data.
+         * @param attributionTag An attribution tag of the receiving package.
+         * @return This builder.
+         */
+        @NonNull
+        public Builder setReceiverPackage(@NonNull String packageName,
+                @Nullable String attributionTag) {
+            return this;
+        }
+
+        /**
+         * Creates a new instance. You need to either specify an attribution tag
+         * or a receiver package or both.
+         *
+         * @return The new instance.
+         */
+        @NonNull
+        public ContextParams build() {
+            return new ContextParams();
+        }
+    }
+}
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 144856b..d0d406a 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -4,4 +4,7 @@
 per-file IntentFilter.java = toddke@google.com
 per-file IntentFilter.java = patb@google.com
 per-file Intent.java = toddke@google.com
-per-file Intent.java = patb@google.com
\ No newline at end of file
+per-file Intent.java = patb@google.com
+per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS
+per-file ContentCaptureOptions* = file:/core/java/android/service/contentcapture/OWNERS
+per-file LocusId* = file:/core/java/android/service/contentcapture/OWNERS
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0aa1be9..1a5dad5 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1205,7 +1205,8 @@
             CATEGORY_SOCIAL,
             CATEGORY_NEWS,
             CATEGORY_MAPS,
-            CATEGORY_PRODUCTIVITY
+            CATEGORY_PRODUCTIVITY,
+            CATEGORY_ACCESSIBILITY
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Category {
@@ -1281,6 +1282,13 @@
     public static final int CATEGORY_PRODUCTIVITY = 7;
 
     /**
+     * Category for apps which are primarily accessibility apps, such as screen-readers.
+     *
+     * @see #category
+     */
+    public static final int CATEGORY_ACCESSIBILITY = 8;
+
+    /**
      * Return a concise, localized title for the given
      * {@link ApplicationInfo#category} value, or {@code null} for unknown
      * values such as {@link #CATEGORY_UNDEFINED}.
@@ -1305,6 +1313,8 @@
                 return context.getText(com.android.internal.R.string.app_category_maps);
             case ApplicationInfo.CATEGORY_PRODUCTIVITY:
                 return context.getText(com.android.internal.R.string.app_category_productivity);
+            case ApplicationInfo.CATEGORY_ACCESSIBILITY:
+                return context.getText(com.android.internal.R.string.app_category_accessibility);
             default:
                 return null;
         }
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index b345748..c91334a 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -21,32 +21,34 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 
-/**
- * {@hide}
- */
+import com.android.internal.infra.AndroidFuture;
+
+/** {@hide} */
 interface IShortcutService {
 
-    boolean setDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
-            int userId);
+    oneway void setDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
+            int userId, in AndroidFuture callback);
 
-    boolean addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
-            int userId);
+    oneway void addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
+            int userId, in AndroidFuture callback);
 
-    void removeDynamicShortcuts(String packageName, in List shortcutIds, int userId);
+    oneway void removeDynamicShortcuts(String packageName, in List shortcutIds, int userId);
 
-    void removeAllDynamicShortcuts(String packageName, int userId);
+    oneway void removeAllDynamicShortcuts(String packageName, int userId);
 
-    boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId);
+    oneway void updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId,
+            in AndroidFuture callback);
 
-    boolean requestPinShortcut(String packageName, in ShortcutInfo shortcut,
-            in IntentSender resultIntent, int userId);
+    oneway void requestPinShortcut(String packageName, in ShortcutInfo shortcut,
+            in IntentSender resultIntent, int userId, in AndroidFuture callback);
 
-    Intent createShortcutResultIntent(String packageName, in ShortcutInfo shortcut, int userId);
+    oneway void createShortcutResultIntent(String packageName, in ShortcutInfo shortcut,
+            int userId, in AndroidFuture callback);
 
-    void disableShortcuts(String packageName, in List shortcutIds, CharSequence disabledMessage,
-            int disabledMessageResId, int userId);
+    oneway void disableShortcuts(String packageName, in List shortcutIds,
+            CharSequence disabledMessage, int disabledMessageResId, int userId);
 
-    void enableShortcuts(String packageName, in List shortcutIds, int userId);
+    oneway void enableShortcuts(String packageName, in List shortcutIds, int userId);
 
     int getMaxShortcutCountPerActivity(String packageName, int userId);
 
@@ -56,29 +58,31 @@
 
     int getIconMaxDimensions(String packageName, int userId);
 
-    void reportShortcutUsed(String packageName, String shortcutId, int userId);
+    oneway void reportShortcutUsed(String packageName, String shortcutId, int userId);
 
-    void resetThrottling(); // system only API for developer opsions
+    oneway void resetThrottling(); // system only API for developer opsions
 
-    void onApplicationActive(String packageName, int userId); // system only API for sysUI
+    oneway void onApplicationActive(String packageName, int userId); // system only API for sysUI
 
     byte[] getBackupPayload(int user);
 
-    void applyRestore(in byte[] payload, int user);
+    oneway void applyRestore(in byte[] payload, int user);
 
     boolean isRequestPinItemSupported(int user, int requestType);
 
     // System API used by framework's ShareSheet (ChooserActivity)
-    ParceledListSlice getShareTargets(String packageName, in IntentFilter filter, int userId);
+    oneway void getShareTargets(String packageName, in IntentFilter filter, int userId,
+           in AndroidFuture<ParceledListSlice> callback);
 
     boolean hasShareTargets(String packageName, String packageToCheck, int userId);
 
-    void removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId);
+    oneway void removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId);
 
-    ParceledListSlice getShortcuts(String packageName, int matchFlags, int userId);
+    oneway void getShortcuts(String packageName, int matchFlags, int userId,
+            in AndroidFuture<ParceledListSlice<ShortcutInfo>> callback);
 
-    void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId);
+    oneway void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId);
 
-    void updateShortcutVisibility(String callingPkg, String packageName, in byte[] certificate,
-            in boolean visible, int userId);
-}
\ No newline at end of file
+    oneway void updateShortcutVisibility(String callingPkg, String packageName,
+            in byte[] certificate, in boolean visible, int userId);
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7b62f3b..d79b66c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7017,7 +7017,7 @@
      * domain to an application, use
      * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)},
      * passing in all of the domains returned inside
-     * {@link DomainVerificationManager#getDomainVerificationUserSelection(String)}.
+     * {@link DomainVerificationManager#getDomainVerificationUserState(String)}.
      *
      * @hide
      */
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index d3bac79..f584ff3 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -24,6 +24,7 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
 import android.app.Notification;
 import android.app.usage.UsageStatsManager;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -42,10 +43,12 @@
 import android.os.UserHandle;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.concurrent.ExecutionException;
 
 /**
  * <p><code>ShortcutManager</code> executes operations on an app's set of <i>shortcuts</i>, which
@@ -140,13 +143,16 @@
      *
      * @throws IllegalStateException when the user is locked.
      */
+    @WorkerThread
     public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+        final AndroidFuture<Boolean> future = new AndroidFuture<>();
         try {
-            return mService.setDynamicShortcuts(mContext.getPackageName(),
-                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
+            mService.setDynamicShortcuts(mContext.getPackageName(),
+                    new ParceledListSlice(shortcutInfoList), injectMyUserId(), future);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            future.completeExceptionally(e);
         }
+        return getFutureOrThrow(future);
     }
 
     /**
@@ -158,14 +164,17 @@
      *
      * @throws IllegalStateException when the user is locked.
      */
+    @WorkerThread
     @NonNull
     public List<ShortcutInfo> getDynamicShortcuts() {
+        final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>();
         try {
-            return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_DYNAMIC,
-                    injectMyUserId()).getList();
+            mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_DYNAMIC, injectMyUserId(),
+                    future);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            future.completeExceptionally(e);
         }
+        return getFutureOrThrow(future).getList();
     }
 
     /**
@@ -177,14 +186,17 @@
      *
      * @throws IllegalStateException when the user is locked.
      */
+    @WorkerThread
     @NonNull
     public List<ShortcutInfo> getManifestShortcuts() {
+        final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>();
         try {
-            return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_MANIFEST,
-                    injectMyUserId()).getList();
+            mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_MANIFEST, injectMyUserId(),
+                    future);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            future.completeExceptionally(e);
         }
+        return getFutureOrThrow(future).getList();
     }
 
     /**
@@ -205,14 +217,16 @@
      *
      * @throws IllegalStateException when the user is locked.
      */
+    @WorkerThread
     @NonNull
     public List<ShortcutInfo> getShortcuts(@ShortcutMatchFlags int matchFlags) {
+        final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>();
         try {
-            return mService.getShortcuts(mContext.getPackageName(), matchFlags, injectMyUserId())
-                    .getList();
+            mService.getShortcuts(mContext.getPackageName(), matchFlags, injectMyUserId(), future);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            future.completeExceptionally(e);
         }
+        return getFutureOrThrow(future).getList();
     }
 
     /**
@@ -228,13 +242,16 @@
      *
      * @throws IllegalStateException when the user is locked.
      */
+    @WorkerThread
     public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+        final AndroidFuture<Boolean> future = new AndroidFuture<>();
         try {
-            return mService.addDynamicShortcuts(mContext.getPackageName(),
-                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
+            mService.addDynamicShortcuts(mContext.getPackageName(),
+                    new ParceledListSlice(shortcutInfoList), injectMyUserId(), future);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            future.completeExceptionally(e);
         }
+        return getFutureOrThrow(future);
     }
 
     /**
@@ -287,14 +304,17 @@
      *
      * @throws IllegalStateException when the user is locked.
      */
+    @WorkerThread
     @NonNull
     public List<ShortcutInfo> getPinnedShortcuts() {
+        final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>();
         try {
-            return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_PINNED,
-                    injectMyUserId()).getList();
+            mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_PINNED, injectMyUserId(),
+                    future);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            future.completeExceptionally(e);
         }
+        return getFutureOrThrow(future).getList();
     }
 
     /**
@@ -309,13 +329,16 @@
      *
      * @throws IllegalStateException when the user is locked.
      */
+    @WorkerThread
     public boolean updateShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+        final AndroidFuture<Boolean> future = new AndroidFuture<>();
         try {
-            return mService.updateShortcuts(mContext.getPackageName(),
-                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
+            mService.updateShortcuts(mContext.getPackageName(),
+                    new ParceledListSlice(shortcutInfoList), injectMyUserId(), future);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            future.completeExceptionally(e);
         }
+        return getFutureOrThrow(future);
     }
 
     /**
@@ -584,14 +607,17 @@
      * @throws IllegalStateException The caller doesn't have a foreground activity or a foreground
      * service, or the device is locked.
      */
+    @WorkerThread
     public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut,
             @Nullable IntentSender resultIntent) {
+        final AndroidFuture<Boolean> future = new AndroidFuture<>();
         try {
-            return mService.requestPinShortcut(mContext.getPackageName(), shortcut,
-                    resultIntent, injectMyUserId());
+            mService.requestPinShortcut(mContext.getPackageName(), shortcut,
+                    resultIntent, injectMyUserId(), future);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            future.completeExceptionally(e);
         }
+        return getFutureOrThrow(future);
     }
 
     /**
@@ -611,13 +637,16 @@
      *
      * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled.
      */
+    @WorkerThread
     public Intent createShortcutResultIntent(@NonNull ShortcutInfo shortcut) {
+        final AndroidFuture<Intent> future = new AndroidFuture<>();
         try {
-            return mService.createShortcutResultIntent(mContext.getPackageName(), shortcut,
-                    injectMyUserId());
+            mService.createShortcutResultIntent(mContext.getPackageName(), shortcut,
+                    injectMyUserId(), future);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            future.completeExceptionally(e);
         }
+        return getFutureOrThrow(future);
     }
 
     /**
@@ -650,16 +679,18 @@
      * @return List of {@link ShareShortcutInfo}s that match the given IntentFilter.
      * @hide
      */
+    @WorkerThread
     @NonNull
     @SystemApi
     @RequiresPermission(Manifest.permission.MANAGE_APP_PREDICTIONS)
     public List<ShareShortcutInfo> getShareTargets(@NonNull IntentFilter filter) {
+        final AndroidFuture<ParceledListSlice> future = new AndroidFuture<>();
         try {
-            return mService.getShareTargets(mContext.getPackageName(), filter,
-                    injectMyUserId()).getList();
+            mService.getShareTargets(mContext.getPackageName(), filter, injectMyUserId(), future);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            future.completeExceptionally(e);
         }
+        return getFutureOrThrow(future).getList();
     }
 
     /**
@@ -788,4 +819,21 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    private static <T> T getFutureOrThrow(@NonNull AndroidFuture<T> future) {
+        try {
+            return future.get();
+        } catch (Throwable e) {
+            if (e instanceof ExecutionException) {
+                e = e.getCause();
+            }
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            }
+            if (e instanceof Error) {
+                throw (Error) e;
+            }
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index 2ea24f7..6f478ac 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -155,6 +155,7 @@
         alias.nonLocalizedLabel = target.nonLocalizedLabel;
         alias.launchMode = target.launchMode;
         alias.lockTaskLaunchMode = target.lockTaskLaunchMode;
+        alias.documentLaunchMode = target.documentLaunchMode;
         alias.descriptionRes = target.descriptionRes;
         alias.screenOrientation = target.screenOrientation;
         alias.taskAffinity = target.taskAffinity;
@@ -179,7 +180,6 @@
 //        alias.exported = target.exported;
 //        alias.permission = target.permission;
 //        alias.splitName = target.splitName;
-//        alias.documentLaunchMode = target.documentLaunchMode;
 //        alias.persistableMode = target.persistableMode;
 //        alias.rotationAnimation = target.rotationAnimation;
 //        alias.colorMode = target.colorMode;
diff --git a/core/java/android/content/pm/verify/domain/DomainOwner.java b/core/java/android/content/pm/verify/domain/DomainOwner.java
index b050f5d..5bf2c09 100644
--- a/core/java/android/content/pm/verify/domain/DomainOwner.java
+++ b/core/java/android/content/pm/verify/domain/DomainOwner.java
@@ -66,16 +66,7 @@
      * @param packageName
      *   Package name of that owns the domain.
      * @param overrideable
-     *   Whether or not this owner can be automatically overridden. If all owners for a domain are
-     *   overrideable, then calling
-     *   {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
-     *   Set, boolean)} to enable the domain will disable all other owners. On the other hand, if any
-     *   of the owners are non-overrideable, then
-     *   {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
-     *   boolean)} must be called with false to disable all of the other owners before this domain can
-     *   be taken by a new owner through
-     *   {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
-     *   Set, boolean)}.
+     *   Whether or not this owner can be automatically overridden.
      */
     @DataClass.Generated.Member
     public DomainOwner(
@@ -98,16 +89,9 @@
     }
 
     /**
-     * Whether or not this owner can be automatically overridden. If all owners for a domain are
-     * overrideable, then calling
-     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
-     * Set, boolean)} to enable the domain will disable all other owners. On the other hand, if any
-     * of the owners are non-overrideable, then
-     * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
-     * boolean)} must be called with false to disable all of the other owners before this domain can
-     * be taken by a new owner through
-     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
-     * Set, boolean)}.
+     * Whether or not this owner can be automatically overridden.
+     *
+     * @see DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)
      */
     @DataClass.Generated.Member
     public boolean isOverrideable() {
@@ -205,7 +189,7 @@
     };
 
     @DataClass.Generated(
-            time = 1614119379978L,
+            time = 1614721802044L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainOwner.java",
             inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final  boolean mOverrideable\nclass DomainOwner extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genEqualsHashCode=true, genAidl=true, genToString=true)")
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
index 8095875..7c335b1 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
@@ -94,7 +94,7 @@
 
     private Map<String, Integer> unparcelHostToStateMap(Parcel in) {
         return DomainVerificationUtils.readHostMap(in, new ArrayMap<>(),
-                DomainVerificationUserSelection.class.getClassLoader());
+                DomainVerificationUserState.class.getClassLoader());
     }
 
 
@@ -105,8 +105,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain
-    // /DomainVerificationInfo.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -321,7 +320,7 @@
     };
 
     @DataClass.Generated(
-            time = 1613002530369L,
+            time = 1614721812023L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java",
             inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate  void parcelHostToStateMap(android.os.Parcel,int)\nprivate  java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index 11402af..f7c81bcf 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -25,6 +25,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.os.UserHandle;
 
 import java.util.List;
@@ -32,55 +34,63 @@
 import java.util.UUID;
 
 /**
- * System service to access the domain verification APIs.
+ * System service to access domain verification APIs.
  *
- * Allows the approved domain verification
- * agent on the device (the sole holder of
- * {@link android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}) to update the approval status
- * of domains declared by applications in their AndroidManifest.xml, to allow them to open those
- * links inside the app when selected by the user. This is done through querying
- * {@link #getDomainVerificationInfo(String)} and calling
- * {@link #setDomainVerificationStatus(UUID, Set, int)}.
- *
- * Also allows the domain preference settings (holder of
- * {@link android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) to update the
- * preferences of the user, when they have chosen to explicitly allow an application to open links.
- * This is done through querying {@link #getDomainVerificationUserSelection(String)} and calling
- * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)} and
- * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}.
- *
- * @hide
+ * Applications should use {@link #getDomainVerificationUserState(String)} if necessary to
+ * check if/how they are verified for a domain, which is required starting from platform
+ * {@link android.os.Build.VERSION_CODES#S} in order to open {@link Intent}s which declare
+ * {@link Intent#CATEGORY_BROWSABLE} or no category and also match against
+ * {@link Intent#CATEGORY_DEFAULT} {@link android.content.IntentFilter}s, either through an
+ * explicit declaration of {@link Intent#CATEGORY_DEFAULT} or through the use of
+ * {@link android.content.pm.PackageManager#MATCH_DEFAULT_ONLY}, which is usually added for the
+ * caller when using {@link Context#startActivity(Intent)} and similar.
  */
-@SystemApi
 @SystemService(Context.DOMAIN_VERIFICATION_SERVICE)
-public interface DomainVerificationManager {
+public final class DomainVerificationManager {
 
     /**
-     * Extra field name for a {@link DomainVerificationRequest} for the requested packages.
-     * Passed to an the domain verification agent that handles
+     * Extra field name for a {@link DomainVerificationRequest} for the requested packages. Passed
+     * to an the domain verification agent that handles
      * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION}.
+     *
+     * @hide
      */
-    String EXTRA_VERIFICATION_REQUEST =
+    @SystemApi
+    public static final String EXTRA_VERIFICATION_REQUEST =
             "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
 
     /**
      * No response has been recorded by either the system or any verification agent.
+     *
+     * @hide
      */
-    int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
-
-    /** The verification agent has explicitly verified the domain at some point. */
-    int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
+    @SystemApi
+    public static final int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
 
     /**
-     * The first available custom response code. This and any greater integer, along with
-     * {@link #STATE_SUCCESS} are the only values settable by the verification agent. All values
-     * will be treated as if the domain is unverified.
+     * The verification agent has explicitly verified the domain at some point.
+     *
+     * @hide
      */
-    int STATE_FIRST_VERIFIER_DEFINED = DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+    @SystemApi
+    public static final int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
 
-    /** @hide */
+    /**
+     * The first available custom response code. This and any greater integer, along with {@link
+     * #STATE_SUCCESS} are the only values settable by the verification agent. All values will be
+     * treated as if the domain is unverified.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int STATE_FIRST_VERIFIER_DEFINED =
+            DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+
+    /**
+     * @hide
+     */
     @NonNull
-    static String stateToDebugString(@DomainVerificationState.State int state) {
+    public static String stateToDebugString(@DomainVerificationState.State int state) {
         switch (state) {
             case DomainVerificationState.STATE_NO_RESPONSE:
                 return "none";
@@ -104,10 +114,13 @@
     }
 
     /**
-     * Checks if a state considers the corresponding domain to be successfully verified. The
-     * domain verification agent may use this to determine whether or not to re-verify a domain.
+     * Checks if a state considers the corresponding domain to be successfully verified. The domain
+     * verification agent may use this to determine whether or not to re-verify a domain.
+     *
+     * @hide
      */
-    static boolean isStateVerified(@DomainVerificationState.State int state) {
+    @SystemApi
+    public static boolean isStateVerified(@DomainVerificationState.State int state) {
         switch (state) {
             case DomainVerificationState.STATE_SUCCESS:
             case DomainVerificationState.STATE_APPROVED:
@@ -126,10 +139,13 @@
     /**
      * Checks if a state is modifiable by the domain verification agent. This is useful as the
      * platform may add new state codes in newer versions, and older verification agents can use
-     * this method to determine if a state can be changed without having to be aware of what the
-     * new state means.
+     * this method to determine if a state can be changed without having to be aware of what the new
+     * state means.
+     *
+     * @hide
      */
-    static boolean isStateModifiable(@DomainVerificationState.State int state) {
+    @SystemApi
+    public static boolean isStateModifiable(@DomainVerificationState.State int state) {
         switch (state) {
             case DomainVerificationState.STATE_NO_RESPONSE:
             case DomainVerificationState.STATE_SUCCESS:
@@ -147,11 +163,12 @@
     }
 
     /**
-     * For determine re-verify policy. This is hidden from the domain verification agent so that
-     * no behavior is made based on the result.
+     * For determine re-verify policy. This is hidden from the domain verification agent so that no
+     * behavior is made based on the result.
+     *
      * @hide
      */
-    static boolean isStateDefault(@DomainVerificationState.State int state) {
+    public static boolean isStateDefault(@DomainVerificationState.State int state) {
         switch (state) {
             case DomainVerificationState.STATE_NO_RESPONSE:
             case DomainVerificationState.STATE_MIGRATED:
@@ -168,14 +185,72 @@
     }
 
     /**
+     * @hide
+     */
+    public static final int ERROR_INVALID_DOMAIN_SET = 1;
+    /**
+     * @hide
+     */
+    public static final int ERROR_NAME_NOT_FOUND = 2;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"ERROR_"}, value = {
+            ERROR_INVALID_DOMAIN_SET,
+            ERROR_NAME_NOT_FOUND,
+    })
+    private @interface Error {
+    }
+
+    private final Context mContext;
+
+    private final IDomainVerificationManager mDomainVerificationManager;
+
+
+    /**
+     * System service to access the domain verification APIs.
+     * <p>
+     * Allows the approved domain verification agent on the device (the sole holder of {@link
+     * android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}) to update the approval status of
+     * domains declared by applications in their AndroidManifest.xml, to allow them to open those
+     * links inside the app when selected by the user. This is done through querying {@link
+     * #getDomainVerificationInfo(String)} and calling {@link #setDomainVerificationStatus(UUID,
+     * Set, int)}.
+     * <p>
+     * Also allows the domain preference settings (holder of
+     * {@link android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION})
+     * to update the preferences of the user, when they have chosen to explicitly allow an
+     * application to open links. This is done through querying
+     * {@link #getDomainVerificationUserState(String)} and calling
+     * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)} and
+     * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}.
+     *
+     * @hide
+     */
+    public DomainVerificationManager(Context context,
+            IDomainVerificationManager domainVerificationManager) {
+        mContext = context;
+        mDomainVerificationManager = domainVerificationManager;
+    }
+
+    /**
      * Used to iterate all {@link DomainVerificationInfo} values to do cleanup or retries. This is
      * usually a heavy workload and should be done infrequently.
      *
      * @return the current snapshot of package names with valid autoVerify URLs.
+     * @hide
      */
+    @SystemApi
     @NonNull
     @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
-    List<String> getValidVerificationPackageNames();
+    public List<String> queryValidVerificationPackageNames() {
+        try {
+            return mDomainVerificationManager.queryValidVerificationPackageNames();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     /**
      * Retrieves the domain verification state for a given package.
@@ -183,61 +258,106 @@
      * @return the data for the package, or null if it does not declare any autoVerify domains
      * @throws NameNotFoundException If the package is unavailable. This is an unrecoverable error
      *                               and should not be re-tried except on a time scheduled basis.
+     * @hide
      */
+    @SystemApi
     @Nullable
     @RequiresPermission(anyOf = {
             android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
             android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
     })
-    DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
-            throws NameNotFoundException;
+    public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
+            throws NameNotFoundException {
+        try {
+            return mDomainVerificationManager.getDomainVerificationInfo(packageName);
+        } catch (Exception e) {
+            Exception converted = rethrow(e, packageName);
+            if (converted instanceof NameNotFoundException) {
+                throw (NameNotFoundException) converted;
+            } else if (converted instanceof RuntimeException) {
+                throw (RuntimeException) converted;
+            } else {
+                throw new RuntimeException(converted);
+            }
+        }
+    }
 
     /**
-     * Change the verification status of the {@param domains} of the package associated with
-     * {@param domainSetId}.
+     * Change the verification status of the {@param domains} of the package associated with {@param
+     * domainSetId}.
      *
      * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
      * @param domains     List of host names to change the state of.
      * @param state       See {@link DomainVerificationInfo#getHostToStateMap()}.
      * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
      *                                  invalid. This usually means the work being processed by the
-     *                                  verification agent is outdated and a new request should
-     *                                  be scheduled, if one has not already been done as part of
-     *                                  the {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION}
-     *                                  broadcast.
+     *                                  verification agent is outdated and a new request should be
+     *                                  scheduled, if one has not already been done as part of the
+     *                                  {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
      * @throws NameNotFoundException    If the ID is known to be good, but the package is
-     *                                  unavailable. This may be because the package is
-     *                                  installed on a volume that is no longer mounted. This
-     *                                  error is unrecoverable until the package is available
-     *                                  again, and should not be re-tried except on a time
-     *                                  scheduled basis.
+     *                                  unavailable. This may be because the package is installed on
+     *                                  a volume that is no longer mounted. This error is
+     *                                  unrecoverable until the package is available again, and
+     *                                  should not be re-tried except on a time scheduled basis.
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
-    void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
-            @DomainVerificationState.State int state) throws NameNotFoundException;
+    public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+            @DomainVerificationState.State int state) throws NameNotFoundException {
+        try {
+            mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
+                    new DomainSet(domains), state);
+        } catch (Exception e) {
+            Exception converted = rethrow(e, domainSetId);
+            if (converted instanceof NameNotFoundException) {
+                throw (NameNotFoundException) converted;
+            } else if (converted instanceof RuntimeException) {
+                throw (RuntimeException) converted;
+            } else {
+                throw new RuntimeException(converted);
+            }
+        }
+    }
 
     /**
-     * TODO(b/178525735): This documentation is incorrect in the context of UX changes.
-     * Change whether the given {@param packageName} is allowed to automatically open verified
-     * HTTP/HTTPS domains. The final state is determined along with the verification status for the
-     * specific domain being opened and other system state. An app with this enabled is not
-     * guaranteed to be the sole link handler for its domains.
+     * Change whether the given packageName is allowed to handle BROWSABLE and DEFAULT category web
+     * (HTTP/HTTPS) {@link Intent} Activity open requests. The final state is determined along with
+     * the verification status for the specific domain being opened and other system state. An app
+     * with this enabled is not guaranteed to be the sole link handler for its domains.
+     * <p>
+     * By default, all apps are allowed to open links. Users must disable them explicitly.
      *
-     * By default, all apps are allowed to open verified links. Users must disable them explicitly.
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
-    void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed)
-            throws NameNotFoundException;
+    public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
+            boolean allowed) throws NameNotFoundException {
+        try {
+            mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(packageName,
+                    allowed, mContext.getUserId());
+        } catch (Exception e) {
+            Exception converted = rethrow(e, packageName);
+            if (converted instanceof NameNotFoundException) {
+                throw (NameNotFoundException) converted;
+            } else if (converted instanceof RuntimeException) {
+                throw (RuntimeException) converted;
+            } else {
+                throw new RuntimeException(converted);
+            }
+        }
+    }
 
     /**
      * Update the recorded user selection for the given {@param domains} for the given {@param
      * domainSetId}. This state is recorded for the lifetime of a domain for a package on device,
      * and will never be reset by the system short of an app data clear.
-     *
+     * <p>
      * This state is stored per device user. If another user needs to be changed, the appropriate
-     * permissions must be acquired and
-     * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used.
-     *
+     * permissions must be acquired and {@link Context#createContextAsUser(UserHandle, int)} should
+     * be used.
+     * <p>
      * Enabling an unverified domain will allow an application to open it, but this can only occur
      * if no other app on the device is approved for a higher approval level. This can queried
      * using {@link #getOwnersForDomain(String)}.
@@ -255,33 +375,55 @@
      * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
      *                                  invalid.
      * @throws NameNotFoundException    If the ID is known to be good, but the package is
-     *                                  unavailable. This may be because the package is
-     *                                  installed on a volume that is no longer mounted. This
-     *                                  error is unrecoverable until the package is available
-     *                                  again, and should not be re-tried except on a time
-     *                                  scheduled basis.
+     *                                  unavailable. This may be because the package is installed on
+     *                                  a volume that is no longer mounted. This error is
+     *                                  unrecoverable until the package is available again, and
+     *                                  should not be re-tried except on a time scheduled basis.
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
-    void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
-            @NonNull Set<String> domains, boolean enabled) throws NameNotFoundException;
+    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+            @NonNull Set<String> domains, boolean enabled) throws NameNotFoundException {
+        try {
+            mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
+                    new DomainSet(domains), enabled, mContext.getUserId());
+        } catch (Exception e) {
+            Exception converted = rethrow(e, domainSetId);
+            if (converted instanceof NameNotFoundException) {
+                throw (NameNotFoundException) converted;
+            } else if (converted instanceof RuntimeException) {
+                throw (RuntimeException) converted;
+            } else {
+                throw new RuntimeException(converted);
+            }
+        }
+    }
 
     /**
      * Retrieve the user selection data for the given {@param packageName} and the current user.
-     * It is the responsibility of the caller to ensure that the
-     * {@link DomainVerificationUserSelection#getIdentifier()} matches any prior API calls.
-     *
-     * This state is stored per device user. If another user needs to be accessed, the appropriate
-     * permissions must be acquired and
-     * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used.
      *
      * @param packageName The app to query state for.
-     * @return the user selection verification data for the given package for the current user,
-     * or null if the package does not declare any HTTP/HTTPS domains.
+     * @return the user selection verification data for the given package for the current user, or
+     * null if the package does not declare any HTTP/HTTPS domains.
      */
     @Nullable
-    @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
-    DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String packageName)
-            throws NameNotFoundException;
+    public DomainVerificationUserState getDomainVerificationUserState(
+            @NonNull String packageName) throws NameNotFoundException {
+        try {
+            return mDomainVerificationManager.getDomainVerificationUserState(packageName,
+                    mContext.getUserId());
+        } catch (Exception e) {
+            Exception converted = rethrow(e, packageName);
+            if (converted instanceof NameNotFoundException) {
+                throw (NameNotFoundException) converted;
+            } else if (converted instanceof RuntimeException) {
+                throw (RuntimeException) converted;
+            } else {
+                throw new RuntimeException(converted);
+            }
+        }
+    }
 
     /**
      * For the given domain, return all apps which are approved to open it in a
@@ -291,21 +433,65 @@
      *
      * By default the list will be returned ordered from lowest to highest
      * priority.
+     *
+     * @hide
      */
+    @SystemApi
     @NonNull
     @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
-    List<DomainOwner> getOwnersForDomain(@NonNull String domain);
+    public List<DomainOwner> getOwnersForDomain(@NonNull String domain) {
+        try {
+            return mDomainVerificationManager.getOwnersForDomain(domain, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private Exception rethrow(Exception exception, @Nullable UUID domainSetId) {
+        return rethrow(exception, domainSetId, null);
+    }
+
+    private Exception rethrow(Exception exception, @Nullable String packageName) {
+        return rethrow(exception, null, packageName);
+    }
+
+    private Exception rethrow(Exception exception, @Nullable UUID domainSetId,
+            @Nullable String packageName) {
+        if (exception instanceof ServiceSpecificException) {
+            int packedErrorCode = ((ServiceSpecificException) exception).errorCode;
+            if (packageName == null) {
+                packageName = exception.getMessage();
+            }
+
+            @Error int managerErrorCode = packedErrorCode & 0xFFFF;
+            switch (managerErrorCode) {
+                case ERROR_INVALID_DOMAIN_SET:
+                    int errorSpecificCode = packedErrorCode >> 16;
+                    return new IllegalArgumentException(InvalidDomainSetException.buildMessage(
+                            domainSetId, packageName, errorSpecificCode));
+                case ERROR_NAME_NOT_FOUND:
+                    return new NameNotFoundException(packageName);
+                default:
+                    return exception;
+            }
+        } else if (exception instanceof RemoteException) {
+            return ((RemoteException) exception).rethrowFromSystemServer();
+        } else {
+            return exception;
+        }
+    }
 
     /**
      * Thrown if a {@link DomainVerificationInfo#getIdentifier()}} or an associated set of domains
      * provided by the caller is no longer valid. This may be recoverable, and the caller should
      * re-query the package name associated with the ID using
-     * {@link #getDomainVerificationInfo(String)} in order to check. If that also fails, then the
-     * package is no longer known to the device and thus all pending work for it should be dropped.
+     * {@link #getDomainVerificationInfo(String)}
+     * in order to check. If that also fails, then the package is no longer known to the device and
+     * thus all pending work for it should be dropped.
      *
      * @hide
      */
-    class InvalidDomainSetException extends IllegalArgumentException {
+    public static class InvalidDomainSetException extends IllegalArgumentException {
 
         public static final int REASON_ID_NULL = 1;
         public static final int REASON_ID_INVALID = 2;
@@ -313,7 +499,9 @@
         public static final int REASON_UNKNOWN_DOMAIN = 4;
         public static final int REASON_UNABLE_TO_APPROVE = 5;
 
-        /** @hide */
+        /**
+         * @hide
+         */
         @IntDef({
                 REASON_ID_NULL,
                 REASON_ID_INVALID,
@@ -352,7 +540,9 @@
         @Nullable
         private final String mPackageName;
 
-        /** @hide */
+        /**
+         * @hide
+         */
         public InvalidDomainSetException(@Nullable UUID domainSetId, @Nullable String packageName,
                 @Reason int reason) {
             super(buildMessage(domainSetId, packageName, reason));
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java
deleted file mode 100644
index 8b9865c..0000000
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java
+++ /dev/null
@@ -1,202 +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.content.pm.verify.domain;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * @hide
- */
-@SuppressWarnings("RedundantThrows")
-public class DomainVerificationManagerImpl implements DomainVerificationManager {
-
-    public static final int ERROR_INVALID_DOMAIN_SET = 1;
-    public static final int ERROR_NAME_NOT_FOUND = 2;
-
-    @IntDef(prefix = { "ERROR_" }, value = {
-            ERROR_INVALID_DOMAIN_SET,
-            ERROR_NAME_NOT_FOUND,
-    })
-    private @interface Error {
-    }
-
-    private final Context mContext;
-
-    private final IDomainVerificationManager mDomainVerificationManager;
-
-    public DomainVerificationManagerImpl(Context context,
-            IDomainVerificationManager domainVerificationManager) {
-        mContext = context;
-        mDomainVerificationManager = domainVerificationManager;
-    }
-
-    @NonNull
-    @Override
-    public List<String> getValidVerificationPackageNames() {
-        try {
-            return mDomainVerificationManager.getValidVerificationPackageNames();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @Nullable
-    @Override
-    public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
-            throws NameNotFoundException {
-        try {
-            return mDomainVerificationManager.getDomainVerificationInfo(packageName);
-        } catch (Exception e) {
-            Exception converted = rethrow(e, packageName);
-            if (converted instanceof NameNotFoundException) {
-                throw (NameNotFoundException) converted;
-            } else if (converted instanceof RuntimeException) {
-                throw (RuntimeException) converted;
-            } else {
-                throw new RuntimeException(converted);
-            }
-        }
-    }
-
-    @Override
-    public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
-            int state) throws IllegalArgumentException, NameNotFoundException {
-        try {
-            mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
-                    new DomainSet(domains), state);
-        } catch (Exception e) {
-            Exception converted = rethrow(e, domainSetId);
-            if (converted instanceof NameNotFoundException) {
-                throw (NameNotFoundException) converted;
-            } else if (converted instanceof RuntimeException) {
-                throw (RuntimeException) converted;
-            } else {
-                throw new RuntimeException(converted);
-            }
-        }
-    }
-
-    @Override
-    public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
-            boolean allowed) throws NameNotFoundException {
-        try {
-            mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(packageName,
-                    allowed, mContext.getUserId());
-        } catch (Exception e) {
-            Exception converted = rethrow(e, packageName);
-            if (converted instanceof NameNotFoundException) {
-                throw (NameNotFoundException) converted;
-            } else if (converted instanceof RuntimeException) {
-                throw (RuntimeException) converted;
-            } else {
-                throw new RuntimeException(converted);
-            }
-        }
-    }
-
-    @Override
-    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
-            @NonNull Set<String> domains, boolean enabled)
-            throws IllegalArgumentException, NameNotFoundException {
-        try {
-            mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
-                    new DomainSet(domains), enabled, mContext.getUserId());
-        } catch (Exception e) {
-            Exception converted = rethrow(e, domainSetId);
-            if (converted instanceof NameNotFoundException) {
-                throw (NameNotFoundException) converted;
-            } else if (converted instanceof RuntimeException) {
-                throw (RuntimeException) converted;
-            } else {
-                throw new RuntimeException(converted);
-            }
-        }
-    }
-
-    @Nullable
-    @Override
-    public DomainVerificationUserSelection getDomainVerificationUserSelection(
-            @NonNull String packageName) throws NameNotFoundException {
-        try {
-            return mDomainVerificationManager.getDomainVerificationUserSelection(packageName,
-                    mContext.getUserId());
-        } catch (Exception e) {
-            Exception converted = rethrow(e, packageName);
-            if (converted instanceof NameNotFoundException) {
-                throw (NameNotFoundException) converted;
-            } else if (converted instanceof RuntimeException) {
-                throw (RuntimeException) converted;
-            } else {
-                throw new RuntimeException(converted);
-            }
-        }
-    }
-
-    @NonNull
-    @Override
-    public List<DomainOwner> getOwnersForDomain(@NonNull String domain) {
-        try {
-            return mDomainVerificationManager.getOwnersForDomain(domain, mContext.getUserId());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    private Exception rethrow(Exception exception, @Nullable UUID domainSetId) {
-        return rethrow(exception, domainSetId, null);
-    }
-
-    private Exception rethrow(Exception exception, @Nullable String packageName) {
-        return rethrow(exception, null, packageName);
-    }
-
-    private Exception rethrow(Exception exception, @Nullable UUID domainSetId,
-            @Nullable String packageName) {
-        if (exception instanceof ServiceSpecificException) {
-            int packedErrorCode = ((ServiceSpecificException) exception).errorCode;
-            if (packageName == null) {
-                packageName = exception.getMessage();
-            }
-
-            @Error int managerErrorCode = packedErrorCode & 0xFFFF;
-            switch (managerErrorCode) {
-                case ERROR_INVALID_DOMAIN_SET:
-                    int errorSpecificCode = packedErrorCode >> 16;
-                    return new IllegalArgumentException(InvalidDomainSetException.buildMessage(
-                            domainSetId, packageName, errorSpecificCode));
-                case ERROR_NAME_NOT_FOUND:
-                    return new NameNotFoundException(packageName);
-                default:
-                    return exception;
-            }
-        } else if (exception instanceof RemoteException) {
-            return ((RemoteException) exception).rethrowFromSystemServer();
-        } else {
-            return exception;
-        }
-    }
-}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationUserState.aidl
similarity index 93%
rename from core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
rename to core/java/android/content/pm/verify/domain/DomainVerificationUserState.aidl
index ddb5ef8..94690c1 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserState.aidl
@@ -16,4 +16,4 @@
 
 package android.content.pm.verify.domain;
 
-parcelable DomainVerificationUserSelection;
+parcelable DomainVerificationUserState;
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java b/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java
similarity index 82%
rename from core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
rename to core/java/android/content/pm/verify/domain/DomainVerificationUserState.java
index d23f5f1..1e60abb 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
@@ -29,39 +30,36 @@
 import com.android.internal.util.Parcelling;
 
 import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
 
 /**
  * Contains the user selection state for a package. This means all web HTTP(S) domains declared by a
  * package in its manifest, whether or not they were marked for auto verification.
  * <p>
- * By default, all apps are allowed to automatically open links with domains that they've
- * successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}. The user
- * can decide to disable this, disallowing the application from opening all links. Note that the
- * toggle affects <b>all</b> links and is not based on the verification state of the domains.
+ * Applications should use {@link #getHostToStateMap()} if necessary to
+ * check if/how they are verified for a domain, which is required starting from platform
+ * {@link android.os.Build.VERSION_CODES#S} in order to open {@link Intent}s which declare
+ * {@link Intent#CATEGORY_BROWSABLE} or no category and also match against
+ * {@link Intent#CATEGORY_DEFAULT} {@link android.content.IntentFilter}s, either through an
+ * explicit declaration of {@link Intent#CATEGORY_DEFAULT} or through the use of
+ * {@link android.content.pm.PackageManager#MATCH_DEFAULT_ONLY}, which is usually added for the
+ * caller when using {@link Context#startActivity(Intent)} and similar.
+ * <p>
+ * By default, all apps are allowed to automatically open links for the above case for domains that
+ * they've successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}.
+ * The user can decide to disable this, disallowing the application from opening all links. Note
+ * that the toggle affects <b>all</b> links and is not based on the verification state of the
+ * domains.
  * <p>
  * Assuming the toggle is enabled, the user can also select additional unverified domains to grant
  * to the application to open, which is reflected in {@link #getHostToStateMap()}. But only a single
  * application can be approved for a domain unless the applications are both approved. If another
  * application is approved, the user will not be allowed to enable the domain.
- * <p>
- * These values can be changed through the
- * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
- * boolean)} and {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
- * boolean)} APIs.
- * <p>
- * Note that because state is per user, if a different user needs to be changed, one will need to
- * use {@link Context#createContextAsUser(UserHandle, int)} and hold the {@link
- * android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
- *
- * @hide
  */
-@SystemApi
 @SuppressWarnings("DefaultAnnotationParam")
 @DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
         genEqualsHashCode = true, genHiddenConstDefs = true)
-public final class DomainVerificationUserSelection implements Parcelable {
+public final class DomainVerificationUserState implements Parcelable {
 
     /**
      * The domain is unverified and unselected, and the application is unable to open web links
@@ -70,9 +68,8 @@
     public static final int DOMAIN_STATE_NONE = 0;
 
     /**
-     * The domain has been selected through the
-     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)}
-     * API, under the assumption it has not been reset by the system.
+     * The domain has been selected by the user. This may be reset to {@link #DOMAIN_STATE_NONE} if
+     * another application is selected or verified for the same domain.
      */
     public static final int DOMAIN_STATE_SELECTED = 1;
 
@@ -119,7 +116,16 @@
     @NonNull
     private Map<String, Integer> unparcelHostToStateMap(Parcel in) {
         return DomainVerificationUtils.readHostMap(in, new ArrayMap<>(),
-                DomainVerificationUserSelection.class.getClassLoader());
+                DomainVerificationUserState.class.getClassLoader());
+    }
+
+    /**
+     * @see DomainVerificationInfo#getIdentifier
+     * @hide
+     */
+    @SystemApi
+    public @NonNull UUID getIdentifier() {
+        return mIdentifier;
     }
 
 
@@ -130,7 +136,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -162,7 +168,7 @@
     }
 
     /**
-     * Creates a new DomainVerificationUserSelection.
+     * Creates a new DomainVerificationUserState.
      *
      * @param packageName
      *   The package name that this data corresponds to.
@@ -175,7 +181,7 @@
      * @hide
      */
     @DataClass.Generated.Member
-    public DomainVerificationUserSelection(
+    public DomainVerificationUserState(
             @NonNull UUID identifier,
             @NonNull String packageName,
             @NonNull UserHandle user,
@@ -201,14 +207,6 @@
     }
 
     /**
-     * @see DomainVerificationInfo#getIdentifier
-     */
-    @DataClass.Generated.Member
-    public @NonNull UUID getIdentifier() {
-        return mIdentifier;
-    }
-
-    /**
      * The package name that this data corresponds to.
      */
     @DataClass.Generated.Member
@@ -246,7 +244,7 @@
         // You can override field toString logic by defining methods like:
         // String fieldNameToString() { ... }
 
-        return "DomainVerificationUserSelection { " +
+        return "DomainVerificationUserState { " +
                 "identifier = " + mIdentifier + ", " +
                 "packageName = " + mPackageName + ", " +
                 "user = " + mUser + ", " +
@@ -259,13 +257,13 @@
     @DataClass.Generated.Member
     public boolean equals(@Nullable Object o) {
         // You can override field equality logic by defining either of the methods like:
-        // boolean fieldNameEquals(DomainVerificationUserSelection other) { ... }
+        // boolean fieldNameEquals(DomainVerificationUserState other) { ... }
         // boolean fieldNameEquals(FieldType otherValue) { ... }
 
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         @SuppressWarnings("unchecked")
-        DomainVerificationUserSelection that = (DomainVerificationUserSelection) o;
+        DomainVerificationUserState that = (DomainVerificationUserState) o;
         //noinspection PointlessBooleanExpression
         return true
                 && java.util.Objects.equals(mIdentifier, that.mIdentifier)
@@ -323,7 +321,7 @@
     /** @hide */
     @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    /* package-private */ DomainVerificationUserSelection(@NonNull Parcel in) {
+    /* package-private */ DomainVerificationUserState(@NonNull Parcel in) {
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
@@ -354,24 +352,24 @@
     }
 
     @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<DomainVerificationUserSelection> CREATOR
-            = new Parcelable.Creator<DomainVerificationUserSelection>() {
+    public static final @NonNull Parcelable.Creator<DomainVerificationUserState> CREATOR
+            = new Parcelable.Creator<DomainVerificationUserState>() {
         @Override
-        public DomainVerificationUserSelection[] newArray(int size) {
-            return new DomainVerificationUserSelection[size];
+        public DomainVerificationUserState[] newArray(int size) {
+            return new DomainVerificationUserState[size];
         }
 
         @Override
-        public DomainVerificationUserSelection createFromParcel(@NonNull Parcel in) {
-            return new DomainVerificationUserSelection(in);
+        public DomainVerificationUserState createFromParcel(@NonNull Parcel in) {
+            return new DomainVerificationUserState(in);
         }
     };
 
     @DataClass.Generated(
-            time = 1613683603297L,
+            time = 1614721840152L,
             codegenVersion = "1.0.22",
-            sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java",
-            inputSignatures = "public static final  int DOMAIN_STATE_NONE\npublic static final  int DOMAIN_STATE_SELECTED\npublic static final  int DOMAIN_STATE_VERIFIED\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate  void parcelHostToStateMap(android.os.Parcel,int)\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)")
+            sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java",
+            inputSignatures = "public static final  int DOMAIN_STATE_NONE\npublic static final  int DOMAIN_STATE_SELECTED\npublic static final  int DOMAIN_STATE_VERIFIED\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate  void parcelHostToStateMap(android.os.Parcel,int)\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\npublic @android.annotation.SystemApi @android.annotation.NonNull java.util.UUID getIdentifier()\nclass DomainVerificationUserState extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
index 701af32..332b925 100644
--- a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
+++ b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
@@ -19,7 +19,7 @@
 import android.content.pm.verify.domain.DomainOwner;
 import android.content.pm.verify.domain.DomainSet;
 import android.content.pm.verify.domain.DomainVerificationInfo;
-import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.DomainVerificationUserState;
 import java.util.List;
 
 /**
@@ -28,13 +28,13 @@
  */
 interface IDomainVerificationManager {
 
-    List<String> getValidVerificationPackageNames();
+    List<String> queryValidVerificationPackageNames();
 
     @nullable
     DomainVerificationInfo getDomainVerificationInfo(String packageName);
 
     @nullable
-    DomainVerificationUserSelection getDomainVerificationUserSelection(String packageName,
+    DomainVerificationUserState getDomainVerificationUserState(String packageName,
             int userId);
 
     @nullable
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index fd98d37..31d1b69 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -62,10 +62,13 @@
      * @hide
      */
     int TYPE_FACE = 1 << 3;
-    @IntDef({TYPE_NONE,
+
+    @IntDef(flag = true, value = {
+            TYPE_NONE,
             TYPE_CREDENTIAL,
             TYPE_FINGERPRINT,
-            TYPE_IRIS})
+            TYPE_IRIS
+    })
     @Retention(RetentionPolicy.SOURCE)
     @interface Modality {}
 
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 5b28e00..1fdce5e 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -23,6 +23,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -193,15 +194,15 @@
         int DEVICE_CREDENTIAL = 1 << 15;
     }
 
-    private final Context mContext;
-    private final IAuthService mService;
+    @NonNull private final Context mContext;
+    @NonNull private final IAuthService mService;
 
     /**
      * @hide
      * @param context
      * @param service
      */
-    public BiometricManager(Context context, IAuthService service) {
+    public BiometricManager(@NonNull Context context, @NonNull IAuthService service) {
         mContext = context;
         mService = service;
     }
@@ -274,7 +275,8 @@
      */
     @Deprecated
     @RequiresPermission(USE_BIOMETRIC)
-    public @BiometricError int canAuthenticate() {
+    @BiometricError
+    public int canAuthenticate() {
         return canAuthenticate(Authenticators.BIOMETRIC_WEAK);
     }
 
@@ -304,7 +306,8 @@
      *     authenticators can currently be used (enrolled and available).
      */
     @RequiresPermission(USE_BIOMETRIC)
-    public @BiometricError int canAuthenticate(@Authenticators.Types int authenticators) {
+    @BiometricError
+    public int canAuthenticate(@Authenticators.Types int authenticators) {
         return canAuthenticate(mContext.getUserId(), authenticators);
     }
 
@@ -312,8 +315,10 @@
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
-    public @BiometricError int canAuthenticate(int userId,
-            @Authenticators.Types int authenticators) {
+    @BiometricError
+    public int canAuthenticate(
+            int userId, @Authenticators.Types int authenticators) {
+
         if (mService != null) {
             try {
                 final String opPackageName = mContext.getOpPackageName();
@@ -322,7 +327,7 @@
                 throw e.rethrowFromSystemServer();
             }
         } else {
-            Slog.w(TAG, "hasEnrolledBiometrics(): Service not connected");
+            Slog.w(TAG, "canAuthenticate(): Service not connected");
             return BIOMETRIC_ERROR_HW_UNAVAILABLE;
         }
     }
@@ -404,5 +409,115 @@
         }
     }
 
+    /**
+     * Provides a localized string that may be used as the label for a button that invokes
+     * {@link BiometricPrompt}.
+     *
+     * <p>When possible, this method should use the given authenticator requirements to more
+     * precisely specify the authentication type that will be used. For example, if
+     * <strong>Class 3</strong> biometric authentication is requested on a device with a
+     * <strong>Class 3</strong> fingerprint sensor and a <strong>Class 2</strong> face sensor, the
+     * returned string should indicate that fingerprint authentication will be used.
+     *
+     * <p>This method should also try to specify which authentication method(s) will be used in
+     * practice when multiple authenticators meet the given requirements. For example, if biometric
+     * authentication is requested on a device with both face and fingerprint sensors but the user
+     * has selected face as their preferred method, the returned string should indicate that face
+     * authentication will be used.
+     *
+     * @param authenticators A bit field representing the types of {@link Authenticators} that may
+     *                       be used for authentication.
+     * @return The label for a button that invokes {@link BiometricPrompt} for authentication.
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    @Nullable
+    public CharSequence getButtonLabel(@Authenticators.Types int authenticators) {
+        if (mService != null) {
+            final int userId = mContext.getUserId();
+            final String opPackageName = mContext.getOpPackageName();
+            try {
+                return mService.getButtonLabel(userId, opPackageName, authenticators);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Slog.w(TAG, "getButtonLabel(): Service not connected");
+            return null;
+        }
+    }
+
+    /**
+     * Provides a localized string that may be shown while the user is authenticating with
+     * {@link BiometricPrompt}.
+     *
+     * <p>When possible, this method should use the given authenticator requirements to more
+     * precisely specify the authentication type that will be used. For example, if
+     * <strong>Class 3</strong> biometric authentication is requested on a device with a
+     * <strong>Class 3</strong> fingerprint sensor and a <strong>Class 2</strong> face sensor, the
+     * returned string should indicate that fingerprint authentication will be used.
+     *
+     * <p>This method should also try to specify which authentication method(s) will be used in
+     * practice when multiple authenticators meet the given requirements. For example, if biometric
+     * authentication is requested on a device with both face and fingerprint sensors but the user
+     * has selected face as their preferred method, the returned string should indicate that face
+     * authentication will be used.
+     *
+     * @param authenticators A bit field representing the types of {@link Authenticators} that may
+     *                       be used for authentication.
+     * @return The label for a button that invokes {@link BiometricPrompt} for authentication.
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    @Nullable
+    public CharSequence getPromptMessage(@Authenticators.Types int authenticators) {
+        if (mService != null) {
+            final int userId = mContext.getUserId();
+            final String opPackageName = mContext.getOpPackageName();
+            try {
+                return mService.getPromptMessage(userId, opPackageName, authenticators);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Slog.w(TAG, "getPromptMessage(): Service not connected");
+            return null;
+        }
+    }
+
+    /**
+     * Provides a localized string that may be shown as the title for an app setting that enables
+     * biometric authentication.
+     *
+     * <p>When possible, this method should use the given authenticator requirements to more
+     * precisely specify the authentication type that will be used. For example, if
+     * <strong>Class 3</strong> biometric authentication is requested on a device with a
+     * <strong>Class 3</strong> fingerprint sensor and a <strong>Class 2</strong> face sensor, the
+     * returned string should indicate that fingerprint authentication will be used.
+     *
+     * <p>This method should <em>not</em> try to specify which authentication method(s) will be used
+     * in practice when multiple authenticators meet the given requirements. For example, if
+     * biometric authentication is requested on a device with both face and fingerprint sensors, the
+     * returned string should indicate that either face or fingerprint authentication may be used,
+     * regardless of whether the user has enrolled or selected either as their preferred method.
+     *
+     * @param authenticators A bit field representing the types of {@link Authenticators} that may
+     *                       be used for authentication.
+     * @return The label for a button that invokes {@link BiometricPrompt} for authentication.
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    @Nullable
+    public CharSequence getSettingName(@Authenticators.Types int authenticators) {
+        if (mService != null) {
+            final int userId = mContext.getUserId();
+            final String opPackageName = mContext.getOpPackageName();
+            try {
+                return mService.getSettingName(userId, opPackageName, authenticators);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Slog.w(TAG, "getSettingName(): Service not connected");
+            return null;
+        }
+    }
 }
 
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index d8c9dbc..1472bb9 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -68,4 +68,16 @@
     // the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore
     // land as SIDs, and are used during key generation.
     long[] getAuthenticatorIds();
+
+    // Provides a localized string that may be used as the label for a button that invokes
+    // BiometricPrompt.
+    CharSequence getButtonLabel(int userId, String opPackageName, int authenticators);
+
+    // Provides a localized string that may be shown while the user is authenticating with
+    // BiometricPrompt.
+    CharSequence getPromptMessage(int userId, String opPackageName, int authenticators);
+
+    // Provides a localized string that may be shown as the title for an app setting that enables
+    // biometric authentication.
+    CharSequence getSettingName(int userId, String opPackageName, int authenticators);
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 2433186..6d8bf0f 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -75,4 +75,10 @@
     long[] getAuthenticatorIds(int callingUserId);
 
     int getCurrentStrength(int sensorId);
+
+    // Returns a bit field of the modality (or modalities) that are will be used for authentication.
+    int getCurrentModality(String opPackageName, int userId, int callingUserId, int authenticators);
+
+    // Returns a bit field of the authentication modalities that are supported by this device.
+    int getSupportedModalities(int authenticators);
 }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 16ab900..07ebbaf 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -24,13 +24,11 @@
 import android.hardware.camera2.impl.SyntheticKey;
 import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
 import android.hardware.camera2.params.SessionConfiguration;
-import android.hardware.camera2.utils.ArrayUtils;
 import android.hardware.camera2.utils.TypeReference;
 import android.os.Build;
 import android.util.Rational;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -641,27 +639,7 @@
      */
     @NonNull
     public Set<String> getPhysicalCameraIds() {
-        int[] availableCapabilities = get(REQUEST_AVAILABLE_CAPABILITIES);
-        if (availableCapabilities == null) {
-            throw new AssertionError("android.request.availableCapabilities must be non-null "
-                        + "in the characteristics");
-        }
-
-        if (!ArrayUtils.contains(availableCapabilities,
-                REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
-            return Collections.emptySet();
-        }
-        byte[] physicalCamIds = get(LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
-
-        String physicalCamIdString = null;
-        try {
-            physicalCamIdString = new String(physicalCamIds, "UTF-8");
-        } catch (java.io.UnsupportedEncodingException e) {
-            throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
-        }
-        String[] physicalCameraIdArray = physicalCamIdString.split("\0");
-
-        return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(physicalCameraIdArray)));
+        return mProperties.getPhysicalCameraIds();
     }
 
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
@@ -2931,6 +2909,74 @@
             new Key<android.util.Size>("android.scaler.defaultSecureImageSize", android.util.Size.class);
 
     /**
+     * <p>The available multi-resolution stream configurations that this
+     * physical camera device supports
+     * (i.e. format, width, height, output/input stream).</p>
+     * <p>This list contains a subset of the parent logical camera's multi-resolution stream
+     * configurations which belong to this physical camera, and it will advertise and will only
+     * advertise the maximum supported resolutions for a particular format.</p>
+     * <p>If this camera device isn't a physical camera device constituting a logical camera,
+     * but a standalone ULTRA_HIGH_RESOLUTION_SENSOR camera, this field represents the
+     * multi-resolution input/output stream configurations of default mode and max resolution
+     * modes. The sizes will be the maximum resolution of a particular format for default mode
+     * and max resolution mode.</p>
+     * <p>This field will only be advertised if the device is a physical camera of a
+     * logical multi-camera device or an ultra high resolution sensor camera. For a logical
+     * multi-camera, the camera API will derive the logical camera’s multi-resolution stream
+     * configurations from all physical cameras. For an ultra high resolution sensor camera, this
+     * is used directly as the camera’s multi-resolution stream configurations.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * <p><b>Limited capability</b> -
+     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+     *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfiguration[]> SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS =
+            new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.scaler.physicalCameraMultiResolutionStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class);
+
+    /**
+     * <p>The multi-resolution stream configurations supported by this logical camera
+     * or ultra high resolution sensor camera device.</p>
+     * <p>Multi-resolution streams can be used by a LOGICAL_MULTI_CAMERA or an
+     * ULTRA_HIGH_RESOLUTION_SENSOR camera where the images sent or received can vary in
+     * resolution per frame. This is useful in cases where the camera device's effective full
+     * resolution changes depending on factors such as the current zoom level, lighting
+     * condition, focus distance, or pixel mode.</p>
+     * <ul>
+     * <li>For a logical multi-camera implementing optical zoom, at different zoom level, a
+     * different physical camera may be active, resulting in different full-resolution image
+     * sizes.</li>
+     * <li>For an ultra high resolution camera, depending on whether the camera operates in default
+     * mode, or maximum resolution mode, the output full-size images may be of either binned
+     * resolution or maximum resolution.</li>
+     * </ul>
+     * <p>To use multi-resolution output streams, the supported formats can be queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats }.
+     * A {@link android.hardware.camera2.MultiResolutionImageReader } can then be created for a
+     * supported format with the MultiResolutionStreamInfo group queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo }.</p>
+     * <p>If a camera device supports multi-resolution output streams for a particular format, for
+     * each of its mandatory stream combinations, the camera device will support using a
+     * MultiResolutionImageReader for the MAXIMUM stream of supported formats. Refer to
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession } for additional details.</p>
+     * <p>To use multi-resolution input streams, the supported formats can be queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getInputFormats }.
+     * A reprocessable CameraCaptureSession can then be created using an {@link android.hardware.camera2.params.InputConfiguration InputConfiguration} constructed with
+     * the input MultiResolutionStreamInfo group, queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getInputInfo }.</p>
+     * <p>If a camera device supports multi-resolution {@code YUV} input and multi-resolution
+     * {@code YUV} output, or multi-resolution {@code PRIVATE} input and multi-resolution
+     * {@code PRIVATE} output, {@code JPEG} and {@code YUV} are guaranteed to be supported
+     * multi-resolution output stream formats. Refer to
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession } for
+     * details about the additional mandatory stream combinations in this case.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    @NonNull
+    @SyntheticKey
+    public static final Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP =
+            new Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap>("android.scaler.multiResolutionStreamConfigurationMap", android.hardware.camera2.params.MultiResolutionStreamConfigurationMap.class);
+
+    /**
      * <p>The area of the image sensor which corresponds to active pixels after any geometric
      * distortion correction has been applied.</p>
      * <p>This is the rectangle representing the size of the active region of the sensor (i.e.
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index ac6ba0a..af48b71 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -770,6 +770,8 @@
      * streams with {@code Y8} in all guaranteed stream combinations for the device's hardware level
      * and capabilities.</p>
      *
+     * <p>Clients can access the above mandatory stream combination tables via
+     * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p>
      *
      * <p>Devices capable of outputting HEIC formats ({@link StreamConfigurationMap#getOutputFormats}
      * contains {@link android.graphics.ImageFormat#HEIC}) will support substituting {@code JPEG}
@@ -777,8 +779,33 @@
      * level and capabilities. Calling createCaptureSession with both JPEG and HEIC outputs is not
      * supported.</p>
      *
-     * <p>Clients can access the above mandatory stream combination tables via
-     * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p>
+     * <p>Devices capable of multi-resolution output for a particular format (
+     * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo}
+     * returns a non-empty list) support using {@link MultiResolutionImageReader} for MAXIMUM
+     * resolution streams of that format for all mandatory stream combinations. For example,
+     * if a LIMITED camera device supports multi-resolution output streams for both {@code JPEG} and
+     * {@code PRIVATE}, in addition to the stream configurations
+     * in the LIMITED and Legacy table above, the camera device supports the following guaranteed
+     * stream combinations ({@code MULTI_RES} in the Max size column refers to a {@link
+     * MultiResolutionImageReader} created based on the variable max resolutions supported):
+     *
+     * <table>
+     * <tr><th colspan="7">LEGACY-level additional guaranteed combinations with MultiResolutionoutputs</th></tr>
+     * <tr> <th colspan="2" id="rb">Target 1</th> <th colspan="2" id="rb">Target 2</th>  <th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+     * <tr> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th></tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td colspan="2" id="rb"></td> <td>Simple preview, GPU video processing, or no-preview video recording.</td> </tr>
+     * <tr> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td colspan="2" id="rb"></td> <td>No-viewfinder still image capture.</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td>Standard still imaging.</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>Still capture plus in-app processing.</td> </tr>
+     * </table><br>
+     * <table>
+     * <tr><th colspan="7">LIMITED-level additional guaranteed configurations with MultiResolutionoutputs</th></tr>
+     * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+     * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+     * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>Two-input in-app processing with still capture.</td> </tr>
+     * </table><br>
+     * The same logic applies to other hardware levels and capabilities.
+     * </p>
      *
      * <p>Since the capabilities of camera devices vary greatly, a given camera device may support
      * target combinations with sizes outside of these guarantees, but this can only be tested for
@@ -939,6 +966,32 @@
      * </table><br>
      * </p>
      *
+     * <p>If a camera device supports multi-resolution {@code YUV} input and multi-resolution
+     * {@code YUV} output or supports multi-resolution {@code PRIVATE} input and multi-resolution
+     * {@code PRIVATE} output, the additional mandatory stream combinations for LIMITED and FULL devices are listed
+     * below ({@code MULTI_RES} in the Max size column refers to a
+     * {@link MultiResolutionImageReader} for output, and a multi-resolution
+     * {@link InputConfiguration} for input):
+     * <table>
+     * <tr><th colspan="11">LIMITED-level additional guaranteed configurations for creating a reprocessable capture session with multi-resolution input and multi-resolution outputs<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr>
+     * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr>
+     * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+     * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>No-viewfinder still image reprocessing.</td> </tr>
+     * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>ZSL(Zero-Shutter-Lag) still imaging.</td> </tr>
+     * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>ZSL still and in-app processing imaging.</td> </tr>
+     * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>ZSL in-app processing with still capture.</td> </tr>
+     * </table><br>
+     * <table>
+     * <tr><th colspan="11">FULL-level additional guaranteed configurations for creating a reprocessable capture session with multi-resolution input and multi-resolution outputs<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr>
+     * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr>
+     * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>Maximum-resolution ZSL in-app processing with regular preview.</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>Maximum-resolution two-input ZSL in-app processing.</td> </tr>
+     * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>ZSL still capture and in-app processing.</td> </tr>
+     * </table><br>
+     * No additional mandatory stream combinations for RAW capability and LEVEL-3 hardware level.
+     * </p>
+     *
      * <h3>Constrained high-speed recording</h3>
      *
      * <p>The application can use a
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 3e0e3f62..a3c6f2f 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -31,6 +31,7 @@
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.params.ExtensionSessionConfiguration;
 import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.params.StreamConfiguration;
 import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
 import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
 import android.hardware.display.DisplayManager;
@@ -51,6 +52,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -372,6 +374,47 @@
     }
 
     /**
+     * Get all physical cameras' multi-resolution stream configuration map
+     *
+     * <p>For a logical multi-camera, query the map between physical camera id and
+     * the physical camera's multi-resolution stream configuration. This map is in turn
+     * combined to form the logical camera's multi-resolution stream configuration map.</p>
+     */
+    private Map<String, StreamConfiguration[]> getPhysicalCameraMultiResolutionConfigs(
+            CameraMetadataNative info, ICameraService cameraService)
+            throws CameraAccessException {
+        HashMap<String, StreamConfiguration[]> multiResolutionStreamConfigurations =
+                new HashMap<String, StreamConfiguration[]>();
+
+        // Query the characteristics of all physical sub-cameras, and combine the multi-resolution
+        // stream configurations. Note that framework derived formats such as HEIC and DEPTH_JPEG
+        // aren't supported as multi-resolution input or output formats.
+        Set<String> physicalCameraIds = info.getPhysicalCameraIds();
+        try {
+            for (String physicalCameraId : physicalCameraIds) {
+                CameraMetadataNative physicalCameraInfo =
+                        cameraService.getCameraCharacteristics(physicalCameraId);
+                StreamConfiguration[] configs = physicalCameraInfo.get(
+                        CameraCharacteristics.
+                                SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS);
+                if (configs != null) {
+                    multiResolutionStreamConfigurations.put(physicalCameraId, configs);
+                }
+            }
+
+            // TODO: If this is an ultra high resolution sensor camera, combine the multi-resolution
+            // stream combination from "info" as well.
+        } catch (RemoteException e) {
+            ServiceSpecificException sse = new ServiceSpecificException(
+                    ICameraService.ERROR_DISCONNECTED,
+                    "Camera service is currently unavailable");
+            throwAsPublicException(sse);
+        }
+
+        return multiResolutionStreamConfigurations;
+    }
+
+    /**
      * <p>Query the capabilities of a camera device. These capabilities are
      * immutable for a given camera.</p>
      *
@@ -418,12 +461,19 @@
                 } catch (NumberFormatException e) {
                     Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer");
                 }
+
                 boolean hasConcurrentStreams =
                         CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
                 info.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
                 info.setDisplaySize(displaySize);
-                characteristics = new CameraCharacteristics(info);
 
+                Map<String, StreamConfiguration[]> multiResolutionSizeMap =
+                        getPhysicalCameraMultiResolutionConfigs(info, cameraService);
+                if (multiResolutionSizeMap.size() > 0) {
+                    info.setMultiResolutionStreamConfigurationMap(multiResolutionSizeMap);
+                }
+
+                characteristics = new CameraCharacteristics(info);
             } catch (ServiceSpecificException e) {
                 throwAsPublicException(e);
             } catch (RemoteException e) {
diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
new file mode 100644
index 0000000..c592f19
--- /dev/null
+++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
@@ -0,0 +1,309 @@
+/*
+ * 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.hardware.camera2;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.graphics.ImageFormat;
+import android.graphics.ImageFormat.Format;
+import android.hardware.HardwareBuffer;
+import android.hardware.HardwareBuffer.Usage;
+import android.media.Image;
+import android.media.ImageReader;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.Surface;
+
+
+import java.nio.NioUtils;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * <p>The MultiResolutionImageReader class wraps a group of {@link ImageReader ImageReaders} with
+ * the same format and different sizes, source camera Id, or camera sensor modes.</p>
+ *
+ * <p>The main use case of this class is for a
+ * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA logical
+ * multi-camera} or an ultra high resolution sensor camera to output variable-size images. For a
+ * logical multi-camera which implements optical zoom, different physical cameras may have different
+ * maximum resolutions. As a result, when the camera device switches between physical cameras
+ * depending on zoom ratio, the maximum resolution for a particular format may change. For an
+ * ultra high resolution sensor camera, the camera device may deem it better or worse to run in
+ * maximum resolution mode / default mode depending on lighting conditions. So the application may
+ * choose to let the camera device decide on its behalf.</p>
+ *
+ * <p>MultiResolutionImageReader should be used for a camera device only if the camera device
+ * supports multi-resolution output stream by advertising the specified output format in {@link
+ * CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}.</p>
+ *
+ * <p>To acquire images from the MultiResolutionImageReader, the application must use the
+ * {@link ImageReader} object passed by
+ * {@link ImageReader.OnImageAvailableListener#onImageAvailable} callback to call
+ * {@link ImageReader#acquireNextImage} or {@link ImageReader#acquireLatestImage}. The application
+ * must not use the {@link ImageReader} passed by an {@link
+ * ImageReader.OnImageAvailableListener#onImageAvailable} callback to acquire future images
+ * because future images may originate from a different {@link ImageReader} contained within the
+ * {@code MultiResolutionImageReader}.</p>
+ *
+ *
+ * @see ImageReader
+ * @see android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
+ */
+public class MultiResolutionImageReader implements AutoCloseable {
+
+    private static final String TAG = "MultiResolutionImageReader";
+
+    /**
+     * <p>
+     * Create a new multi-resolution reader based on a group of camera stream properties returned
+     * by a camera device.
+     * </p>
+     * <p>
+     * The valid size and formats depend on the camera characteristics.
+     * {@code MultiResolutionImageReader} for an image format is supported by the camera device if
+     * the format is in the supported multi-resolution output stream formats returned by
+     * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats}.
+     * If the image format is supported, the {@code MultiResolutionImageReader} object can be
+     * created with the {@code streams} objects returned by
+     * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo}.
+     * </p>
+     * <p>
+     * The {@code maxImages} parameter determines the maximum number of
+     * {@link Image} objects that can be be acquired from each of the {@code ImageReader}
+     * within the {@code MultiResolutionImageReader}. However, requesting more buffers will
+     * use up more memory, so it is important to use only the minimum number necessary. The
+     * application is strongly recommended to acquire no more than {@code maxImages} images
+     * from all of the internal ImageReader objects combined. By keeping track of the number of
+     * acquired images for the MultiResolutionImageReader, the application doesn't need to do the
+     * bookkeeping for each internal ImageReader returned from {@link
+     * ImageReader.OnImageAvailableListener#onImageAvailable onImageAvailable} callback.
+     * </p>
+     * <p>
+     * Unlike the normal ImageReader, the MultiResolutionImageReader has a more complex
+     * configuration sequence. Instead of passing the same surface to OutputConfiguration and
+     * CaptureRequest, the
+     * {@link android.hardware.camera2.params.OutputConfiguration#createInstancesForMultiResolutionOutput}
+     * call needs to be used to create the OutputConfigurations for session creation, and then
+     * {@link #getSurface} is used to get {@link CaptureRequest.Builder#addTarget the target for
+     * CaptureRequest}.
+     * </p>
+     * @param streams The group of multi-resolution stream info, which is used to create
+     *            a multi-resolution reader containing a number of ImageReader objects. Each
+     *            ImageReader object represents a multi-resolution stream in the group.
+     * @param format The format of the Image that this multi-resolution reader will produce.
+     *            This must be one of the {@link android.graphics.ImageFormat} or
+     *            {@link android.graphics.PixelFormat} constants. Note that not all formats are
+     *            supported, like ImageFormat.NV21. The supported multi-resolution
+     *            reader format can be queried by {@link
+     *            android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats}.
+     * @param maxImages The maximum number of images the user will want to
+     *            access simultaneously. This should be as small as possible to
+     *            limit memory use. Once maxImages images are obtained by the
+     *            user from any given internal ImageReader, one of them has to be released before
+     *            a new Image will become available for access through the ImageReader's
+     *            {@link ImageReader#acquireLatestImage()} or
+     *            {@link ImageReader#acquireNextImage()}. Must be greater than 0.
+     * @see Image
+     * @see
+     * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
+     * @see
+     * android.hardware.camera2.params.MultiResolutionStreamConfigurationMap
+     */
+    public static @NonNull MultiResolutionImageReader newInstance(
+            @NonNull Collection<MultiResolutionStreamInfo> streams,
+            @Format             int format,
+            @IntRange(from = 1) int maxImages) {
+        return new MultiResolutionImageReader(streams, format, maxImages);
+    }
+
+    /**
+     * @hide
+     */
+    protected MultiResolutionImageReader(Collection<MultiResolutionStreamInfo> streams,
+            int format, int maxImages) {
+        mFormat = format;
+        mMaxImages = maxImages;
+
+        if (streams == null || streams.size() <= 1) {
+            throw new IllegalArgumentException(
+                "The streams info collection must contain at least 2 entries");
+        }
+        if (mMaxImages < 1) {
+            throw new IllegalArgumentException(
+                "Maximum outstanding image count must be at least 1");
+        }
+
+        if (format == ImageFormat.NV21) {
+            throw new IllegalArgumentException(
+                    "NV21 format is not supported");
+        }
+
+        int numImageReaders = streams.size();
+        mReaders = new ImageReader[numImageReaders];
+        mStreamInfo = new MultiResolutionStreamInfo[numImageReaders];
+        int index = 0;
+        for (MultiResolutionStreamInfo streamInfo : streams) {
+            mReaders[index] = ImageReader.newInstance(streamInfo.getWidth(),
+                    streamInfo.getHeight(), format, maxImages);
+            mStreamInfo[index] = streamInfo;
+            index++;
+        }
+    }
+
+    /**
+     * Set onImageAvailableListener callback.
+     *
+     * <p>This function sets the onImageAvailableListener for all the internal
+     * {@link ImageReader} objects.</p>
+     *
+     * <p>For a multi-resolution ImageReader, the timestamps of images acquired in
+     * onImageAvailable callback from different internal ImageReaders may become
+     * out-of-order due to the asynchronous callbacks between the different resolution
+     * image queues.</p>
+     *
+     * @param listener
+     *            The listener that will be run.
+     * @param executor
+     *            The executor which will be used when invoking the callback.
+     */
+    @SuppressLint({"ExecutorRegistration", "SamShouldBeLast"})
+    public void setOnImageAvailableListener(
+            @Nullable ImageReader.OnImageAvailableListener listener,
+            @Nullable @CallbackExecutor Executor executor) {
+        for (int i = 0; i < mReaders.length; i++) {
+            mReaders[i].setOnImageAvailableListenerWithExecutor(listener, executor);
+        }
+    }
+
+    @Override
+    public void close() {
+        flush();
+
+        for (int i = 0; i < mReaders.length; i++) {
+            mReaders[i].close();
+        }
+    }
+
+    @Override
+    protected void finalize() {
+        close();
+    }
+
+    /**
+     * Flush pending images from all internal ImageReaders
+     *
+     * <p>Acquire and close pending images from all internal ImageReaders. This has the same
+     * effect as calling acquireLatestImage() on all internal ImageReaders, and closing all
+     * latest images.</p>
+     */
+    public void flush() {
+        flushOther(null);
+    }
+
+    /**
+     * Flush pending images from other internal ImageReaders
+     *
+     * <p>Acquire and close pending images from all internal ImageReaders except for the
+     * one specified.</p>
+     *
+     * @param reader The ImageReader object that won't be flushed.
+     *
+     * @hide
+     */
+    public void flushOther(ImageReader reader) {
+        for (int i = 0; i < mReaders.length; i++) {
+            if (reader != null && reader == mReaders[i]) {
+                continue;
+            }
+
+            while (true) {
+                Image image = mReaders[i].acquireNextImageNoThrowISE();
+                if (image == null) {
+                    break;
+                } else {
+                    image.close();
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the internal ImageReader objects
+     *
+     * @hide
+     */
+    public @NonNull ImageReader[] getReaders() {
+        return mReaders;
+    }
+
+    /**
+     * Get the surface that is used as a target for {@link CaptureRequest}
+     *
+     * <p>The application must use the surface returned by this function as a target for
+     * {@link CaptureRequest}. The camera device makes the decision on which internal
+     * {@code ImageReader} will receive the output image.</p>
+     *
+     * <p>Please note that holding on to the Surface objects returned by this method is not enough
+     * to keep their parent MultiResolutionImageReaders from being reclaimed. In that sense, a
+     * Surface acts like a {@link java.lang.ref.WeakReference weak reference} to the
+     * MultiResolutionImageReader that provides it.</p>
+     *
+     * @return a {@link Surface} to use as the target for a capture request.
+     */
+    public @NonNull Surface getSurface() {
+        //TODO: Pick the surface from the reader for default mode stream.
+        return mReaders[0].getSurface();
+    }
+
+    /**
+     * Get the MultiResolutionStreamInfo describing the ImageReader an image originates from
+     *
+     *<p>An image from a {@code MultiResolutionImageReader} is produced from one of the underlying
+     *{@code ImageReader}s. This function returns the {@link MultiResolutionStreamInfo} to describe
+     *the property for that {@code ImageReader}, such as width, height, and physical camera Id.</p>
+     *
+     * @param reader An internal ImageReader within {@code MultiResolutionImageReader}.
+     *
+     * @return The stream info describing the internal {@code ImageReader}.
+     */
+    public @NonNull MultiResolutionStreamInfo getStreamInfoForImageReader(
+            @NonNull ImageReader reader) {
+        for (int i = 0; i < mReaders.length; i++) {
+            if (reader == mReaders[i]) {
+                return mStreamInfo[i];
+            }
+        }
+
+        throw new IllegalArgumentException("ImageReader doesn't belong to this multi-resolution "
+                + "imagereader");
+    }
+
+    // mReaders and mStreamInfo has the same length, and their entries are 1:1 mapped.
+    private final ImageReader[] mReaders;
+    private final MultiResolutionStreamInfo[] mStreamInfo;
+
+    private final int mFormat;
+    private final int mMaxImages;
+}
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index da65f71..df8eecc 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -61,8 +61,8 @@
 
     private final List<CaptureResult> mPartialResults;
     private final int mSessionId;
-    // The map between physical camera id and capture result
-    private final HashMap<String, CaptureResult> mPhysicalCaptureResults;
+    // The map between physical camera ids and their total capture result
+    private final HashMap<String, TotalCaptureResult> mPhysicalCaptureResults;
 
     /**
      * Takes ownership of the passed-in camera metadata and the partial results
@@ -83,10 +83,11 @@
 
         mSessionId = sessionId;
 
-        mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
+        mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
         for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
-            CaptureResult physicalResult = new CaptureResult(onePhysicalResult.getCameraId(),
-                    onePhysicalResult.getCameraMetadata(), parent, extras);
+            TotalCaptureResult physicalResult = new TotalCaptureResult(
+                    onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(),
+                    parent, extras, /*partials*/null, sessionId, new PhysicalCaptureResultInfo[0]);
             mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
                     physicalResult);
         }
@@ -103,7 +104,7 @@
 
         mPartialResults = new ArrayList<>();
         mSessionId = CameraCaptureSession.SESSION_ID_NONE;
-        mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
+        mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
     }
 
     /**
@@ -146,8 +147,37 @@
      * cameras. Otherwise, an empty map is returned.</p>
 
      * @return unmodifiable map between physical camera ids and their capture result metadata
+     *
+     * @deprecated
+     * <p>Please use {@link #getPhysicalCameraTotalResults() instead to get the
+     * physical cameras' {@code TotalCaptureResult}.</p>
      */
     public Map<String, CaptureResult> getPhysicalCameraResults() {
         return Collections.unmodifiableMap(mPhysicalCaptureResults);
     }
+
+    /**
+     * Get the map between physical camera ids and their total capture result metadata
+     *
+     * <p>This function can be called for logical multi-camera devices, which are devices that have
+     * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability.</p>
+     *
+     * <p>If one or more streams from the underlying physical cameras were requested by the
+     * corresponding capture request, this function returns the total result metadata for those
+     * physical cameras. Otherwise, an empty map is returned.</p>
+     *
+     * <p>This function replaces the deprecated {@link #getPhysicalCameraResults}, and its return
+     * value is a map of TotalCaptureResult rather than CaptureResult. </p>
+     *
+     * <p>To reprocess an image from a physical camera stream, typically returned from a
+     * {@link MultiResolutionImageReader}, the application must look up this map to get the {@link
+     * TotalCaptureResult} from the physical camera and pass it to {@link
+     * CameraDevice#createReprocessCaptureRequest}.</p>
+     *
+     * @return unmodifiable map between physical camera ids and their total capture result metadata
+     */
+    @NonNull
+    public Map<String, TotalCaptureResult> getPhysicalCameraTotalResults() {
+        return Collections.unmodifiableMap(mPhysicalCaptureResults);
+    }
 }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ce3c81a..4defd23 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -36,6 +36,8 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.params.ExtensionSessionConfiguration;
 import android.hardware.camera2.params.InputConfiguration;
+import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
@@ -468,7 +470,8 @@
                     }
                     if (inputConfig != null) {
                         int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(),
-                                inputConfig.getHeight(), inputConfig.getFormat());
+                                inputConfig.getHeight(), inputConfig.getFormat(),
+                                inputConfig.isMultiResolution());
                         mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
                                 streamId, inputConfig);
                     }
@@ -1355,7 +1358,42 @@
     }
 
     private void checkInputConfiguration(InputConfiguration inputConfig) {
-        if (inputConfig != null) {
+        if (inputConfig == null) {
+            return;
+        }
+
+        if (inputConfig.isMultiResolution()) {
+            MultiResolutionStreamConfigurationMap configMap = mCharacteristics.get(
+                    CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP);
+
+            int[] inputFormats = configMap.getInputFormats();
+            boolean validFormat = false;
+            for (int format : inputFormats) {
+                if (format == inputConfig.getFormat()) {
+                    validFormat = true;
+                }
+            }
+
+            if (validFormat == false) {
+                throw new IllegalArgumentException("multi-resolution input format " +
+                        inputConfig.getFormat() + " is not valid");
+            }
+
+            boolean validSize = false;
+            Collection<MultiResolutionStreamInfo> inputStreamInfo =
+                    configMap.getInputInfo(inputConfig.getFormat());
+            for (MultiResolutionStreamInfo info : inputStreamInfo) {
+                if (inputConfig.getWidth() == info.getWidth() &&
+                        inputConfig.getHeight() == info.getHeight()) {
+                    validSize = true;
+                }
+            }
+
+            if (validSize == false) {
+                throw new IllegalArgumentException("Multi-resolution input size " +
+                        inputConfig.getWidth() + "x" + inputConfig.getHeight() + " is not valid");
+            }
+        } else {
             StreamConfigurationMap configMap = mCharacteristics.get(
                     CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
 
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index e9bae0b..0cdf744 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -16,6 +16,7 @@
 
 package android.hardware.camera2.impl;
 
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.ImageFormat;
 import android.graphics.Point;
@@ -53,6 +54,7 @@
 import android.hardware.camera2.params.HighSpeedVideoConfiguration;
 import android.hardware.camera2.params.LensShadingMap;
 import android.hardware.camera2.params.MandatoryStreamCombination;
+import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap;
 import android.hardware.camera2.params.OisSample;
 import android.hardware.camera2.params.RecommendedStreamConfiguration;
 import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
@@ -61,6 +63,7 @@
 import android.hardware.camera2.params.StreamConfigurationDuration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.params.TonemapCurve;
+import android.hardware.camera2.utils.ArrayUtils;
 import android.hardware.camera2.utils.TypeReference;
 import android.location.Location;
 import android.location.LocationManager;
@@ -79,9 +82,14 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Implementation of camera metadata marshal/unmarshal across Binder to
@@ -747,6 +755,15 @@
                         return (T) metadata.getExtendedSceneModeCapabilities();
                     }
                 });
+        sGetCommandMap.put(
+                CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP.getNativeKey(),
+                        new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getMultiResolutionStreamConfigurationMap();
+                    }
+                });
     }
 
     private int[] getAvailableFormats() {
@@ -1688,6 +1705,7 @@
     private boolean mHasMandatoryConcurrentStreams = false;
     private Size mDisplaySize = new Size(0, 0);
     private long mBufferSize = 0;
+    private MultiResolutionStreamConfigurationMap mMultiResolutionStreamConfigurationMap = null;
 
     /**
      * Set the current camera Id.
@@ -1723,6 +1741,30 @@
         mDisplaySize = displaySize;
     }
 
+    /**
+     * Set the multi-resolution stream configuration map.
+     *
+     * @param multiResolutionMap The multi-resolution stream configuration map.
+     *
+     * @hide
+     */
+    public void setMultiResolutionStreamConfigurationMap(
+            @NonNull Map<String, StreamConfiguration[]> multiResolutionMap) {
+        mMultiResolutionStreamConfigurationMap =
+                new MultiResolutionStreamConfigurationMap(multiResolutionMap);
+    }
+
+    /**
+     * Get the multi-resolution stream configuration map.
+     *
+     * @return The multi-resolution stream configuration map.
+     *
+     * @hide
+     */
+    public MultiResolutionStreamConfigurationMap getMultiResolutionStreamConfigurationMap() {
+        return mMultiResolutionStreamConfigurationMap;
+    }
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private long mMetadataPtr; // native std::shared_ptr<CameraMetadata>*
 
@@ -1777,6 +1819,7 @@
         mCameraId = other.mCameraId;
         mHasMandatoryConcurrentStreams = other.mHasMandatoryConcurrentStreams;
         mDisplaySize = other.mDisplaySize;
+        mMultiResolutionStreamConfigurationMap = other.mMultiResolutionStreamConfigurationMap;
         updateNativeAllocation();
         other.updateNativeAllocation();
     }
@@ -1980,6 +2023,39 @@
         return true;
     }
 
+    /**
+     * Return the set of physical camera ids that this logical {@link CameraDevice} is made
+     * up of.
+     *
+     * If the camera device isn't a logical camera, return an empty set.
+     *
+     * @hide
+     */
+    public Set<String> getPhysicalCameraIds() {
+        int[] availableCapabilities = get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+        if (availableCapabilities == null) {
+            throw new AssertionError("android.request.availableCapabilities must be non-null "
+                        + "in the characteristics");
+        }
+
+        if (!ArrayUtils.contains(availableCapabilities,
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
+            return Collections.emptySet();
+        }
+        byte[] physicalCamIds = get(CameraCharacteristics.LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
+
+        String physicalCamIdString = null;
+        try {
+            physicalCamIdString = new String(physicalCamIds, "UTF-8");
+        } catch (java.io.UnsupportedEncodingException e) {
+            throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
+        }
+        String[] physicalCameraIdArray = physicalCamIdString.split("\0");
+
+        return Collections.unmodifiableSet(
+                new HashSet<String>(Arrays.asList(physicalCameraIdArray)));
+    }
+
     static {
         registerAllMarshalers();
     }
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index ba4395f..b6b1968 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -140,9 +140,10 @@
         }
     }
 
-    public int createInputStream(int width, int height, int format) throws CameraAccessException {
+    public int createInputStream(int width, int height, int format, boolean isMultiResolution)
+            throws CameraAccessException {
         try {
-            return mRemoteDevice.createInputStream(width, height, format);
+            return mRemoteDevice.createInputStream(width, height, format, isMultiResolution);
         } catch (Throwable t) {
             CameraManager.throwAsPublicException(t);
             throw new UnsupportedOperationException("Unexpected exception", t);
diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java
index 0a50f97..d63683f 100644
--- a/core/java/android/hardware/camera2/params/InputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/InputConfiguration.java
@@ -16,9 +16,17 @@
 
 package android.hardware.camera2.params;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.ImageFormat.Format;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
 import android.hardware.camera2.utils.HashCodeHelpers;
 
+import java.util.Collection;
+import java.util.List;
+
+import static com.android.internal.util.Preconditions.*;
+
 /**
  * Immutable class to store an input configuration that is used to create a reprocessable capture
  * session.
@@ -31,11 +39,12 @@
     private final int mWidth;
     private final int mHeight;
     private final int mFormat;
+    private final boolean mIsMultiResolution;
 
     /**
      * Create an input configration with the width, height, and user-defined format.
      *
-     * <p>Images of an user-defined format are accessible by applications. Use
+     * <p>Images of a user-defined format are accessible by applications. Use
      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
      * to query supported input formats</p>
      *
@@ -51,6 +60,52 @@
         mWidth = width;
         mHeight = height;
         mFormat = format;
+        mIsMultiResolution = false;
+    }
+
+    /**
+     * Create an input configration with the format and a list of multi-resolution input stream
+     * info.
+     *
+     * <p>Use {@link
+     * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}
+     * to query supported multi-resolution input formats.</p>
+     *
+     * <p>To do reprocessing with variable resolution input, the application calls
+     * {@link android.media.ImageWriter#queueInputImage ImageWriter.queueInputImage}
+     * using an image from an {@link android.media.ImageReader ImageReader} or {@link
+     * android.hardware.camera2.MultiResolutionImageReader MultiResolutionImageReader}. See
+     * {@link android.hardware.camera2.CameraDevice#createReprocessCaptureRequest} for more
+     * details on camera reprocessing.
+     * </p>
+     *
+     * @param multiResolutionInputs A group of multi-resolution input info for the specified format.
+     * @param format Format of the input buffers. One of ImageFormat or PixelFormat constants.
+     *
+     * @see android.graphics.ImageFormat
+     * @see android.graphics.PixelFormat
+     * @see
+     * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
+     */
+    public InputConfiguration(@NonNull Collection<MultiResolutionStreamInfo> multiResolutionInputs,
+            @Format int format) {
+        checkCollectionNotEmpty(multiResolutionInputs, "Input multi-resolution stream info");
+        //TODO: Pick the default mode stream info for ultra-high resolution sensor camera
+        MultiResolutionStreamInfo info = multiResolutionInputs.iterator().next();
+        mWidth = info.getWidth();
+        mHeight = info.getHeight();
+        mFormat = format;
+        mIsMultiResolution = true;
+    }
+
+    /**
+     * @hide
+     */
+    public InputConfiguration(int width, int height, int format, boolean isMultiResolution) {
+        mWidth = width;
+        mHeight = height;
+        mFormat = format;
+        mIsMultiResolution = isMultiResolution;
     }
 
     /**
@@ -81,6 +136,18 @@
     }
 
     /**
+     * Whether this input configuration is of multi-resolution.
+     *
+     * <p>An multi-resolution InputConfiguration means that the reprocessing session created from it
+     * allows input images of different sizes.</p>
+     *
+     * @return  this input configuration is multi-resolution or not.
+     */
+    public boolean isMultiResolution() {
+        return mIsMultiResolution;
+    }
+
+    /**
      * Check if this InputConfiguration is equal to another InputConfiguration.
      *
      * <p>Two input configurations are equal if and only if they have the same widths, heights, and
@@ -100,7 +167,8 @@
 
         if (otherInputConfig.getWidth() == mWidth &&
                 otherInputConfig.getHeight() == mHeight &&
-                otherInputConfig.getFormat() == mFormat) {
+                otherInputConfig.getFormat() == mFormat &&
+                otherInputConfig.isMultiResolution() == mIsMultiResolution) {
             return true;
         }
         return false;
@@ -111,19 +179,21 @@
      */
     @Override
     public int hashCode() {
-        return HashCodeHelpers.hashCode(mWidth, mHeight, mFormat);
+        return HashCodeHelpers.hashCode(mWidth, mHeight, mFormat, mIsMultiResolution ? 1 : 0);
     }
 
     /**
      * Return this {@link InputConfiguration} as a string representation.
      *
-     * <p> {@code "InputConfiguration(w:%d, h:%d, format:%d)"}, where {@code %d} represents
-     * the width, height, and format, respectively.</p>
+     * <p> {@code "InputConfiguration(w:%d, h:%d, format:%d, isMultiResolution:%d)"},
+     * where {@code %d} represents the width, height, format, and multi-resolution flag
+     * respectively.</p>
      *
      * @return string representation of {@link InputConfiguration}
      */
     @Override
     public String toString() {
-        return String.format("InputConfiguration(w:%d, h:%d, format:%d)", mWidth, mHeight, mFormat);
+        return String.format("InputConfiguration(w:%d, h:%d, format:%d, isMultiResolution %b)",
+                mWidth, mHeight, mFormat, mIsMultiResolution);
     }
 }
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 776d155..8a0172e 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -1297,19 +1297,6 @@
         }
 
         /**
-         * Size comparison method used by size comparators.
-         */
-        private static int compareSizes(int widthA, int heightA, int widthB, int heightB) {
-            long left = widthA * (long) heightA;
-            long right = widthB * (long) heightB;
-            if (left == right) {
-                left = widthA;
-                right = widthB;
-            }
-            return (left < right) ? -1 : (left > right ? 1 : 0);
-        }
-
-        /**
          * Size comparator that compares the number of pixels it covers.
          *
          * <p>If two the areas of two sizes are same, compare the widths.</p>
@@ -1317,8 +1304,8 @@
         public static class SizeComparator implements Comparator<Size> {
             @Override
             public int compare(@NonNull Size lhs, @NonNull Size rhs) {
-                return compareSizes(lhs.getWidth(), lhs.getHeight(), rhs.getWidth(),
-                        rhs.getHeight());
+                return StreamConfigurationMap.compareSizes(lhs.getWidth(), lhs.getHeight(),
+                        rhs.getWidth(), rhs.getHeight());
             }
         }
 
diff --git a/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java
new file mode 100644
index 0000000..1b368fb
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java
@@ -0,0 +1,335 @@
+/*
+ * 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.hardware.camera2.params;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import android.graphics.ImageFormat;
+import android.graphics.ImageFormat.Format;
+import android.graphics.PixelFormat;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.utils.HashCodeHelpers;
+
+import android.util.Size;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.Set;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Immutable class to store the information of the multi-resolution streams supported by
+ * the camera device.
+ *
+ * <p>For a {@link
+ * android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
+ * logical multi-camera} or an ultra high resolution sensor camera, the maximum resolution of images
+ * produced by the camera device may be variable. For example, for a logical multi-camera, depending
+ * on factors such as current zoom ratio, the camera device may be backed by different physical
+ * cameras. If the physical cameras are of different resolutions, the application may intend to
+ * consume the variable full resolution images from the physical cameras. For an ultra high
+ * resolution sensor camera, the same use case exists where depending on lighting conditions, the
+ * camera device may deem it better to run in default mode and maximum resolution mode.
+ * </p>
+ *
+ * <p>For the use cases described above, multi-resolution output streams can be used by
+ * {@link android.hardware.camera2.MultiResolutionImageReader} to allow the
+ * camera device to output variable size maximum-resolution images.</p>
+ *
+ * <p>Similarly, multi-resolution input streams can be used for reprocessing of variable size
+ * images. In order to reprocess input images of different sizes, the {@link InputConfiguration}
+ * used for creating reprocessable session can be initialized using the group of input stream
+ * configurations returned by {@link #getInputInfo}.</p>
+ */
+public final class MultiResolutionStreamConfigurationMap {
+    /**
+     * Create a new {@link MultiResolutionStreamConfigurationMap}.
+     *
+     * @param configurations a non-{@code null} array of multi-resolution stream
+     *        configurations supported by this camera device
+     * @hide
+     */
+    public MultiResolutionStreamConfigurationMap(
+            @NonNull Map<String, StreamConfiguration[]> configurations) {
+        checkNotNull(configurations, "multi-resolution configurations must not be null");
+        if (configurations.size() == 0) {
+            throw new IllegalArgumentException("multi-resolution configurations must not be empty");
+        }
+
+        mConfigurations = configurations;
+
+        // For each multi-resolution stream configuration, track how many formats and sizes there
+        // are available to configure
+        for (Map.Entry<String, StreamConfiguration[]> entry :
+                mConfigurations.entrySet()) {
+            String cameraId = entry.getKey();
+            StreamConfiguration[] configs = entry.getValue();
+
+            for (int i = 0; i < configs.length; i++) {
+                StreamConfiguration config = configs[i];
+                int format = config.getFormat();
+
+                MultiResolutionStreamInfo multiResolutionStreamInfo = new MultiResolutionStreamInfo(
+                        config.getWidth(), config.getHeight(), cameraId);
+                Map<Integer, List<MultiResolutionStreamInfo>> destMap;
+                if (config.isInput()) {
+                    destMap = mMultiResolutionInputConfigs;
+                } else {
+                    destMap = mMultiResolutionOutputConfigs;
+                }
+
+                if (!destMap.containsKey(format)) {
+                    List<MultiResolutionStreamInfo> multiResolutionStreamInfoList =
+                            new ArrayList<MultiResolutionStreamInfo>();
+                    destMap.put(format, multiResolutionStreamInfoList);
+                }
+                destMap.get(format).add(multiResolutionStreamInfo);
+            }
+        }
+    }
+
+    /**
+     * Size comparator that compares the number of pixels two MultiResolutionStreamInfo size covers.
+     *
+     * <p>If two the areas of two sizes are same, compare the widths.</p>
+     *
+     * @hide
+     */
+    public static class SizeComparator implements Comparator<MultiResolutionStreamInfo> {
+        @Override
+        public int compare(@NonNull MultiResolutionStreamInfo lhs,
+                @NonNull MultiResolutionStreamInfo rhs) {
+            return StreamConfigurationMap.compareSizes(
+                    lhs.getWidth(), lhs.getHeight(), rhs.getWidth(), rhs.getHeight());
+        }
+    }
+
+    /**
+     * Get the output formats in this multi-resolution stream configuration.
+     *
+     * <p>A logical multi-camera or an ultra high resolution sensor camera may support
+     * {@link android.hardware.camera2.MultiResolutionImageReader} to dynamically output maximum
+     * resolutions of different sizes (when switching between physical cameras, or between different
+     * modes of an ultra high resolution sensor camera). This function returns the formats
+     * supported for such case.</p>
+     *
+     * <p>All image formats returned by this function will be defined in either {@link ImageFormat}
+     * or in {@link PixelFormat} (and there is no possibility of collision).</p>
+     *
+     * @return an array of integer format, or empty array if multi-resolution output is not
+     *         supported
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     * @see android.hardware.camera2.MultiResolutionImageReader
+     */
+    public @NonNull @Format int[] getOutputFormats() {
+        return getPublicImageFormats(/*output*/true);
+    }
+
+    /**
+     * Get the input formats in this multi-resolution stream configuration.
+     *
+     * <p>A logical multi-camera or ultra high resolution sensor camera may support reprocessing
+     * images of different resolutions when switching between physical cameras, or between
+     * different modes of the ultra high resolution sensor camera. This function returns the
+     * formats supported for such case.</p>
+     *
+     * <p>The supported output format for an input format can be queried by calling the camera
+     * device's {@link StreamConfigurationMap#getValidOutputFormatsForInput}.</p>
+     *
+     * <p>All image formats returned by this function will be defined in either {@link ImageFormat}
+     * or in {@link PixelFormat} (and there is no possibility of collision).</p>
+     *
+     * @return an array of integer format, or empty array if no multi-resolution reprocessing is
+     *         supported
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    public @NonNull @Format int[] getInputFormats() {
+        return getPublicImageFormats(/*output*/false);
+    }
+
+    // Get the list of publicly visible multi-resolution input/output stream formats
+    private int[] getPublicImageFormats(boolean output) {
+        Map<Integer, List<MultiResolutionStreamInfo>> multiResolutionConfigs =
+                output ? mMultiResolutionOutputConfigs : mMultiResolutionInputConfigs;
+        int formatCount = multiResolutionConfigs.size();
+
+        int[] formats = new int[formatCount];
+        int i = 0;
+        for (Integer format : multiResolutionConfigs.keySet()) {
+            formats[i++] = StreamConfigurationMap.imageFormatToPublic(format);
+        }
+
+        return formats;
+    }
+
+    /**
+     * Get a group of {@code MultiResolutionStreamInfo} with the requested output image
+     * {@code format}
+     *
+     * <p>The {@code format} should be a supported format (one of the formats returned by
+     * {@link #getOutputFormats}).</p>
+     *
+     * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+     * @return
+     *          a group of supported {@link MultiResolutionStreamInfo}. If the {@code format} is not
+     *          a supported multi-resolution output, an empty group is returned.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     * @see #getOutputFormats
+     */
+    public @NonNull Collection<MultiResolutionStreamInfo> getOutputInfo(@Format int format) {
+        return getInfo(format, /*false*/ true);
+    }
+
+    /**
+     * Get a group of {@code MultiResolutionStreamInfo} with the requested input image {@code format}
+     *
+     * <p>The {@code format} should be a supported format (one of the formats returned by
+     * {@link #getInputFormats}).</p>
+     *
+     * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+     * @return
+     *          a group of supported {@link MultiResolutionStreamInfo}. If the {@code format} is not
+     *          a supported multi-resolution input, an empty group is returned.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     * @see #getInputFormats
+     */
+    public @NonNull Collection<MultiResolutionStreamInfo> getInputInfo(@Format int format) {
+        return getInfo(format, /*false*/ false);
+    }
+
+    // Get multi-resolution stream info for a particular format
+    private @NonNull Collection<MultiResolutionStreamInfo> getInfo(int format, boolean output) {
+        int internalFormat = StreamConfigurationMap.imageFormatToInternal(format);
+        Map<Integer, List<MultiResolutionStreamInfo>> multiResolutionConfigs =
+                output ? mMultiResolutionOutputConfigs : mMultiResolutionInputConfigs;
+        if (multiResolutionConfigs.containsKey(internalFormat)) {
+            return Collections.unmodifiableCollection(multiResolutionConfigs.get(internalFormat));
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    private void appendConfigurationsString(StringBuilder sb, boolean output) {
+        sb.append(output ? "Outputs(" : "Inputs(");
+        int[] formats = getPublicImageFormats(output);
+        if (formats != null) {
+            for (int format : formats) {
+                Collection<MultiResolutionStreamInfo> streamInfoList =
+                        getInfo(format, output);
+                sb.append("[" + StreamConfigurationMap.formatToString(format) + ":");
+                for (MultiResolutionStreamInfo streamInfo : streamInfoList) {
+                    sb.append(String.format("[w:%d, h:%d, id:%s], ",
+                            streamInfo.getWidth(), streamInfo.getHeight(),
+                            streamInfo.getPhysicalCameraId()));
+                }
+                // Remove the pending ", "
+                if (sb.charAt(sb.length() - 1) == ' ') {
+                    sb.delete(sb.length() - 2, sb.length());
+                }
+                sb.append("]");
+            }
+        }
+        sb.append(")");
+    }
+
+    /**
+     * Check if this {@link MultiResolutionStreamConfigurationMap} is equal to another
+     * {@link MultiResolutionStreamConfigurationMap}.
+     *
+     * @return {@code true} if the objects were equal, {@code false} otherwise
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof MultiResolutionStreamConfigurationMap) {
+            final MultiResolutionStreamConfigurationMap other =
+                    (MultiResolutionStreamConfigurationMap) obj;
+            if (!mConfigurations.keySet().equals(other.mConfigurations.keySet())) {
+                return false;
+            }
+
+            for (String id : mConfigurations.keySet()) {
+                if (!Arrays.equals(mConfigurations.get(id), other.mConfigurations.get(id))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return HashCodeHelpers.hashCodeGeneric(
+                mConfigurations, mMultiResolutionOutputConfigs, mMultiResolutionInputConfigs);
+    }
+
+    /**
+     * Return this {@link MultiResolutionStreamConfigurationMap} as a string representation.
+     *
+     * <p>{@code "MultiResolutionStreamConfigurationMap(Outputs([format1: [w:%d, h:%d, id:%s], ...
+     * ... [w:%d, h:%d, id:%s]), [format2: [w:%d, h:%d, id:%s], ... [w:%d, h:%d, id:%s]], ...),
+     * Inputs([format1: [w:%d, h:%d, id:%s], ... [w:%d, h:%d, id:%s], ...).</p>
+     *
+     * @return string representation of {@link MultiResolutionStreamConfigurationMap}
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("MultiResolutionStreamConfigurationMap(");
+        appendConfigurationsString(sb, /*output*/ true);
+        sb.append(",");
+        appendConfigurationsString(sb, /*output*/ false);
+        sb.append(")");
+
+        return sb.toString();
+    }
+
+
+    private final Map<String, StreamConfiguration[]> mConfigurations;
+
+    /** Format -> list of MultiResolutionStreamInfo used to create MultiResolutionImageReader */
+    private final Map<Integer, List<MultiResolutionStreamInfo>> mMultiResolutionOutputConfigs
+            = new HashMap<Integer, List<MultiResolutionStreamInfo>>();
+    /** Format -> list of MultiResolutionStreamInfo used for multi-resolution reprocessing */
+    private final Map<Integer, List<MultiResolutionStreamInfo>> mMultiResolutionInputConfigs
+            = new HashMap<Integer, List<MultiResolutionStreamInfo>>();
+}
diff --git a/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java
new file mode 100644
index 0000000..aa1d1d4
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java
@@ -0,0 +1,113 @@
+/*
+ * 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.hardware.camera2.params;
+
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * A utility class describing the properties of one stream of fixed-size image buffers
+ * backing a multi-resolution image stream.
+ *
+ * <p>A group of {@link MultiResolutionStreamInfo} are used to describe the properties of a
+ * multi-resolution image stream for a particular format. The
+ * {@link android.hardware.camera2.MultiResolutionImageReader} class represents a
+ * multi-resolution output stream, and is constructed using a group of
+ * {@link MultiResolutionStreamInfo}. A group of {@link MultiResolutionStreamInfo} can also be used
+ * to create a multi-resolution reprocessable camera capture session. See
+ * {@link android.hardware.camera2.params.InputConfiguration} for details.</p>
+ *
+ * @see InputConfiguration
+ * @see android.hardware.camera2.MultiResolutionImageReader
+ */
+public class MultiResolutionStreamInfo {
+    private int mStreamWidth;
+    private int mStreamHeight;
+    private String mPhysicalCameraId;
+
+    /**
+     * Create a new {@link MultiResolutionStreamInfo}.
+     *
+     * <p>This class creates a {@link MultiResolutionStreamInfo} using image width, image height,
+     * and the physical camera Id images originate from.</p>
+     *
+     * <p>Normally applications do not need to create these directly. Use {@link
+     * MultiResolutionStreamConfigurationMap#getOutputInfo} or {@link
+     * MultiResolutionStreamConfigurationMap#getInputInfo} to obtain them for a particular format
+     * instead.</p>
+     */
+    public MultiResolutionStreamInfo(int streamWidth, int streamHeight,
+            @NonNull String physicalCameraId) {
+        mStreamWidth = streamWidth;
+        mStreamHeight = streamHeight;
+        mPhysicalCameraId = physicalCameraId;
+    }
+
+    /**
+     * The width of this particular image buffer stream in pixels.
+     */
+    public int getWidth() {
+        return mStreamWidth;
+    }
+
+    /**
+     * The height of this particular image buffer stream in pixels.
+     */
+    public int getHeight() {
+        return mStreamHeight;
+    }
+
+    /**
+     * The physical camera Id of this particular image buffer stream.
+     */
+    public @NonNull String getPhysicalCameraId() {
+        return mPhysicalCameraId;
+    }
+
+    /**
+     * Check if this {@link MultiResolutionStreamInfo} is equal to another
+     * {@link MultiResolutionStreamInfo}.
+     *
+     * @return {@code true} if the objects were equal, {@code false} otherwise
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof MultiResolutionStreamInfo) {
+            final MultiResolutionStreamInfo other = (MultiResolutionStreamInfo) obj;
+            return mStreamWidth == other.mStreamWidth &&
+                    mStreamHeight == other.mStreamHeight &&
+                    mPhysicalCameraId.equals(other.mPhysicalCameraId);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mStreamWidth, mStreamHeight, mPhysicalCameraId);
+    }
+}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index a20a1bf..e31bd60 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -24,9 +24,13 @@
 import android.annotation.SystemApi;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.MultiResolutionImageReader;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
 import android.hardware.camera2.utils.HashCodeHelpers;
 import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.ImageReader;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -34,6 +38,7 @@
 import android.view.Surface;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -81,6 +86,13 @@
  * {@link CameraCaptureSession#updateOutputConfiguration} can be called after the configuration
  * finalize method returns without exceptions.</li>
  *
+ * <li>If the camera device supports multi-resolution output streams, {@link
+ * CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP} will contain the
+ * formats and their corresponding stream info. The application can use an OutputConfiguration
+ * created with the multi-resolution stream info queried from {@link
+ * MultiResolutionStreamConfigurationMap#getOutputInfo} and
+ * {@link android.hardware.camera2.MultiResolutionImageReader} to capture variable size images.
+ *
  * </ul>
  *
  * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats except
@@ -88,6 +100,7 @@
  * device support. On prior API levels, only {@link ImageFormat#PRIVATE} format may be used.</p>
  *
  * @see CameraDevice#createCaptureSessionByOutputConfigurations
+ * @see CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
  *
  */
 public final class OutputConfiguration implements Parcelable {
@@ -206,6 +219,33 @@
     }
 
     /**
+     * Set the multi-resolution output flag.
+     *
+     * <p>Specify that this OutputConfiguration is part of a multi-resolution output stream group
+     * used by {@link android.hardware.camera2.MultiResolutionImageReader}.</p>
+     *
+     * <p>This function must only be called for an OutputConfiguration with a non-negative
+     * group ID. And all OutputConfigurations of a MultiResolutionImageReader will have the same
+     * group ID and have this flag set.</p>
+     *
+     * @throws IllegalStateException If surface sharing is enabled via {@link #enableSurfaceSharing}
+     *         call, or no non-negative group ID has been set.
+     * @hide
+     */
+    void setMultiResolutionOutput() {
+        if (mIsShared) {
+            throw new IllegalStateException("Multi-resolution output flag must not be set for " +
+                    "configuration with surface sharing");
+        }
+        if (mSurfaceGroupId == SURFACE_GROUP_ID_NONE) {
+            throw new IllegalStateException("Multi-resolution output flag should only be set for " +
+                    "surface with non-negative group ID");
+        }
+
+        mIsMultiResolution = true;
+    }
+
+    /**
      * Create a new {@link OutputConfiguration} instance.
      *
      * <p>This constructor takes an argument for desired camera rotation</p>
@@ -265,6 +305,45 @@
         mIsDeferredConfig = false;
         mIsShared = false;
         mPhysicalCameraId = null;
+        mIsMultiResolution = false;
+    }
+
+    /**
+     * Create a list of {@link OutputConfiguration} instances for the outputs used by a
+     * {@link android.hardware.camera2.MultiResolutionImageReader}.
+     *
+     * <p>This constructor takes an argument for a
+     * {@link android.hardware.camera2.MultiResolutionImageReader}.</p>
+     *
+     * @param multiResolutionImageReader
+     *          The multi-resolution image reader object.
+     */
+    public static @NonNull Collection<OutputConfiguration> createInstancesForMultiResolutionOutput(
+            @NonNull MultiResolutionImageReader multiResolutionImageReader)  {
+        checkNotNull(multiResolutionImageReader, "Multi-resolution image reader must not be null");
+
+        int groupId = MULTI_RESOLUTION_GROUP_ID_COUNTER;
+        MULTI_RESOLUTION_GROUP_ID_COUNTER++;
+        // Skip in case the group id counter overflows to -1, the invalid value.
+        if (MULTI_RESOLUTION_GROUP_ID_COUNTER == -1) {
+            MULTI_RESOLUTION_GROUP_ID_COUNTER++;
+        }
+
+        ImageReader[] imageReaders = multiResolutionImageReader.getReaders();
+        ArrayList<OutputConfiguration> configs = new ArrayList<OutputConfiguration>();
+        for (int i = 0; i < imageReaders.length; i++) {
+            MultiResolutionStreamInfo streamInfo =
+                    multiResolutionImageReader.getStreamInfoForImageReader(imageReaders[i]);
+
+            OutputConfiguration config = new OutputConfiguration(
+                    groupId, imageReaders[i].getSurface());
+            config.setPhysicalCameraId(streamInfo.getPhysicalCameraId());
+            config.setMultiResolutionOutput();
+            configs.add(config);
+            // TODO: Set sensor pixel mode for ultra high resolution sensor camera.
+        }
+
+        return configs;
     }
 
     /**
@@ -319,6 +398,7 @@
         mIsDeferredConfig = true;
         mIsShared = false;
         mPhysicalCameraId = null;
+        mIsMultiResolution = false;
     }
 
     /**
@@ -355,8 +435,18 @@
      * <p>Up to {@link #getMaxSharedSurfaceCount} surfaces can be shared for an OutputConfiguration.
      * The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView,
      * MediaRecorder, MediaCodec, or implementation defined ImageReader.</p>
+     *
+     * <p>This function must not be called from OuptutConfigurations created by {@link
+     * #createInstancesForMultiResolutionOutput}.</p>
+     *
+     * @throws IllegalStateException If this OutputConfiguration is created via {@link
+     * #createInstancesForMultiResolutionOutput} to back a MultiResolutionImageReader.
      */
     public void enableSurfaceSharing() {
+        if (mIsMultiResolution) {
+            throw new IllegalStateException("Cannot enable surface sharing on "
+                    + "multi-resolution output configurations");
+        }
         mIsShared = true;
     }
 
@@ -368,8 +458,7 @@
      * This call achieves it by mapping the OutputConfiguration to the physical camera id.</p>
      *
      * <p>The valid physical camera ids can be queried by {@link
-     * android.hardware.camera2.CameraCharacteristics#getPhysicalCameraIds}.
-     * </p>
+     * CameraCharacteristics#getPhysicalCameraIds}.</p>
      *
      * <p>Passing in a null physicalCameraId means that the OutputConfiguration is for a logical
      * stream.</p>
@@ -380,8 +469,16 @@
      * after {@link CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
      * CameraDevice#createReprocessableCaptureSessionByConfigurations} has no effect.</p>
      *
-     * <p>The surface belonging to a physical camera OutputConfiguration must not be used as input
-     * or output of a reprocessing request. </p>
+     * <p>As of {@link android.os.Build.VERSION_CODES#S Android 12}, an image buffer from a
+     * physical camera stream can be used for reprocessing to logical camera streams and streams
+     * from the same physical camera if the camera device supports multi-resolution input and output
+     * streams. See {@link CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}
+     * for details. The behaviors of reprocessing from a non-physical camera stream to a physical
+     * camera stream, and from a physical camera stream to a physical camera stream of different
+     * physical camera, are device-specific and not guaranteed to be supported.</p>
+     *
+     * <p>On prior API levels, the surface belonging to a physical camera OutputConfiguration must
+     * not be used as input or output of a reprocessing request. </p>
      */
     public void setPhysicalCameraId(@Nullable String physicalCameraId) {
         mPhysicalCameraId = physicalCameraId;
@@ -527,6 +624,7 @@
         this.mIsDeferredConfig = other.mIsDeferredConfig;
         this.mIsShared = other.mIsShared;
         this.mPhysicalCameraId = other.mPhysicalCameraId;
+        this.mIsMultiResolution = other.mIsMultiResolution;
     }
 
     /**
@@ -543,6 +641,7 @@
         ArrayList<Surface> surfaces = new ArrayList<Surface>();
         source.readTypedList(surfaces, Surface.CREATOR);
         String physicalCameraId = source.readString();
+        boolean isMultiResolutionOutput = source.readInt() == 1;
 
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
 
@@ -566,6 +665,7 @@
             mConfiguredGenerationId = 0;
         }
         mPhysicalCameraId = physicalCameraId;
+        mIsMultiResolution = isMultiResolutionOutput;
     }
 
     /**
@@ -665,6 +765,7 @@
         dest.writeInt(mIsShared ? 1 : 0);
         dest.writeTypedList(mSurfaces);
         dest.writeString(mPhysicalCameraId);
+        dest.writeInt(mIsMultiResolution ? 1 : 0);
     }
 
     /**
@@ -694,7 +795,8 @@
                     mConfiguredFormat != other.mConfiguredFormat ||
                     mConfiguredDataspace != other.mConfiguredDataspace ||
                     mConfiguredGenerationId != other.mConfiguredGenerationId ||
-                    !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId))
+                    !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
+                    mIsMultiResolution != other.mIsMultiResolution)
                 return false;
 
             int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
@@ -720,17 +822,24 @@
             return HashCodeHelpers.hashCode(
                     mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
                     mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
-                    mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
+                    mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
+                    mIsMultiResolution ? 1 : 0);
         }
 
         return HashCodeHelpers.hashCode(
                 mRotation, mSurfaces.hashCode(), mConfiguredGenerationId,
                 mConfiguredSize.hashCode(), mConfiguredFormat,
                 mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
-                mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
+                mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
+                mIsMultiResolution ? 1 : 0);
     }
 
     private static final String TAG = "OutputConfiguration";
+
+    // A surfaceGroupId counter used for MultiResolutionImageReader. Its value is
+    // incremented everytime {@link createInstancesForMultiResolutionOutput} is called.
+    private static int MULTI_RESOLUTION_GROUP_ID_COUNTER = 0;
+
     private ArrayList<Surface> mSurfaces;
     private final int mRotation;
     private final int mSurfaceGroupId;
@@ -749,4 +858,7 @@
     private boolean mIsShared;
     // The physical camera id that this output configuration is for.
     private String mPhysicalCameraId;
+    // Flag indicating if this config is for a multi-resolution output with a
+    // MultiResolutionImageReader
+    private boolean mIsMultiResolution;
 }
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 8fc919f..ea6b92d 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -130,11 +130,13 @@
         int inputWidth = source.readInt();
         int inputHeight = source.readInt();
         int inputFormat = source.readInt();
+        boolean isInputMultiResolution = source.readBoolean();
         ArrayList<OutputConfiguration> outConfigs = new ArrayList<OutputConfiguration>();
         source.readTypedList(outConfigs, OutputConfiguration.CREATOR);
 
         if ((inputWidth > 0) && (inputHeight > 0) && (inputFormat != -1)) {
-            mInputConfig = new InputConfiguration(inputWidth, inputHeight, inputFormat);
+            mInputConfig = new InputConfiguration(inputWidth, inputHeight,
+                    inputFormat, isInputMultiResolution);
         }
         mSessionType = sessionType;
         mOutputConfigurations = outConfigs;
@@ -169,10 +171,12 @@
             dest.writeInt(mInputConfig.getWidth());
             dest.writeInt(mInputConfig.getHeight());
             dest.writeInt(mInputConfig.getFormat());
+            dest.writeBoolean(mInputConfig.isMultiResolution());
         } else {
             dest.writeInt(/*inputWidth*/ 0);
             dest.writeInt(/*inputHeight*/ 0);
             dest.writeInt(/*inputFormat*/ -1);
+            dest.writeBoolean(/*isMultiResolution*/ false);
         }
         dest.writeTypedList(mOutputConfigurations);
     }
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 10a814a..a25ae60 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1575,7 +1575,7 @@
         return sizes;
     }
 
-    /** Get the list of publically visible output formats; does not include IMPL_DEFINED */
+    /** Get the list of publicly visible output formats */
     private int[] getPublicFormats(boolean output) {
         int[] formats = new int[getPublicFormatCount(output)];
 
@@ -1746,6 +1746,21 @@
         return sb.toString();
     }
 
+    /**
+     * Size comparison method used by size comparators.
+     *
+     * @hide
+     */
+    public static int compareSizes(int widthA, int heightA, int widthB, int heightB) {
+        long left = widthA * (long) heightA;
+        long right = widthB * (long) heightB;
+        if (left == right) {
+            left = widthA;
+            right = widthB;
+        }
+        return (left < right) ? -1 : (left > right ? 1 : 0);
+    }
+
     private void appendOutputsString(StringBuilder sb) {
         sb.append("Outputs(");
         int[] formats = getOutputFormats();
@@ -1843,7 +1858,10 @@
         sb.append(")");
     }
 
-    private String formatToString(int format) {
+    /**
+     * @hide
+     */
+    public static String formatToString(int format) {
         switch (format) {
             case ImageFormat.YV12:
                 return "YV12";
diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java
index 2626a46..6fb0eb5 100644
--- a/core/java/android/hardware/lights/LightsRequest.java
+++ b/core/java/android/hardware/lights/LightsRequest.java
@@ -17,6 +17,7 @@
 package android.hardware.lights;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.util.SparseArray;
 
 import com.android.internal.util.Preconditions;
@@ -94,6 +95,20 @@
         }
 
         /**
+         * Overrides the color and intensity of a given light.
+         *
+         * @param light the light to modify
+         * @param state the desired color and intensity of the light         *
+         * @deprecated Use {@link #addLight(Light, LightState)} instead.
+         * @hide
+         */
+        @SystemApi
+        @Deprecated
+        public @NonNull Builder setLight(@NonNull Light light, @NonNull LightState state) {
+            return addLight(light, state);
+        }
+
+        /**
          * Removes the override for the color and intensity of a given light.
          *
          * @param light the light to modify
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index b016ed6..9bf791b 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -19,8 +19,6 @@
 import android.net.INetworkPolicyListener;
 import android.net.Network;
 import android.net.NetworkPolicy;
-import android.net.NetworkQuotaInfo;
-import android.net.NetworkState;
 import android.net.NetworkTemplate;
 import android.telephony.SubscriptionPlan;
 
@@ -70,9 +68,6 @@
 
     int getMultipathPreference(in Network network);
 
-    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
-    NetworkQuotaInfo getNetworkQuotaInfo(in NetworkState state);
-
     SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
     void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
     String getSubscriptionPlansOwner(int subId);
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 268002f..8f1e2de 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -232,10 +232,11 @@
         ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, SDK_VERSION_ZERO);
         ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, SDK_VERSION_ZERO);
 
-        ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.S);
-        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.S);
-        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.S);
-        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.S);
+        // STOPSHIP: b/170424293 Use Build.VERSION_CODES.S when it is defined
+        ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.R + 1);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
     }
 
     private static final Set<String> ENABLED_ALGOS =
diff --git a/core/java/android/net/NetworkStateSnapshot.java b/core/java/android/net/NetworkStateSnapshot.java
index b3d8d4e..0d26c2d 100644
--- a/core/java/android/net/NetworkStateSnapshot.java
+++ b/core/java/android/net/NetworkStateSnapshot.java
@@ -24,6 +24,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.net.module.util.NetworkIdentityUtils;
+
 import java.util.Objects;
 
 /**
@@ -124,4 +126,15 @@
     public int hashCode() {
         return Objects.hash(network, networkCapabilities, linkProperties, subscriberId, legacyType);
     }
+
+    @Override
+    public String toString() {
+        return "NetworkStateSnapshot{"
+                + "network=" + network
+                + ", networkCapabilities=" + networkCapabilities
+                + ", linkProperties=" + linkProperties
+                + ", subscriberId='" + NetworkIdentityUtils.scrubSubscriberId(subscriberId) + '\''
+                + ", legacyType=" + legacyType
+                + '}';
+    }
 }
diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl
index d91cef5..236ae8b 100644
--- a/core/java/android/net/vcn/IVcnStatusCallback.aidl
+++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl
@@ -18,7 +18,6 @@
 
 /** @hide */
 oneway interface IVcnStatusCallback {
-    void onEnteredSafeMode();
     void onVcnStatusChanged(int statusCode);
     void onGatewayConnectionError(
             in int[] gatewayNetworkCapabilities,
diff --git a/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
index f8ae492..62de821 100644
--- a/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
+++ b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
@@ -17,6 +17,6 @@
 package android.net.vcn;
 
 /** @hide */
-interface IVcnUnderlyingNetworkPolicyListener {
+oneway interface IVcnUnderlyingNetworkPolicyListener {
     void onPolicyChanged();
 }
\ No newline at end of file
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index eb8c251..8ebf757 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -359,8 +359,6 @@
     /**
      * 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;
 
@@ -369,8 +367,6 @@
      *
      * <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;
 
@@ -380,8 +376,6 @@
      * <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;
 
@@ -391,8 +385,6 @@
      * <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;
 
@@ -407,8 +399,6 @@
 
     /**
      * Value indicating that an internal failure occurred in this Gateway Connection.
-     *
-     * @hide
      */
     public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0;
 
@@ -416,8 +406,6 @@
      * Value indicating that an error with this Gateway Connection's configuration occurred.
      *
      * <p>For example, this error code will be returned after authentication failures.
-     *
-     * @hide
      */
     public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1;
 
@@ -427,38 +415,19 @@
      * <p>For example, this error code will be returned if an underlying {@link android.net.Network}
      * for this Gateway Connection is lost, or if an error occurs while resolving the connection
      * endpoint address.
-     *
-     * @hide
      */
     public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2;
 
-    // TODO: make VcnStatusCallback @SystemApi
     /**
      * VcnStatusCallback is the interface for Carrier apps to receive updates for their VCNs.
      *
      * <p>VcnStatusCallbacks may be registered before {@link VcnConfig}s are provided for a
      * subscription group.
-     *
-     * @hide
      */
     public abstract static class VcnStatusCallback {
         private VcnStatusCallbackBinder mCbBinder;
 
         /**
-         * Invoked when the VCN for this Callback's subscription group enters 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).
-         *
-         * <p>A VCN-configuring app may opt to exit safe mode by (re)setting the VCN configuration
-         * via {@link #setVcnConfig(ParcelUuid, VcnConfig)}.
-         *
-         * @hide
-         */
-        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
@@ -467,15 +436,16 @@
         public abstract void onVcnStatusChanged(@VcnStatusCode int statusCode);
 
         /**
-         * Invoked when a VCN Gateway Connection corresponding to this callback's subscription
+         * Invoked when a VCN Gateway Connection corresponding to this callback's subscription group
          * encounters an error.
          *
-         * @param networkCapabilities an array of underlying NetworkCapabilities for the Gateway
-         *     Connection that encountered the error for identification purposes. These will be a
-         *     sorted list with no duplicates, matching one of the {@link
+         * @param networkCapabilities an array of NetworkCapabilities.NET_CAPABILITY_* capabilities
+         *     for the Gateway Connection that encountered the error, for identification purposes.
+         *     These will be a sorted list with no duplicates and will match {@link
+         *     VcnGatewayConnectionConfig#getRequiredUnderlyingCapabilities()} for one of the {@link
          *     VcnGatewayConnectionConfig}s set in the {@link VcnConfig} for this subscription
          *     group.
-         * @param errorCode {@link VcnErrorCode} to indicate the error that occurred
+         * @param errorCode the code to indicate the error that occurred
          * @param detail Throwable to provide additional information about the error, or {@code
          *     null} if none
          */
@@ -496,6 +466,10 @@
      * <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>A {@link VcnStatusCallback} is eligible to begin receiving callbacks once it is registered
+     * and there is a VCN active for its specified subscription group (this may happen after the
+     * callback is registered).
+     *
      * <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
@@ -505,7 +479,6 @@
      * @param executor The {@link Executor} to be used for invoking callbacks
      * @param callback The VcnStatusCallback to be registered
      * @throws IllegalStateException if callback is currently registered with VcnManager
-     * @hide
      */
     public void registerVcnStatusCallback(
             @NonNull ParcelUuid subscriptionGroup,
@@ -538,7 +511,6 @@
      * was registered with.
      *
      * @param callback The callback to be unregistered
-     * @hide
      */
     public void unregisterVcnStatusCallback(@NonNull VcnStatusCallback callback) {
         requireNonNull(callback, "callback must not be null");
@@ -599,12 +571,6 @@
         }
 
         @Override
-        public void onEnteredSafeMode() {
-            Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(() -> mCallback.onEnteredSafeMode()));
-        }
-
-        @Override
         public void onVcnStatusChanged(@VcnStatusCode int statusCode) {
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> mCallback.onVcnStatusChanged(statusCode)));
diff --git a/core/java/android/net/vcn/persistablebundleutils/CertUtils.java b/core/java/android/net/vcn/persistablebundleutils/CertUtils.java
new file mode 100644
index 0000000..b6036b4
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/CertUtils.java
@@ -0,0 +1,46 @@
+/*
+ * 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.net.vcn.persistablebundleutils;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Objects;
+
+/**
+ * CertUtils provides utility methods for constructing Certificate.
+ *
+ * @hide
+ */
+public class CertUtils {
+    private static final String CERT_TYPE_X509 = "X.509";
+
+    /** Decodes an ASN.1 DER encoded Certificate */
+    public static X509Certificate certificateFromByteArray(byte[] derEncoded) {
+        Objects.requireNonNull(derEncoded, "derEncoded is null");
+
+        try {
+            CertificateFactory certFactory = CertificateFactory.getInstance(CERT_TYPE_X509);
+            InputStream in = new ByteArrayInputStream(derEncoded);
+            return (X509Certificate) certFactory.generateCertificate(in);
+        } catch (CertificateException e) {
+            throw new IllegalArgumentException("Fail to decode certificate", e);
+        }
+    }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java b/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
new file mode 100644
index 0000000..ce5ec75
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
@@ -0,0 +1,73 @@
+/*
+ * 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.net.vcn.persistablebundleutils;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert ChildSaProposal to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class ChildSaProposalUtils extends SaProposalUtilsBase {
+    /** Serializes a ChildSaProposal to a PersistableBundle. */
+    @NonNull
+    public static PersistableBundle toPersistableBundle(ChildSaProposal proposal) {
+        return SaProposalUtilsBase.toPersistableBundle(proposal);
+    }
+
+    /** Constructs a ChildSaProposal by deserializing a PersistableBundle. */
+    @NonNull
+    public static ChildSaProposal fromPersistableBundle(@NonNull PersistableBundle in) {
+        Objects.requireNonNull(in, "PersistableBundle was null");
+
+        final ChildSaProposal.Builder builder = new ChildSaProposal.Builder();
+
+        final PersistableBundle encryptionBundle = in.getPersistableBundle(ENCRYPT_ALGO_KEY);
+        Objects.requireNonNull(encryptionBundle, "Encryption algo bundle was null");
+        final List<EncryptionAlgoKeyLenPair> encryptList =
+                PersistableBundleUtils.toList(encryptionBundle, EncryptionAlgoKeyLenPair::new);
+        for (EncryptionAlgoKeyLenPair t : encryptList) {
+            builder.addEncryptionAlgorithm(t.encryptionAlgo, t.keyLen);
+        }
+
+        final int[] integrityAlgoIdArray = in.getIntArray(INTEGRITY_ALGO_KEY);
+        Objects.requireNonNull(integrityAlgoIdArray, "Integrity algo array was null");
+        for (int algo : integrityAlgoIdArray) {
+            builder.addIntegrityAlgorithm(algo);
+        }
+
+        final int[] dhGroupArray = in.getIntArray(DH_GROUP_KEY);
+        Objects.requireNonNull(dhGroupArray, "DH Group array was null");
+        for (int dh : dhGroupArray) {
+            builder.addDhGroup(dh);
+        }
+
+        return builder.build();
+    }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
new file mode 100644
index 0000000..853a52d
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
@@ -0,0 +1,276 @@
+/*
+ * 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.net.vcn.persistablebundleutils;
+
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.eap.EapSessionConfig;
+import android.net.eap.EapSessionConfig.EapAkaConfig;
+import android.net.eap.EapSessionConfig.EapAkaPrimeConfig;
+import android.net.eap.EapSessionConfig.EapMethodConfig;
+import android.net.eap.EapSessionConfig.EapMsChapV2Config;
+import android.net.eap.EapSessionConfig.EapSimConfig;
+import android.net.eap.EapSessionConfig.EapTtlsConfig;
+import android.net.eap.EapSessionConfig.EapUiccConfig;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert EapSessionConfig to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class EapSessionConfigUtils {
+    private static final String EAP_ID_KEY = "EAP_ID_KEY";
+    private static final String EAP_SIM_CONFIG_KEY = "EAP_SIM_CONFIG_KEY";
+    private static final String EAP_TTLS_CONFIG_KEY = "EAP_TTLS_CONFIG_KEY";
+    private static final String EAP_AKA_CONFIG_KEY = "EAP_AKA_CONFIG_KEY";
+    private static final String EAP_MSCHAP_V2_CONFIG_KEY = "EAP_MSCHAP_V2_CONFIG_KEY";
+    private static final String EAP_AKA_PRIME_CONFIG_KEY = "EAP_AKA_PRIME_CONFIG_KEY";
+
+    /** Serializes an EapSessionConfig to a PersistableBundle. */
+    @NonNull
+    public static PersistableBundle toPersistableBundle(@NonNull EapSessionConfig config) {
+        final PersistableBundle result = new PersistableBundle();
+
+        result.putPersistableBundle(
+                EAP_ID_KEY, PersistableBundleUtils.fromByteArray(config.getEapIdentity()));
+
+        if (config.getEapSimConfig() != null) {
+            result.putPersistableBundle(
+                    EAP_SIM_CONFIG_KEY,
+                    EapSimConfigUtils.toPersistableBundle(config.getEapSimConfig()));
+        }
+
+        if (config.getEapTtlsConfig() != null) {
+            result.putPersistableBundle(
+                    EAP_TTLS_CONFIG_KEY,
+                    EapTtlsConfigUtils.toPersistableBundle(config.getEapTtlsConfig()));
+        }
+
+        if (config.getEapAkaConfig() != null) {
+            result.putPersistableBundle(
+                    EAP_AKA_CONFIG_KEY,
+                    EapAkaConfigUtils.toPersistableBundle(config.getEapAkaConfig()));
+        }
+
+        if (config.getEapMsChapV2Config() != null) {
+            result.putPersistableBundle(
+                    EAP_MSCHAP_V2_CONFIG_KEY,
+                    EapMsChapV2ConfigUtils.toPersistableBundle(config.getEapMsChapV2Config()));
+        }
+
+        if (config.getEapAkaPrimeConfig() != null) {
+            result.putPersistableBundle(
+                    EAP_AKA_PRIME_CONFIG_KEY,
+                    EapAkaPrimeConfigUtils.toPersistableBundle(config.getEapAkaPrimeConfig()));
+        }
+
+        return result;
+    }
+
+    /** Constructs an EapSessionConfig by deserializing a PersistableBundle. */
+    @NonNull
+    public static EapSessionConfig fromPersistableBundle(@NonNull PersistableBundle in) {
+        Objects.requireNonNull(in, "PersistableBundle was null");
+
+        final EapSessionConfig.Builder builder = new EapSessionConfig.Builder();
+
+        final PersistableBundle eapIdBundle = in.getPersistableBundle(EAP_ID_KEY);
+        Objects.requireNonNull(eapIdBundle, "EAP ID was null");
+        builder.setEapIdentity(PersistableBundleUtils.toByteArray(eapIdBundle));
+
+        final PersistableBundle simBundle = in.getPersistableBundle(EAP_SIM_CONFIG_KEY);
+        if (simBundle != null) {
+            EapSimConfigUtils.setBuilderByReadingPersistableBundle(simBundle, builder);
+        }
+
+        final PersistableBundle ttlsBundle = in.getPersistableBundle(EAP_TTLS_CONFIG_KEY);
+        if (ttlsBundle != null) {
+            EapTtlsConfigUtils.setBuilderByReadingPersistableBundle(ttlsBundle, builder);
+        }
+
+        final PersistableBundle akaBundle = in.getPersistableBundle(EAP_AKA_CONFIG_KEY);
+        if (akaBundle != null) {
+            EapAkaConfigUtils.setBuilderByReadingPersistableBundle(akaBundle, builder);
+        }
+
+        final PersistableBundle msChapV2Bundle = in.getPersistableBundle(EAP_MSCHAP_V2_CONFIG_KEY);
+        if (msChapV2Bundle != null) {
+            EapMsChapV2ConfigUtils.setBuilderByReadingPersistableBundle(msChapV2Bundle, builder);
+        }
+
+        final PersistableBundle akaPrimeBundle = in.getPersistableBundle(EAP_AKA_PRIME_CONFIG_KEY);
+        if (akaPrimeBundle != null) {
+            EapAkaPrimeConfigUtils.setBuilderByReadingPersistableBundle(akaPrimeBundle, builder);
+        }
+
+        return builder.build();
+    }
+
+    private static class EapMethodConfigUtils {
+        private static final String METHOD_TYPE = "METHOD_TYPE";
+
+        /** Serializes an EapMethodConfig to a PersistableBundle. */
+        @NonNull
+        public static PersistableBundle toPersistableBundle(@NonNull EapMethodConfig config) {
+            final PersistableBundle result = new PersistableBundle();
+            result.putInt(METHOD_TYPE, config.getMethodType());
+            return result;
+        }
+    }
+
+    private static class EapUiccConfigUtils extends EapMethodConfigUtils {
+        static final String SUB_ID_KEY = "SUB_ID_KEY";
+        static final String APP_TYPE_KEY = "APP_TYPE_KEY";
+
+        @NonNull
+        protected static PersistableBundle toPersistableBundle(@NonNull EapUiccConfig config) {
+            final PersistableBundle result = EapMethodConfigUtils.toPersistableBundle(config);
+            result.putInt(SUB_ID_KEY, config.getSubId());
+            result.putInt(APP_TYPE_KEY, config.getAppType());
+
+            return result;
+        }
+    }
+
+    private static final class EapSimConfigUtils extends EapUiccConfigUtils {
+        @NonNull
+        public static PersistableBundle toPersistableBundle(EapSimConfig config) {
+            return EapUiccConfigUtils.toPersistableBundle(config);
+        }
+
+        public static void setBuilderByReadingPersistableBundle(
+                @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+            Objects.requireNonNull(in, "PersistableBundle was null");
+            builder.setEapSimConfig(in.getInt(SUB_ID_KEY), in.getInt(APP_TYPE_KEY));
+        }
+    }
+
+    private static class EapAkaConfigUtils extends EapUiccConfigUtils {
+        @NonNull
+        public static PersistableBundle toPersistableBundle(@NonNull EapAkaConfig config) {
+            return EapUiccConfigUtils.toPersistableBundle(config);
+        }
+
+        public static void setBuilderByReadingPersistableBundle(
+                @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+            Objects.requireNonNull(in, "PersistableBundle was null");
+            builder.setEapAkaConfig(in.getInt(SUB_ID_KEY), in.getInt(APP_TYPE_KEY));
+        }
+    }
+
+    private static final class EapAkaPrimeConfigUtils extends EapAkaConfigUtils {
+        private static final String NETWORK_NAME_KEY = "NETWORK_NAME_KEY";
+        private static final String ALL_MISMATCHED_NETWORK_KEY = "ALL_MISMATCHED_NETWORK_KEY";
+
+        @NonNull
+        public static PersistableBundle toPersistableBundle(@NonNull EapAkaPrimeConfig config) {
+            final PersistableBundle result = EapUiccConfigUtils.toPersistableBundle(config);
+            result.putString(NETWORK_NAME_KEY, config.getNetworkName());
+            result.putBoolean(ALL_MISMATCHED_NETWORK_KEY, config.allowsMismatchedNetworkNames());
+
+            return result;
+        }
+
+        public static void setBuilderByReadingPersistableBundle(
+                @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+            Objects.requireNonNull(in, "PersistableBundle was null");
+            builder.setEapAkaPrimeConfig(
+                    in.getInt(SUB_ID_KEY),
+                    in.getInt(APP_TYPE_KEY),
+                    in.getString(NETWORK_NAME_KEY),
+                    in.getBoolean(ALL_MISMATCHED_NETWORK_KEY));
+        }
+    }
+
+    private static final class EapMsChapV2ConfigUtils extends EapMethodConfigUtils {
+        private static final String USERNAME_KEY = "USERNAME_KEY";
+        private static final String PASSWORD_KEY = "PASSWORD_KEY";
+
+        @NonNull
+        public static PersistableBundle toPersistableBundle(@NonNull EapMsChapV2Config config) {
+            final PersistableBundle result = EapMethodConfigUtils.toPersistableBundle(config);
+            result.putString(USERNAME_KEY, config.getUsername());
+            result.putString(PASSWORD_KEY, config.getPassword());
+
+            return result;
+        }
+
+        public static void setBuilderByReadingPersistableBundle(
+                @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+            Objects.requireNonNull(in, "PersistableBundle was null");
+            builder.setEapMsChapV2Config(in.getString(USERNAME_KEY), in.getString(PASSWORD_KEY));
+        }
+    }
+
+    private static final class EapTtlsConfigUtils extends EapMethodConfigUtils {
+        private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
+        private static final String EAP_SESSION_CONFIG_KEY = "EAP_SESSION_CONFIG_KEY";
+
+        @NonNull
+        public static PersistableBundle toPersistableBundle(@NonNull EapTtlsConfig config) {
+            final PersistableBundle result = EapMethodConfigUtils.toPersistableBundle(config);
+            try {
+                if (config.getServerCaCert() != null) {
+                    final PersistableBundle caBundle =
+                            PersistableBundleUtils.fromByteArray(
+                                    config.getServerCaCert().getEncoded());
+                    result.putPersistableBundle(TRUST_CERT_KEY, caBundle);
+                }
+            } catch (CertificateEncodingException e) {
+                throw new IllegalStateException("Fail to encode the certificate");
+            }
+
+            result.putPersistableBundle(
+                    EAP_SESSION_CONFIG_KEY,
+                    EapSessionConfigUtils.toPersistableBundle(config.getInnerEapSessionConfig()));
+
+            return result;
+        }
+
+        public static void setBuilderByReadingPersistableBundle(
+                @NonNull PersistableBundle in, @NonNull EapSessionConfig.Builder builder) {
+            Objects.requireNonNull(in, "PersistableBundle was null");
+
+            final PersistableBundle caBundle = in.getPersistableBundle(TRUST_CERT_KEY);
+            X509Certificate caCert = null;
+            if (caBundle != null) {
+                caCert =
+                        CertUtils.certificateFromByteArray(
+                                PersistableBundleUtils.toByteArray(caBundle));
+            }
+
+            final PersistableBundle eapSessionConfigBundle =
+                    in.getPersistableBundle(EAP_SESSION_CONFIG_KEY);
+            Objects.requireNonNull(eapSessionConfigBundle, "Inner EAP Session Config was null");
+            final EapSessionConfig eapSessionConfig =
+                    EapSessionConfigUtils.fromPersistableBundle(eapSessionConfigBundle);
+
+            builder.setEapTtlsConfig(caCert, eapSessionConfig);
+        }
+    }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
new file mode 100644
index 0000000..6acb34e
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
@@ -0,0 +1,143 @@
+/*
+ * 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.net.vcn.persistablebundleutils;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.InetAddresses;
+import android.net.ipsec.ike.IkeDerAsn1DnIdentification;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeRfc822AddrIdentification;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.util.Objects;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Abstract utility class to convert IkeIdentification to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class IkeIdentificationUtils {
+    private static final String ID_TYPE_KEY = "ID_TYPE_KEY";
+
+    private static final String DER_ASN1_DN_KEY = "DER_ASN1_DN_KEY";
+    private static final String FQDN_KEY = "FQDN_KEY";
+    private static final String KEY_ID_KEY = "KEY_ID_KEY";
+    private static final String IP4_ADDRESS_KEY = "IP4_ADDRESS_KEY";
+    private static final String IP6_ADDRESS_KEY = "IP6_ADDRESS_KEY";
+    private static final String RFC822_ADDRESS_KEY = "RFC822_ADDRESS_KEY";
+
+    private static final int ID_TYPE_DER_ASN1_DN = 1;
+    private static final int ID_TYPE_FQDN = 2;
+    private static final int ID_TYPE_IPV4_ADDR = 3;
+    private static final int ID_TYPE_IPV6_ADDR = 4;
+    private static final int ID_TYPE_KEY_ID = 5;
+    private static final int ID_TYPE_RFC822_ADDR = 6;
+
+    /** Serializes an IkeIdentification to a PersistableBundle. */
+    @NonNull
+    public static PersistableBundle toPersistableBundle(@NonNull IkeIdentification ikeId) {
+        if (ikeId instanceof IkeDerAsn1DnIdentification) {
+            final PersistableBundle result = createPersistableBundle(ID_TYPE_DER_ASN1_DN);
+            IkeDerAsn1DnIdentification id = (IkeDerAsn1DnIdentification) ikeId;
+            result.putPersistableBundle(
+                    DER_ASN1_DN_KEY,
+                    PersistableBundleUtils.fromByteArray(id.derAsn1Dn.getEncoded()));
+            return result;
+        } else if (ikeId instanceof IkeFqdnIdentification) {
+            final PersistableBundle result = createPersistableBundle(ID_TYPE_FQDN);
+            IkeFqdnIdentification id = (IkeFqdnIdentification) ikeId;
+            result.putString(FQDN_KEY, id.fqdn);
+            return result;
+        } else if (ikeId instanceof IkeIpv4AddrIdentification) {
+            final PersistableBundle result = createPersistableBundle(ID_TYPE_IPV4_ADDR);
+            IkeIpv4AddrIdentification id = (IkeIpv4AddrIdentification) ikeId;
+            result.putString(IP4_ADDRESS_KEY, id.ipv4Address.getHostAddress());
+            return result;
+        } else if (ikeId instanceof IkeIpv6AddrIdentification) {
+            final PersistableBundle result = createPersistableBundle(ID_TYPE_IPV6_ADDR);
+            IkeIpv6AddrIdentification id = (IkeIpv6AddrIdentification) ikeId;
+            result.putString(IP6_ADDRESS_KEY, id.ipv6Address.getHostAddress());
+            return result;
+        } else if (ikeId instanceof IkeKeyIdIdentification) {
+            final PersistableBundle result = createPersistableBundle(ID_TYPE_KEY_ID);
+            IkeKeyIdIdentification id = (IkeKeyIdIdentification) ikeId;
+            result.putPersistableBundle(KEY_ID_KEY, PersistableBundleUtils.fromByteArray(id.keyId));
+            return result;
+        } else if (ikeId instanceof IkeRfc822AddrIdentification) {
+            final PersistableBundle result = createPersistableBundle(ID_TYPE_RFC822_ADDR);
+            IkeRfc822AddrIdentification id = (IkeRfc822AddrIdentification) ikeId;
+            result.putString(RFC822_ADDRESS_KEY, id.rfc822Name);
+            return result;
+        } else {
+            throw new IllegalStateException("Unrecognized IkeIdentification subclass");
+        }
+    }
+
+    private static PersistableBundle createPersistableBundle(int idType) {
+        final PersistableBundle result = new PersistableBundle();
+        result.putInt(ID_TYPE_KEY, idType);
+        return result;
+    }
+
+    /** Constructs an IkeIdentification by deserializing a PersistableBundle. */
+    @NonNull
+    public static IkeIdentification fromPersistableBundle(@NonNull PersistableBundle in) {
+        Objects.requireNonNull(in, "PersistableBundle was null");
+        int idType = in.getInt(ID_TYPE_KEY);
+        switch (idType) {
+            case ID_TYPE_DER_ASN1_DN:
+                final PersistableBundle dnBundle = in.getPersistableBundle(DER_ASN1_DN_KEY);
+                Objects.requireNonNull(dnBundle, "ASN1 DN was null");
+                return new IkeDerAsn1DnIdentification(
+                        new X500Principal(PersistableBundleUtils.toByteArray(dnBundle)));
+            case ID_TYPE_FQDN:
+                return new IkeFqdnIdentification(in.getString(FQDN_KEY));
+            case ID_TYPE_IPV4_ADDR:
+                final String v4AddressStr = in.getString(IP4_ADDRESS_KEY);
+                Objects.requireNonNull(v4AddressStr, "IPv4 address was null");
+                return new IkeIpv4AddrIdentification(
+                        (Inet4Address) InetAddresses.parseNumericAddress(v4AddressStr));
+            case ID_TYPE_IPV6_ADDR:
+                final String v6AddressStr = in.getString(IP6_ADDRESS_KEY);
+                Objects.requireNonNull(v6AddressStr, "IPv6 address was null");
+                return new IkeIpv6AddrIdentification(
+                        (Inet6Address) InetAddresses.parseNumericAddress(v6AddressStr));
+            case ID_TYPE_KEY_ID:
+                final PersistableBundle keyIdBundle = in.getPersistableBundle(KEY_ID_KEY);
+                Objects.requireNonNull(in, "Key ID was null");
+                return new IkeKeyIdIdentification(PersistableBundleUtils.toByteArray(keyIdBundle));
+            case ID_TYPE_RFC822_ADDR:
+                return new IkeRfc822AddrIdentification(in.getString(RFC822_ADDRESS_KEY));
+            default:
+                throw new IllegalStateException("Unrecognized IKE ID type: " + idType);
+        }
+    }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
new file mode 100644
index 0000000..1459671
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
@@ -0,0 +1,87 @@
+/*
+ * 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.net.vcn.persistablebundleutils;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert IkeSaProposal to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class IkeSaProposalUtils extends SaProposalUtilsBase {
+    private static final String PRF_KEY = "PRF_KEY";
+
+    /** Serializes an IkeSaProposal to a PersistableBundle. */
+    @NonNull
+    public static PersistableBundle toPersistableBundle(IkeSaProposal proposal) {
+        final PersistableBundle result = SaProposalUtilsBase.toPersistableBundle(proposal);
+
+        final int[] prfArray =
+                proposal.getPseudorandomFunctions().stream().mapToInt(i -> i).toArray();
+        result.putIntArray(PRF_KEY, prfArray);
+
+        return result;
+    }
+
+    /** Constructs an IkeSaProposal by deserializing a PersistableBundle. */
+    @NonNull
+    public static IkeSaProposal fromPersistableBundle(@NonNull PersistableBundle in) {
+        Objects.requireNonNull(in, "PersistableBundle was null");
+
+        final IkeSaProposal.Builder builder = new IkeSaProposal.Builder();
+
+        final PersistableBundle encryptionBundle = in.getPersistableBundle(ENCRYPT_ALGO_KEY);
+        Objects.requireNonNull(encryptionBundle, "Encryption algo bundle was null");
+        final List<EncryptionAlgoKeyLenPair> encryptList =
+                PersistableBundleUtils.toList(encryptionBundle, EncryptionAlgoKeyLenPair::new);
+        for (EncryptionAlgoKeyLenPair t : encryptList) {
+            builder.addEncryptionAlgorithm(t.encryptionAlgo, t.keyLen);
+        }
+
+        final int[] integrityAlgoIdArray = in.getIntArray(INTEGRITY_ALGO_KEY);
+        Objects.requireNonNull(integrityAlgoIdArray, "Integrity algo array was null");
+        for (int algo : integrityAlgoIdArray) {
+            builder.addIntegrityAlgorithm(algo);
+        }
+
+        final int[] dhGroupArray = in.getIntArray(DH_GROUP_KEY);
+        Objects.requireNonNull(dhGroupArray, "DH Group array was null");
+        for (int dh : dhGroupArray) {
+            builder.addDhGroup(dh);
+        }
+
+        final int[] prfArray = in.getIntArray(PRF_KEY);
+        Objects.requireNonNull(prfArray, "PRF array was null");
+        for (int prf : prfArray) {
+            builder.addPseudorandomFunction(prf);
+        }
+
+        return builder.build();
+    }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java b/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
new file mode 100644
index 0000000..0c9ee84
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
@@ -0,0 +1,96 @@
+/*
+ * 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.net.vcn.persistablebundleutils;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.SaProposal;
+import android.os.PersistableBundle;
+import android.util.Pair;
+
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Abstract utility class to convert SaProposal to/from PersistableBundle.
+ *
+ * @hide
+ */
+abstract class SaProposalUtilsBase {
+    static final String ENCRYPT_ALGO_KEY = "ENCRYPT_ALGO_KEY";
+    static final String INTEGRITY_ALGO_KEY = "INTEGRITY_ALGO_KEY";
+    static final String DH_GROUP_KEY = "DH_GROUP_KEY";
+
+    static class EncryptionAlgoKeyLenPair {
+        private static final String ALGO_KEY = "ALGO_KEY";
+        private static final String KEY_LEN_KEY = "KEY_LEN_KEY";
+
+        public final int encryptionAlgo;
+        public final int keyLen;
+
+        EncryptionAlgoKeyLenPair(int encryptionAlgo, int keyLen) {
+            this.encryptionAlgo = encryptionAlgo;
+            this.keyLen = keyLen;
+        }
+
+        EncryptionAlgoKeyLenPair(PersistableBundle in) {
+            Objects.requireNonNull(in, "PersistableBundle was null");
+
+            this.encryptionAlgo = in.getInt(ALGO_KEY);
+            this.keyLen = in.getInt(KEY_LEN_KEY);
+        }
+
+        public PersistableBundle toPersistableBundle() {
+            final PersistableBundle result = new PersistableBundle();
+
+            result.putInt(ALGO_KEY, encryptionAlgo);
+            result.putInt(KEY_LEN_KEY, keyLen);
+
+            return result;
+        }
+    }
+
+    /**
+     * Serializes common info of a SaProposal to a PersistableBundle.
+     *
+     * @hide
+     */
+    @NonNull
+    static PersistableBundle toPersistableBundle(SaProposal proposal) {
+        final PersistableBundle result = new PersistableBundle();
+
+        final List<EncryptionAlgoKeyLenPair> encryptAlgoKeyLenPairs = new ArrayList<>();
+        for (Pair<Integer, Integer> pair : proposal.getEncryptionAlgorithms()) {
+            encryptAlgoKeyLenPairs.add(new EncryptionAlgoKeyLenPair(pair.first, pair.second));
+        }
+        final PersistableBundle encryptionBundle =
+                PersistableBundleUtils.fromList(
+                        encryptAlgoKeyLenPairs, EncryptionAlgoKeyLenPair::toPersistableBundle);
+        result.putPersistableBundle(ENCRYPT_ALGO_KEY, encryptionBundle);
+
+        final int[] integrityAlgoIdArray =
+                proposal.getIntegrityAlgorithms().stream().mapToInt(i -> i).toArray();
+        result.putIntArray(INTEGRITY_ALGO_KEY, integrityAlgoIdArray);
+
+        final int[] dhGroupArray = proposal.getDhGroups().stream().mapToInt(i -> i).toArray();
+        result.putIntArray(DH_GROUP_KEY, dhGroupArray);
+
+        return result;
+    }
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
new file mode 100644
index 0000000..e62acac
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
@@ -0,0 +1,285 @@
+/*
+ * 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.net.vcn.persistablebundleutils;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.InetAddresses;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4Address;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv4Netmask;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6Address;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer;
+import android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest;
+import android.os.PersistableBundle;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert TunnelModeChildSessionParams to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class TunnelModeChildSessionParamsUtils {
+    private static final String TAG = TunnelModeChildSessionParamsUtils.class.getSimpleName();
+
+    private static final String INBOUND_TS_KEY = "INBOUND_TS_KEY";
+    private static final String OUTBOUND_TS_KEY = "OUTBOUND_TS_KEY";
+    private static final String SA_PROPOSALS_KEY = "SA_PROPOSALS_KEY";
+    private static final String HARD_LIFETIME_SEC_KEY = "HARD_LIFETIME_SEC_KEY";
+    private static final String SOFT_LIFETIME_SEC_KEY = "SOFT_LIFETIME_SEC_KEY";
+    private static final String CONFIG_REQUESTS_KEY = "CONFIG_REQUESTS_KEY";
+
+    private static class ConfigRequest {
+        private static final int TYPE_IPV4_ADDRESS = 1;
+        private static final int TYPE_IPV6_ADDRESS = 2;
+        private static final int TYPE_IPV4_DNS = 3;
+        private static final int TYPE_IPV6_DNS = 4;
+        private static final int TYPE_IPV4_DHCP = 5;
+        private static final int TYPE_IPV4_NETMASK = 6;
+
+        private static final String TYPE_KEY = "type";
+        private static final String VALUE_KEY = "address";
+        private static final String IP6_PREFIX_LEN = "ip6PrefixLen";
+
+        private static final int PREFIX_LEN_UNUSED = -1;
+
+        public final int type;
+        public final int ip6PrefixLen;
+
+        // Null when it is an empty request
+        @Nullable public final InetAddress address;
+
+        ConfigRequest(TunnelModeChildConfigRequest config) {
+            int prefixLen = PREFIX_LEN_UNUSED;
+
+            if (config instanceof ConfigRequestIpv4Address) {
+                type = TYPE_IPV4_ADDRESS;
+                address = ((ConfigRequestIpv4Address) config).getAddress();
+            } else if (config instanceof ConfigRequestIpv6Address) {
+                type = TYPE_IPV6_ADDRESS;
+                address = ((ConfigRequestIpv6Address) config).getAddress();
+                if (address != null) {
+                    prefixLen = ((ConfigRequestIpv6Address) config).getPrefixLength();
+                }
+            } else if (config instanceof ConfigRequestIpv4DnsServer) {
+                type = TYPE_IPV4_DNS;
+                address = null;
+            } else if (config instanceof ConfigRequestIpv6DnsServer) {
+                type = TYPE_IPV6_DNS;
+                address = null;
+            } else if (config instanceof ConfigRequestIpv4DhcpServer) {
+                type = TYPE_IPV4_DHCP;
+                address = null;
+            } else if (config instanceof ConfigRequestIpv4Netmask) {
+                type = TYPE_IPV4_NETMASK;
+                address = null;
+            } else {
+                throw new IllegalStateException("Unknown TunnelModeChildConfigRequest");
+            }
+
+            ip6PrefixLen = prefixLen;
+        }
+
+        ConfigRequest(PersistableBundle in) {
+            Objects.requireNonNull(in, "PersistableBundle was null");
+
+            type = in.getInt(TYPE_KEY);
+            ip6PrefixLen = in.getInt(IP6_PREFIX_LEN);
+
+            String addressStr = in.getString(VALUE_KEY);
+            if (addressStr == null) {
+                address = null;
+            } else {
+                address = InetAddresses.parseNumericAddress(addressStr);
+            }
+        }
+
+        @NonNull
+        public PersistableBundle toPersistableBundle() {
+            final PersistableBundle result = new PersistableBundle();
+
+            result.putInt(TYPE_KEY, type);
+            result.putInt(IP6_PREFIX_LEN, ip6PrefixLen);
+
+            if (address != null) {
+                result.putString(VALUE_KEY, address.getHostAddress());
+            }
+
+            return result;
+        }
+    }
+
+    /** Serializes a TunnelModeChildSessionParams to a PersistableBundle. */
+    @NonNull
+    public static PersistableBundle toPersistableBundle(
+            @NonNull TunnelModeChildSessionParams params) {
+        final PersistableBundle result = new PersistableBundle();
+
+        final PersistableBundle saProposalBundle =
+                PersistableBundleUtils.fromList(
+                        params.getSaProposals(), ChildSaProposalUtils::toPersistableBundle);
+        result.putPersistableBundle(SA_PROPOSALS_KEY, saProposalBundle);
+
+        final PersistableBundle inTsBundle =
+                PersistableBundleUtils.fromList(
+                        params.getInboundTrafficSelectors(),
+                        IkeTrafficSelectorUtils::toPersistableBundle);
+        result.putPersistableBundle(INBOUND_TS_KEY, inTsBundle);
+
+        final PersistableBundle outTsBundle =
+                PersistableBundleUtils.fromList(
+                        params.getOutboundTrafficSelectors(),
+                        IkeTrafficSelectorUtils::toPersistableBundle);
+        result.putPersistableBundle(OUTBOUND_TS_KEY, outTsBundle);
+
+        result.putInt(HARD_LIFETIME_SEC_KEY, params.getHardLifetimeSeconds());
+        result.putInt(SOFT_LIFETIME_SEC_KEY, params.getSoftLifetimeSeconds());
+
+        final List<ConfigRequest> reqList = new ArrayList<>();
+        for (TunnelModeChildConfigRequest req : params.getConfigurationRequests()) {
+            reqList.add(new ConfigRequest(req));
+        }
+        final PersistableBundle configReqListBundle =
+                PersistableBundleUtils.fromList(reqList, ConfigRequest::toPersistableBundle);
+        result.putPersistableBundle(CONFIG_REQUESTS_KEY, configReqListBundle);
+
+        return result;
+    }
+
+    private static List<IkeTrafficSelector> getTsFromPersistableBundle(
+            PersistableBundle in, String key) {
+        PersistableBundle tsBundle = in.getPersistableBundle(key);
+        Objects.requireNonNull(tsBundle, "Value for key " + key + " was null");
+        return PersistableBundleUtils.toList(
+                tsBundle, IkeTrafficSelectorUtils::fromPersistableBundle);
+    }
+
+    /** Constructs a TunnelModeChildSessionParams by deserializing a PersistableBundle. */
+    @NonNull
+    public static TunnelModeChildSessionParams fromPersistableBundle(
+            @NonNull PersistableBundle in) {
+        Objects.requireNonNull(in, "PersistableBundle was null");
+
+        final TunnelModeChildSessionParams.Builder builder =
+                new TunnelModeChildSessionParams.Builder();
+
+        final PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY);
+        Objects.requireNonNull(proposalBundle, "SA proposal was null");
+        final List<ChildSaProposal> proposals =
+                PersistableBundleUtils.toList(
+                        proposalBundle, ChildSaProposalUtils::fromPersistableBundle);
+        for (ChildSaProposal p : proposals) {
+            builder.addSaProposal(p);
+        }
+
+        for (IkeTrafficSelector ts : getTsFromPersistableBundle(in, INBOUND_TS_KEY)) {
+            builder.addInboundTrafficSelectors(ts);
+        }
+
+        for (IkeTrafficSelector ts : getTsFromPersistableBundle(in, OUTBOUND_TS_KEY)) {
+            builder.addOutboundTrafficSelectors(ts);
+        }
+
+        builder.setLifetimeSeconds(
+                in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
+        final PersistableBundle configReqListBundle = in.getPersistableBundle(CONFIG_REQUESTS_KEY);
+        Objects.requireNonNull(configReqListBundle, "Config request list was null");
+        final List<ConfigRequest> reqList =
+                PersistableBundleUtils.toList(configReqListBundle, ConfigRequest::new);
+
+        boolean hasIpv4AddressReq = false;
+        boolean hasIpv4NetmaskReq = false;
+        for (ConfigRequest req : reqList) {
+            switch (req.type) {
+                case ConfigRequest.TYPE_IPV4_ADDRESS:
+                    hasIpv4AddressReq = true;
+                    if (req.address == null) {
+                        builder.addInternalAddressRequest(AF_INET);
+                    } else {
+                        builder.addInternalAddressRequest((Inet4Address) req.address);
+                    }
+                    break;
+                case ConfigRequest.TYPE_IPV6_ADDRESS:
+                    if (req.address == null) {
+                        builder.addInternalAddressRequest(AF_INET6);
+                    } else {
+                        builder.addInternalAddressRequest(
+                                (Inet6Address) req.address, req.ip6PrefixLen);
+                    }
+                    break;
+                case ConfigRequest.TYPE_IPV4_NETMASK:
+                    // Do not need to set netmask because it will be automatically set by the
+                    // builder when an IPv4 internal address request is set.
+                    hasIpv4NetmaskReq = true;
+                    break;
+                case ConfigRequest.TYPE_IPV4_DNS:
+                    if (req.address != null) {
+                        Log.w(TAG, "Requesting a specific IPv4 DNS server is unsupported");
+                    }
+                    builder.addInternalDnsServerRequest(AF_INET);
+                    break;
+                case ConfigRequest.TYPE_IPV6_DNS:
+                    if (req.address != null) {
+                        Log.w(TAG, "Requesting a specific IPv6 DNS server is unsupported");
+                    }
+                    builder.addInternalDnsServerRequest(AF_INET6);
+                    break;
+                case ConfigRequest.TYPE_IPV4_DHCP:
+                    if (req.address != null) {
+                        Log.w(TAG, "Requesting a specific IPv4 DHCP server is unsupported");
+                    }
+                    builder.addInternalDhcpServerRequest(AF_INET);
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unrecognized config request type: " + req.type);
+            }
+        }
+
+        if (hasIpv4AddressReq != hasIpv4NetmaskReq) {
+            Log.w(
+                    TAG,
+                    String.format(
+                            "Expect IPv4 address request and IPv4 netmask request either both"
+                                + " exist or both absent, but found hasIpv4AddressReq exists? %b,"
+                                + " hasIpv4AddressReq exists? %b, ",
+                            hasIpv4AddressReq, hasIpv4NetmaskReq));
+        }
+
+        return builder.build();
+    }
+}
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index de7b885..f12eb88 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -37,6 +37,8 @@
     private final long mStatsStartRealtimeMs;
     private final double mDischargedPowerLowerBound;
     private final double mDischargedPowerUpperBound;
+    private final long mBatteryTimeRemainingMs;
+    private final long mChargeTimeRemainingMs;
     private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers;
     private final ArrayList<SystemBatteryConsumer> mSystemBatteryConsumers;
     private final ArrayList<UserBatteryConsumer> mUserBatteryConsumers;
@@ -50,6 +52,8 @@
         mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah;
         mHistoryBuffer = builder.mHistoryBuffer;
         mHistoryTagPool = builder.mHistoryTagPool;
+        mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs;
+        mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs;
 
         double totalPower = 0;
 
@@ -110,6 +114,25 @@
     }
 
     /**
+     * Returns an approximation for how much run time (in milliseconds) is remaining on
+     * the battery.  Returns -1 if no time can be computed: either there is not
+     * enough current data to make a decision, or the battery is currently
+     * charging.
+     */
+    public long getBatteryTimeRemainingMs() {
+        return mBatteryTimeRemainingMs;
+    }
+
+    /**
+     * Returns an approximation for how much time (in milliseconds) remains until the battery
+     * is fully charged.  Returns -1 if no time can be computed: either there is not
+     * enough current data to make a decision, or the battery is currently discharging.
+     */
+    public long getChargeTimeRemainingMs() {
+        return mChargeTimeRemainingMs;
+    }
+
+    /**
      * Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully
      * charged), in mAh
      */
@@ -156,6 +179,8 @@
         mDischargePercentage = source.readInt();
         mDischargedPowerLowerBound = source.readDouble();
         mDischargedPowerUpperBound = source.readDouble();
+        mBatteryTimeRemainingMs = source.readLong();
+        mChargeTimeRemainingMs = source.readLong();
         mUidBatteryConsumers = new ArrayList<>();
         source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader());
         mSystemBatteryConsumers = new ArrayList<>();
@@ -194,6 +219,8 @@
         dest.writeInt(mDischargePercentage);
         dest.writeDouble(mDischargedPowerLowerBound);
         dest.writeDouble(mDischargedPowerUpperBound);
+        dest.writeLong(mBatteryTimeRemainingMs);
+        dest.writeLong(mChargeTimeRemainingMs);
         dest.writeParcelableList(mUidBatteryConsumers, flags);
         dest.writeParcelableList(mSystemBatteryConsumers, flags);
         dest.writeParcelableList(mUserBatteryConsumers, flags);
@@ -237,6 +264,8 @@
         private int mDischargePercentage;
         private double mDischargedPowerLowerBoundMah;
         private double mDischargedPowerUpperBoundMah;
+        private long mBatteryTimeRemainingMs = -1;
+        private long mChargeTimeRemainingMs = -1;
         private final SparseArray<UidBatteryConsumer.Builder> mUidBatteryConsumerBuilders =
                 new SparseArray<>();
         private final SparseArray<SystemBatteryConsumer.Builder> mSystemBatteryConsumerBuilders =
@@ -289,6 +318,26 @@
         }
 
         /**
+         * Sets an approximation for how much time (in milliseconds) remains until the battery
+         * is fully discharged.
+         */
+        @NonNull
+        public Builder setBatteryTimeRemainingMs(long batteryTimeRemainingMs) {
+            mBatteryTimeRemainingMs = batteryTimeRemainingMs;
+            return this;
+        }
+
+        /**
+         * Sets an approximation for how much time (in milliseconds) remains until the battery
+         * is fully charged.
+         */
+        @NonNull
+        public Builder setChargeTimeRemainingMs(long chargeTimeRemainingMs) {
+            mChargeTimeRemainingMs = chargeTimeRemainingMs;
+            return this;
+        }
+
+        /**
          * Sets the parceled recent history.
          */
         @NonNull
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 74df1b2..a5b0e8d 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1288,10 +1288,12 @@
     public static final String HOST = getString("ro.build.host");
 
     /**
-     * Returns true if we are running a debug build such as "user-debug" or "eng".
-     * @hide
+     * Returns true if the device is running a debuggable build such as "userdebug" or "eng".
+     *
+     * Debuggable builds allow users to gain root access via local shell, attach debuggers to any
+     * application regardless of whether they have the "debuggable" attribute set, or downgrade
+     * selinux into "permissive" mode in particular.
      */
-    @UnsupportedAppUsage
     public static final boolean IS_DEBUGGABLE =
             SystemProperties.getInt("ro.debuggable", 0) == 1;
 
diff --git a/core/java/android/os/BytesMatcher.java b/core/java/android/os/BytesMatcher.java
new file mode 100644
index 0000000..8537f47
--- /dev/null
+++ b/core/java/android/os/BytesMatcher.java
@@ -0,0 +1,248 @@
+/*
+ * 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.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothUuid;
+import android.net.MacAddress;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+/**
+ * Predicate that tests if a given {@code byte[]} value matches a set of
+ * configured rules.
+ * <p>
+ * Rules are tested in the order in which they were originally added, which
+ * means a narrow rule can reject a specific value before a later broader rule
+ * might accept that same value, or vice versa.
+ * <p>
+ * Matchers can contain rules of varying lengths, and tested values will only be
+ * matched against rules of the exact same length. This is designed to support
+ * {@link BluetoothUuid} style values which can be variable length.
+ *
+ * @hide
+ */
+public class BytesMatcher implements Predicate<byte[]> {
+   private static final String TAG = "BytesMatcher";
+
+   private static final char TYPE_ACCEPT = '+';
+   private static final char TYPE_REJECT = '-';
+
+   private final ArrayList<Rule> mRules = new ArrayList<>();
+
+   private static class Rule {
+       public final char type;
+       public final @NonNull byte[] value;
+       public final @Nullable byte[] mask;
+
+       public Rule(char type, @NonNull byte[] value, @Nullable byte[] mask) {
+           if (mask != null && value.length != mask.length) {
+               throw new IllegalArgumentException(
+                       "Expected length " + value.length + " but found " + mask.length);
+           }
+           this.type = type;
+           this.value = value;
+           this.mask = mask;
+       }
+
+       @Override
+       public String toString() {
+           StringBuilder builder = new StringBuilder();
+           encode(builder);
+           return builder.toString();
+       }
+
+       public void encode(@NonNull StringBuilder builder) {
+           builder.append(type);
+           builder.append(HexDump.toHexString(value));
+           if (mask != null) {
+               builder.append('/');
+               builder.append(HexDump.toHexString(mask));
+           }
+       }
+
+       public boolean test(@NonNull byte[] value) {
+           if (value.length != this.value.length) {
+               return false;
+           }
+           for (int i = 0; i < this.value.length; i++) {
+               byte local = this.value[i];
+               byte remote = value[i];
+               if (this.mask != null) {
+                   local &= this.mask[i];
+                   remote &= this.mask[i];
+               }
+               if (local != remote) {
+                   return false;
+               }
+           }
+           return true;
+       }
+   }
+
+   /**
+    * Add a rule that will result in {@link #test(byte[])} returning
+    * {@code true} when a value being tested matches it.
+    * <p>
+    * Rules are tested in the order in which they were originally added, which
+    * means a narrow rule can reject a specific value before a later broader
+    * rule might accept that same value, or vice versa.
+    *
+    * @param value to be matched
+    * @param mask to be applied to both values before testing for equality; if
+    *            {@code null} then both values must match exactly
+    */
+   public void addAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) {
+       mRules.add(new Rule(TYPE_ACCEPT, value, mask));
+   }
+
+   /**
+    * Add a rule that will result in {@link #test(byte[])} returning
+    * {@code false} when a value being tested matches it.
+    * <p>
+    * Rules are tested in the order in which they were originally added, which
+    * means a narrow rule can reject a specific value before a later broader
+    * rule might accept that same value, or vice versa.
+    *
+    * @param value to be matched
+    * @param mask to be applied to both values before testing for equality; if
+    *            {@code null} then both values must match exactly
+    */
+   public void addRejectRule(@NonNull byte[] value, @Nullable byte[] mask) {
+       mRules.add(new Rule(TYPE_REJECT, value, mask));
+   }
+
+   /**
+    * Test if the given {@code ParcelUuid} value matches the set of rules
+    * configured in this matcher.
+    */
+   public boolean testBluetoothUuid(@NonNull ParcelUuid value) {
+       return test(BluetoothUuid.uuidToBytes(value));
+   }
+
+   /**
+    * Test if the given {@code MacAddress} value matches the set of rules
+    * configured in this matcher.
+    */
+   public boolean testMacAddress(@NonNull MacAddress value) {
+       return test(value.toByteArray());
+   }
+
+   /**
+    * Test if the given {@code byte[]} value matches the set of rules
+    * configured in this matcher.
+    */
+   @Override
+   public boolean test(@NonNull byte[] value) {
+       return test(value, false);
+   }
+
+   /**
+    * Test if the given {@code byte[]} value matches the set of rules
+    * configured in this matcher.
+    */
+   public boolean test(@NonNull byte[] value, boolean defaultValue) {
+       final int size = mRules.size();
+       for (int i = 0; i < size; i++) {
+           final Rule rule = mRules.get(i);
+           if (rule.test(value)) {
+               return (rule.type == TYPE_ACCEPT);
+           }
+       }
+       return defaultValue;
+   }
+
+   /**
+    * Encode the given matcher into a human-readable {@link String} which can
+    * be used to transport matchers across device boundaries.
+    * <p>
+    * The human-readable format is an ordered list separated by commas, where
+    * each rule is a {@code +} or {@code -} symbol indicating if the match
+    * should be accepted or rejected, then followed by a hex value and an
+    * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
+    * encoded matcher.
+    *
+    * @see #decode(String)
+    */
+   public static @NonNull String encode(@NonNull BytesMatcher matcher) {
+       final StringBuilder builder = new StringBuilder();
+       final int size = matcher.mRules.size();
+       for (int i = 0; i < size; i++) {
+           final Rule rule = matcher.mRules.get(i);
+           rule.encode(builder);
+           builder.append(',');
+       }
+       builder.deleteCharAt(builder.length() - 1);
+       return builder.toString();
+   }
+
+   /**
+    * Decode the given human-readable {@link String} used to transport matchers
+    * across device boundaries.
+    * <p>
+    * The human-readable format is an ordered list separated by commas, where
+    * each rule is a {@code +} or {@code -} symbol indicating if the match
+    * should be accepted or rejected, then followed by a hex value and an
+    * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
+    * encoded matcher.
+    *
+    * @see #encode(BytesMatcher)
+    */
+   public static @NonNull BytesMatcher decode(@NonNull String value) {
+       final BytesMatcher matcher = new BytesMatcher();
+       final int length = value.length();
+       for (int i = 0; i < length;) {
+           final char type = value.charAt(i);
+
+           int nextRule = value.indexOf(',', i);
+           int nextMask = value.indexOf('/', i);
+
+           if (nextRule == -1) nextRule = length;
+           if (nextMask > nextRule) nextMask = -1;
+
+           final byte[] ruleValue;
+           final byte[] ruleMask;
+           if (nextMask >= 0) {
+               ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextMask));
+               ruleMask = HexDump.hexStringToByteArray(value.substring(nextMask + 1, nextRule));
+           } else {
+               ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextRule));
+               ruleMask = null;
+           }
+
+           switch (type) {
+               case TYPE_ACCEPT:
+                   matcher.addAcceptRule(ruleValue, ruleMask);
+                   break;
+               case TYPE_REJECT:
+                   matcher.addRejectRule(ruleValue, ruleMask);
+                   break;
+               default:
+                   Log.w(TAG, "Ignoring unknown type " + type);
+                   break;
+           }
+
+           i = nextRule + 1;
+       }
+       return matcher;
+   }
+}
diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java
index c8e682c..e068772 100644
--- a/core/java/android/os/CombinedVibrationEffect.java
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -76,7 +76,9 @@
      * A sequential vibration effect should be performed by multiple vibrators in order.
      *
      * @see CombinedVibrationEffect.SequentialCombination
+     * @hide
      */
+    @TestApi
     @NonNull
     public static SequentialCombination startSequential() {
         return new SequentialCombination();
@@ -162,7 +164,9 @@
      * A combination of haptic effects that should be played in multiple vibrators in sequence.
      *
      * @see CombinedVibrationEffect#startSequential()
+     * @hide
      */
+    @TestApi
     public static final class SequentialCombination {
 
         private final ArrayList<CombinedVibrationEffect> mEffects = new ArrayList<>();
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 8f61613..ae7d94c 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -42,8 +42,7 @@
     void updateWakeLockWorkSource(IBinder lock, in WorkSource ws, String historyTag);
     boolean isWakeLockLevelSupported(int level);
 
-    @UnsupportedAppUsage
-    void userActivity(long time, int event, int flags);
+    void userActivity(int displayId, long time, int event, int flags);
     void wakeUp(long time, int reason, String details, String opPackageName);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void goToSleep(long time, int reason, int flags);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index e5163d8..786a7d0 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1277,7 +1277,7 @@
     })
     public void userActivity(long when, int event, int flags) {
         try {
-            mService.userActivity(when, event, flags);
+            mService.userActivity(mContext.getDisplayId(), when, event, flags);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 0587610..03e5f1d 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -503,6 +503,20 @@
     }
 
     /** @hide */
+    public static String effectStrengthToString(int effectStrength) {
+        switch (effectStrength) {
+            case EFFECT_STRENGTH_LIGHT:
+                return "LIGHT";
+            case EFFECT_STRENGTH_MEDIUM:
+                return "MEDIUM";
+            case EFFECT_STRENGTH_STRONG:
+                return "STRONG";
+            default:
+                return Integer.toString(effectStrength);
+        }
+    }
+
+    /** @hide */
     @TestApi
     public static class OneShot extends VibrationEffect implements Parcelable {
         private final long mDuration;
@@ -936,8 +950,8 @@
 
         @Override
         public String toString() {
-            return "Prebaked{mEffectId=" + mEffectId
-                + ", mEffectStrength=" + mEffectStrength
+            return "Prebaked{mEffectId=" + effectIdToString(mEffectId)
+                + ", mEffectStrength=" + effectStrengthToString(mEffectStrength)
                 + ", mFallback=" + mFallback
                 + ", mFallbackEffect=" + mFallbackEffect
                 + "}";
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 07272e7..50d2de3 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.vibrator.IVibrator;
 import android.util.SparseBooleanArray;
 
 import java.util.ArrayList;
@@ -33,20 +34,7 @@
  * @hide
  */
 public final class VibratorInfo implements Parcelable {
-
-    /**
-     * Capability to set amplitude values to vibrations.
-     * @hide
-     */
-    // Internally this maps to the HAL constant IVibrator::CAP_AMPLITUDE_CONTROL
-    public static final int CAPABILITY_AMPLITUDE_CONTROL = 4;
-
-    /**
-     * Capability to compose primitives into a single effect.
-     * @hide
-     */
-    // Internally this maps to the HAL constant IVibrator::CAP_COMPOSE_EFFECTS
-    public static final int CAPABILITY_COMPOSE_EFFECTS = 32;
+    private static final String TAG = "VibratorInfo";
 
     private final int mId;
     private final long mCapabilities;
@@ -108,7 +96,7 @@
         return "VibratorInfo{"
                 + "mId=" + mId
                 + ", mCapabilities=" + Arrays.toString(getCapabilitiesNames())
-                + ", mCapabilities flags=" + mCapabilities
+                + ", mCapabilities flags=" + Long.toBinaryString(mCapabilities)
                 + ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
                 + ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
                 + '}';
@@ -125,7 +113,7 @@
      * @return True if the hardware can control the amplitude of the vibrations, otherwise false.
      */
     public boolean hasAmplitudeControl() {
-        return hasCapability(CAPABILITY_AMPLITUDE_CONTROL);
+        return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL);
     }
 
     /**
@@ -153,7 +141,7 @@
      * @return Whether the primitive is supported.
      */
     public boolean isPrimitiveSupported(@VibrationEffect.Composition.Primitive int primitiveId) {
-        return hasCapability(CAPABILITY_COMPOSE_EFFECTS) && mSupportedPrimitives != null
+        return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) && mSupportedPrimitives != null
                 && mSupportedPrimitives.get(primitiveId, false);
     }
 
@@ -170,11 +158,26 @@
 
     private String[] getCapabilitiesNames() {
         List<String> names = new ArrayList<>();
-        if (hasCapability(CAPABILITY_AMPLITUDE_CONTROL)) {
+        if (hasCapability(IVibrator.CAP_ON_CALLBACK)) {
+            names.add("ON_CALLBACK");
+        }
+        if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) {
+            names.add("PERFORM_CALLBACK");
+        }
+        if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+            names.add("COMPOSE_EFFECTS");
+        }
+        if (hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
+            names.add("ALWAYS_ON_CONTROL");
+        }
+        if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
             names.add("AMPLITUDE_CONTROL");
         }
-        if (hasCapability(CAPABILITY_COMPOSE_EFFECTS)) {
-            names.add("COMPOSE_EFFECTS");
+        if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
+            names.add("EXTERNAL_CONTROL");
+        }
+        if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) {
+            names.add("EXTERNAL_AMPLITUDE_CONTROL");
         }
         return names.toArray(new String[names.size()]);
     }
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 9ffc5aa0..bf28981 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -333,7 +333,7 @@
      *                             started.
      * @param pkgDataInfoMap Map from related package names to private data directory
      *                       volume UUID and inode number.
-     * @param whitelistedDataInfoMap Map from allowlisted package names to private data directory
+     * @param allowlistedDataInfoList Map from allowlisted package names to private data directory
      *                       volume UUID and inode number.
      * @param bindMountAppsData whether zygote needs to mount CE and DE data.
      * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
@@ -359,7 +359,7 @@
                                                   @Nullable Map<String, Pair<String, Long>>
                                                           pkgDataInfoMap,
                                                   @Nullable Map<String, Pair<String, Long>>
-                                                          whitelistedDataInfoMap,
+                                                          allowlistedDataInfoList,
                                                   boolean bindMountAppsData,
                                                   boolean bindMountAppStorageDirs,
                                                   @Nullable String[] zygoteArgs) {
@@ -373,7 +373,7 @@
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
                     packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
-                    pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
+                    pkgDataInfoMap, allowlistedDataInfoList, bindMountAppsData,
                     bindMountAppStorageDirs, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
@@ -615,7 +615,7 @@
      * @param disabledCompatChanges a list of disabled compat changes for the process being started.
      * @param pkgDataInfoMap Map from related package names to private data directory volume UUID
      *                       and inode number.
-     * @param whitelistedDataInfoMap Map from allowlisted package names to private data directory
+     * @param allowlistedDataInfoList Map from allowlisted package names to private data directory
      *                       volume UUID and inode number.
      * @param bindMountAppsData whether zygote needs to mount CE and DE data.
      * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
@@ -642,7 +642,7 @@
                                                       @Nullable Map<String, Pair<String, Long>>
                                                               pkgDataInfoMap,
                                                       @Nullable Map<String, Pair<String, Long>>
-                                                              whitelistedDataInfoMap,
+                                                              allowlistedDataInfoList,
                                                       boolean bindMountAppsData,
                                                       boolean bindMountAppStorageDirs,
                                                       @Nullable String[] extraArgs)
@@ -733,12 +733,12 @@
             }
             argsForZygote.add(sb.toString());
         }
-        if (whitelistedDataInfoMap != null && whitelistedDataInfoMap.size() > 0) {
+        if (allowlistedDataInfoList != null && allowlistedDataInfoList.size() > 0) {
             StringBuilder sb = new StringBuilder();
-            sb.append(Zygote.WHITELISTED_DATA_INFO_MAP);
+            sb.append(Zygote.ALLOWLISTED_DATA_INFO_MAP);
             sb.append("=");
             boolean started = false;
-            for (Map.Entry<String, Pair<String, Long>> entry : whitelistedDataInfoMap.entrySet()) {
+            for (Map.Entry<String, Pair<String, Long>> entry : allowlistedDataInfoList.entrySet()) {
                 if (started) {
                     sb.append(',');
                 }
@@ -1318,7 +1318,7 @@
                     true /* startChildZygote */, null /* packageName */,
                     ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
                     null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
-                    null /* whitelistedDataInfoMap */, true /* bindMountAppsData*/,
+                    null /* allowlistedDataInfoList */, true /* bindMountAppsData*/,
                     /* bindMountAppStorageDirs */ false, extraArgs);
 
         } catch (ZygoteStartFailedEx ex) {
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 87dced8..dc6f63a 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -58,6 +58,8 @@
 
     private static final String ALLOWED_PROPERTY = "incremental.allowed";
 
+    public static final int MIN_VERSION_TO_SUPPORT_FSVERITY = 2;
+
     public static final int CREATE_MODE_TEMPORARY_BIND =
             IIncrementalService.CREATE_MODE_TEMPORARY_BIND;
     public static final int CREATE_MODE_PERMANENT_BIND =
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index b12bb2e..396ba2d 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.os.IVold;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -112,4 +113,10 @@
      * @param bytes number of bytes which need to be freed
      */
     public abstract void freeCache(@Nullable String volumeUuid, long bytes);
+
+    /**
+     * Returns the {@link VolumeInfo#getId()} values for the volumes matching
+     * {@link VolumeInfo#isPrimary()}
+     */
+    public abstract List<String> getPrimaryVolumeIds();
 }
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 084b18e..913b827 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -668,7 +668,7 @@
     public void getPrivilegesDescriptionStringForProfile(
             @NonNull String profileName,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull Consumer<String> callback) {
+            @NonNull Consumer<CharSequence> callback) {
         mRemoteService.postAsync(service -> {
             AndroidFuture<String> future = new AndroidFuture<>();
             service.getPrivilegesDescriptionStringForProfile(profileName, future);
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 6e89faf..e9bbcc79 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -505,6 +505,22 @@
             "connectivity_thermal_power_manager";
 
     /**
+     * Namespace for all statsd java features that can be applied immediately.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_STATSD_JAVA = "statsd_java";
+
+    /**
+     * Namespace for all statsd java features that are applied on boot.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_STATSD_JAVA_BOOT = "statsd_java_boot";
+
+    /**
      * Namespace for all statsd native features that can be applied immediately.
      *
      * @hide
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index fbc25a6..4c8ee59 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -18,10 +18,13 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -44,6 +47,8 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * {@link ServiceInfo} and meta-data about an {@link AutofillService}.
@@ -76,6 +81,8 @@
 
     @Nullable
     private final String mSettingsActivity;
+    @Nullable
+    private final String mPasswordsActivity;
 
     @Nullable
     private final ArrayMap<String, Long> mCompatibilityPackages;
@@ -113,12 +120,14 @@
                 AutofillService.SERVICE_META_DATA);
         if (parser == null) {
             mSettingsActivity = null;
+            mPasswordsActivity = null;
             mCompatibilityPackages = null;
             mInlineSuggestionsEnabled = false;
             return;
         }
 
         String settingsActivity = null;
+        String passwordsActivity = null;
         ArrayMap<String, Long> compatibilityPackages = null;
         boolean inlineSuggestionsEnabled = false; // false by default.
 
@@ -139,6 +148,8 @@
                             com.android.internal.R.styleable.AutofillService);
                     settingsActivity = afsAttributes.getString(
                             R.styleable.AutofillService_settingsActivity);
+                    passwordsActivity = afsAttributes.getString(
+                            R.styleable.AutofillService_passwordsActivity);
                     inlineSuggestionsEnabled = afsAttributes.getBoolean(
                             R.styleable.AutofillService_supportsInlineSuggestions, false);
                 } finally {
@@ -155,6 +166,7 @@
         }
 
         mSettingsActivity = settingsActivity;
+        mPasswordsActivity = passwordsActivity;
         mCompatibilityPackages = compatibilityPackages;
         mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
     }
@@ -221,6 +233,7 @@
         return compatibilityPackages;
     }
 
+    @NonNull
     public ServiceInfo getServiceInfo() {
         return mServiceInfo;
     }
@@ -230,6 +243,12 @@
         return mSettingsActivity;
     }
 
+    @Nullable
+    public String getPasswordsActivity() {
+        return mPasswordsActivity;
+    }
+
+    @Nullable
     public ArrayMap<String, Long> getCompatibilityPackages() {
         return mCompatibilityPackages;
     }
@@ -238,12 +257,37 @@
         return mInlineSuggestionsEnabled;
     }
 
+    /**
+     * Queries the valid autofill services available for the user.
+     */
+    public static List<AutofillServiceInfo> getAvailableServices(
+            Context context, @UserIdInt int user) {
+        final List<AutofillServiceInfo> services = new ArrayList<>();
+
+        final List<ResolveInfo> resolveInfos =
+                context.getPackageManager().queryIntentServicesAsUser(
+                        new Intent(AutofillService.SERVICE_INTERFACE),
+                        PackageManager.GET_META_DATA,
+                        user);
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+            try {
+                services.add(new AutofillServiceInfo(context, serviceInfo));
+            } catch (SecurityException e) {
+                // Service does not declare the proper permission, ignore it.
+                Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e);
+            }
+        }
+        return services;
+    }
+
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
         builder.append(getClass().getSimpleName());
         builder.append("[").append(mServiceInfo);
         builder.append(", settings:").append(mSettingsActivity);
+        builder.append(", passwords activity:").append(mPasswordsActivity);
         builder.append(", hasCompatPckgs:").append(mCompatibilityPackages != null
                 && !mCompatibilityPackages.isEmpty()).append("]");
         builder.append(", inline suggestions enabled:").append(mInlineSuggestionsEnabled);
@@ -256,6 +300,7 @@
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("Component: "); pw.println(getServiceInfo().getComponentName());
         pw.print(prefix); pw.print("Settings: "); pw.println(mSettingsActivity);
+        pw.print(prefix); pw.print("Passwords activity: "); pw.println(mPasswordsActivity);
         pw.print(prefix); pw.print("Compat packages: "); pw.println(mCompatibilityPackages);
         pw.print(prefix); pw.print("Inline Suggestions Enabled: ");
         pw.println(mInlineSuggestionsEnabled);
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index 1e07a87..bbe184b 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -239,14 +239,13 @@
         }
 
         @Override
-        public void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason,
-                RemoteCallback callback) throws RemoteException {
+        public void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason)
+                throws RemoteException {
             mHandler.post(() -> {
                 try {
                     onAnrDelayStarted(packageName, uid, tid, reason);
-                    sendResult(packageName, null /* throwable */, callback);
                 } catch (Throwable t) {
-                    sendResult(packageName, t, callback);
+                    // Ignored
                 }
             });
         }
diff --git a/core/java/android/service/storage/IExternalStorageService.aidl b/core/java/android/service/storage/IExternalStorageService.aidl
index ba98efa..0766b75 100644
--- a/core/java/android/service/storage/IExternalStorageService.aidl
+++ b/core/java/android/service/storage/IExternalStorageService.aidl
@@ -32,6 +32,5 @@
         in RemoteCallback callback);
     void freeCache(@utf8InCpp String sessionId, in String volumeUuid, long bytes,
         in RemoteCallback callback);
-    void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason,
-         in RemoteCallback callback);
+    void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason);
 }
\ No newline at end of file
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index e7ceada..e37921e 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -17,10 +17,7 @@
 package android.telephony;
 
 import android.Manifest;
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.compat.annotation.ChangeId;
@@ -31,16 +28,12 @@
 import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.telephony.Annotation.CallState;
-import android.telephony.Annotation.DataActivityType;
 import android.telephony.Annotation.DisconnectCauses;
-import android.telephony.Annotation.NetworkType;
 import android.telephony.Annotation.PreciseDisconnectCauses;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SimActivationState;
 import android.telephony.Annotation.SrvccState;
-import android.telephony.NetworkRegistrationInfo.Domain;
 import android.telephony.TelephonyManager.DataEnabledReason;
-import android.telephony.TelephonyManager.DataState;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsReasonInfo;
 
@@ -49,8 +42,6 @@
 
 import dalvik.system.VMRuntime;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.Map;
@@ -71,49 +62,15 @@
  * information unless it has the appropriate permissions declared in
  * its manifest file. Where permissions apply, they are noted in the
  * appropriate LISTEN_ flags.
+ *
+ * @deprecated Use {@link TelephonyCallback} instead.
  */
+@Deprecated
 public class PhoneStateListener {
     private static final String LOG_TAG = "PhoneStateListener";
     private static final boolean DBG = false; // STOPSHIP if true
 
     /**
-     * Experiment flag to set the per-pid registration limit for PhoneStateListeners
-     *
-     * Limit on registrations of {@link PhoneStateListener}s on a per-pid
-     * basis. When this limit is exceeded, any calls to {@link TelephonyManager#listen} will fail
-     * with an {@link IllegalStateException}.
-     *
-     * {@link android.os.Process#PHONE_UID}, {@link android.os.Process#SYSTEM_UID}, and the uid that
-     * TelephonyRegistry runs under are exempt from this limit.
-     *
-     * If the value of the flag is less than 1, enforcement of the limit will be disabled.
-     * @hide
-     */
-    public static final String FLAG_PER_PID_REGISTRATION_LIMIT =
-            "phone_state_listener_per_pid_registration_limit";
-
-    /**
-     * Default value for the per-pid registation limit.
-     * See {@link #FLAG_PER_PID_REGISTRATION_LIMIT}.
-     * @hide
-     */
-    public static final int DEFAULT_PER_PID_REGISTRATION_LIMIT = 50;
-
-    /**
-     * This change enables a limit on the number of {@link PhoneStateListener} objects any process
-     * may register via {@link TelephonyManager#listen}. The default limit is 50, which may change
-     * via remote device config updates.
-     *
-     * This limit is enforced via an {@link IllegalStateException} thrown from
-     * {@link TelephonyManager#listen} when the offending process attempts to register one too many
-     * listeners.
-     *
-     * @hide
-     */
-    @ChangeId
-    public static final long PHONE_STATE_LISTENER_LIMIT_CHANGE_ID = 150880553L;
-
-    /**
      * Stop listening for updates.
      *
      * The PhoneStateListener is not tied to any subscription and unregistered for any update.
@@ -125,7 +82,7 @@
      *
      *  @see #onServiceStateChanged
      *  @see ServiceState
-     *  @deprecated Use {@link ServiceStateChangedListener} instead.
+     *  @deprecated Use {@link TelephonyCallback.ServiceStateListener} instead.
      */
     @Deprecated
     public static final int LISTEN_SERVICE_STATE                            = 0x00000001;
@@ -135,7 +92,7 @@
      * {@more}
      *
      * @see #onSignalStrengthChanged
-     * @deprecated Use {@link SignalStrengthsChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead.
      */
     @Deprecated
     public static final int LISTEN_SIGNAL_STRENGTH                          = 0x00000002;
@@ -151,7 +108,7 @@
      * voicemail icon.
      *
      * @see #onMessageWaitingIndicatorChanged
-     * @deprecated Use {@link MessageWaitingIndicatorChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead.
      */
     @Deprecated
     public static final int LISTEN_MESSAGE_WAITING_INDICATOR                = 0x00000004;
@@ -164,7 +121,7 @@
      * {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onCallForwardingIndicatorChanged
-     * @deprecated Use {@link CallForwardingIndicatorChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead.
      */
     @Deprecated
     public static final int LISTEN_CALL_FORWARDING_INDICATOR                = 0x00000008;
@@ -182,7 +139,7 @@
      * instead.
      *
      * @see #onCellLocationChanged
-     * @deprecated Use {@link CellLocationChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead.
      */
     @Deprecated
     public static final int LISTEN_CELL_LOCATION                            = 0x00000010;
@@ -192,7 +149,7 @@
      * {@more}
      *
      * @see #onCallStateChanged
-     * @deprecated Use {@link CallStateChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.CallStateListener} instead.
      */
     @Deprecated
     public static final int LISTEN_CALL_STATE                               = 0x00000020;
@@ -201,7 +158,7 @@
      * Listen for changes to the data connection state (cellular).
      *
      * @see #onDataConnectionStateChanged
-     * @deprecated Use {@link DataConnectionStateChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead.
      */
     @Deprecated
     public static final int LISTEN_DATA_CONNECTION_STATE                    = 0x00000040;
@@ -214,7 +171,7 @@
      * data-traffic icon.
      *
      * @see #onDataActivity
-     * @deprecated Use {@link DataActivityListener} instead.
+     * @deprecated Use {@link TelephonyCallback.DataActivityListener} instead.
      */
     @Deprecated
     public static final int LISTEN_DATA_ACTIVITY                            = 0x00000080;
@@ -226,7 +183,7 @@
      * icon.
      *
      * @see #onSignalStrengthsChanged
-     * @deprecated Use {@link SignalStrengthsChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead.
      */
     @Deprecated
     public static final int LISTEN_SIGNAL_STRENGTHS                         = 0x00000100;
@@ -238,7 +195,8 @@
      * @see #onSignalStrengthsChanged
      *
      * @hide
-     * @deprecated Use {@link AlwaysReportedSignalStrengthChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.AlwaysReportedSignalStrengthListener}
+     * instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
@@ -251,7 +209,7 @@
      * permission.
      *
      * @see #onCellInfoChanged
-     * @deprecated Use {@link CellInfoChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead.
      */
     @Deprecated
     public static final int LISTEN_CELL_INFO = 0x00000400;
@@ -265,7 +223,7 @@
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @hide
-     * @deprecated Use {@link PreciseCallStateChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.PreciseCallStateListener} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -280,7 +238,7 @@
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onPreciseDataConnectionStateChanged
-     * @deprecated Use {@link PreciseDataConnectionStateChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.PreciseDataConnectionStateListener} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -306,7 +264,7 @@
      *
      * @see #onServiceStateChanged(ServiceState)
      * @hide
-     * @deprecated Use {@link SrvccStateChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.SrvccStateListener} instead.
      */
     @Deprecated
     @SystemApi
@@ -328,7 +286,7 @@
      *
      * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
      * @hide
-     * @deprecated Use {@link CarrierNetworkChangeListener} instead.
+     * @deprecated Use {@link TelephonyCallback.CarrierNetworkListener} instead.
      */
     @Deprecated
     public static final int LISTEN_CARRIER_NETWORK_CHANGE                   = 0x00010000;
@@ -349,7 +307,7 @@
      *
      * @see #onVoiceActivationStateChanged
      * @hide
-     * @deprecated Use {@link VoiceActivationStateChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.VoiceActivationStateListener} instead.
      */
     @Deprecated
     @SystemApi
@@ -369,7 +327,7 @@
      *
      * @see #onDataActivationStateChanged
      * @hide
-     * @deprecated Use {@link DataActivationStateChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.DataActivationStateListener} instead.
      */
     @Deprecated
     public static final int LISTEN_DATA_ACTIVATION_STATE                   = 0x00040000;
@@ -378,7 +336,7 @@
      *  Listen for changes to the user mobile data state
      *
      *  @see #onUserMobileDataStateChanged
-     *  @deprecated Use {@link UserMobileDataStateChangedListener} instead.
+     *  @deprecated Use {@link TelephonyCallback.UserMobileDataStateListener} instead.
      */
     @Deprecated
     public static final int LISTEN_USER_MOBILE_DATA_STATE                  = 0x00080000;
@@ -391,7 +349,7 @@
      *  {@link TelephonyManager#hasCarrierPrivileges}).
      *
      *  @see #onDisplayInfoChanged
-     * @deprecated Use {@link DisplayInfoChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead.
      */
     @Deprecated
     public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000;
@@ -401,7 +359,7 @@
      *
      *  @see #onPhoneCapabilityChanged
      *  @hide
-     *  @deprecated Use {@link PhoneCapabilityChangedListener} instead.
+     *  @deprecated Use {@link TelephonyCallback.PhoneCapabilityListener} instead.
      */
     @Deprecated
     public static final int LISTEN_PHONE_CAPABILITY_CHANGE                 = 0x00200000;
@@ -413,7 +371,7 @@
      *  subscription user selected as default data subscription in DSDS mode.
      *
      *  @see #onActiveDataSubscriptionIdChanged
-     *  @deprecated Use {@link ActiveDataSubscriptionIdChangedListener} instead.
+     *  @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead.
      */
     @Deprecated
     public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000;
@@ -423,7 +381,7 @@
      *
      *  @see #onRadioPowerStateChanged
      *  @hide
-     *  @deprecated Use {@link RadioPowerStateChangedListener} instead.
+     *  @deprecated Use {@link TelephonyCallback.RadioPowerStateListener} instead.
      */
     @Deprecated
     @SystemApi
@@ -436,7 +394,7 @@
      * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
      * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
-     * @deprecated Use {@link EmergencyNumberListChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead.
      */
     @Deprecated
     public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
@@ -449,7 +407,7 @@
      * or the calling app has carrier privileges
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
-     * @deprecated Use {@link CallDisconnectCauseChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.CallDisconnectCauseListener} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -464,7 +422,7 @@
      *
      * @see #onCallAttributesChanged
      * @hide
-     * @deprecated Use {@link CallAttributesChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.CallAttributesListener} instead.
      */
     @Deprecated
     @SystemApi
@@ -480,7 +438,7 @@
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo)
-     * @deprecated Use {@link ImsCallDisconnectCauseChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.ImsCallDisconnectCauseListener} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -491,7 +449,7 @@
      *
      * @see #onOutgoingEmergencyCall
      * @hide
-     * @deprecated Use {@link OutgoingEmergencyCallListener} instead.
+     * @deprecated Use {@link TelephonyCallback.OutgoingEmergencyCallListener} instead.
      */
     @Deprecated
     @SystemApi
@@ -503,7 +461,7 @@
      *
      * @see #onOutgoingEmergencySms
      * @hide
-     * @deprecated Use {@link OutgoingEmergencySmsListener} instead.
+     * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead.
      */
     @Deprecated
     @SystemApi
@@ -524,7 +482,7 @@
      * of whether the calling app has carrier privileges.
      *
      * @see #onRegistrationFailed
-     * @deprecated Use {@link RegistrationFailedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead.
      */
     @Deprecated
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -540,540 +498,12 @@
      * of whether the calling app has carrier privileges.
      *
      * @see #onBarringInfoChanged
-     * @deprecated Use {@link BarringInfoChangedListener} instead.
+     * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead.
      */
     @Deprecated
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public static final int LISTEN_BARRING_INFO = 0x80000000;
 
-    /**
-     *  Event for changes to the network service state (cellular).
-     *
-     *  @see ServiceStateChangedListener#onServiceStateChanged
-     *  @see ServiceState
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_SERVICE_STATE_CHANGED = 1;
-
-    /**
-     * Event for changes to the network signal strength (cellular).
-     *
-     * @see SignalStrengthsChangedListener#onSignalStrengthsChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2;
-
-    /**
-     * Event for changes to the message-waiting indicator.
-     *
-     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
-     * the calling app has carrier privileges (see
-     * {@link TelephonyManager#hasCarrierPrivileges}).
-     * <p>
-     * Example: The status bar uses this to determine when to display the
-     * voicemail icon.
-     *
-     * @see MessageWaitingIndicatorChangedListener#onMessageWaitingIndicatorChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3;
-
-    /**
-     * Event for changes to the call-forwarding indicator.
-     *
-     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
-     * the calling app has carrier privileges (see
-     * {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see CallForwardingIndicatorChangedListener#onCallForwardingIndicatorChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4;
-
-    /**
-     * Event for changes to the device's cell location. Note that
-     * this will result in frequent callbacks to the listener.
-     *
-     * If you need regular location updates but want more control over
-     * the update interval or location precision, you can set up a listener
-     * through the {@link android.location.LocationManager location manager}
-     * instead.
-     *
-     * @see CellLocationChangedListener#onCellLocationChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-    public static final int EVENT_CELL_LOCATION_CHANGED = 5;
-
-    /**
-     * Event for changes to the device call state.
-     *
-     * @see CallStateChangedListener#onCallStateChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
-    public static final int EVENT_CALL_STATE_CHANGED = 6;
-
-    /**
-     * Event for changes to the data connection state (cellular).
-     *
-     * @see DataConnectionStateChangedListener#onDataConnectionStateChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7;
-
-    /**
-     * Event for changes to the direction of data traffic on the data
-     * connection (cellular).
-     *
-     * Example: The status bar uses this to display the appropriate
-     * data-traffic icon.
-     *
-     * @see DataActivityListener#onDataActivity
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_DATA_ACTIVITY_CHANGED = 8;
-
-    /**
-     * Event for changes to the network signal strengths (cellular).
-     * <p>
-     * Example: The status bar uses this to control the signal-strength
-     * icon.
-     *
-     * @see SignalStrengthsChangedListener#onSignalStrengthsChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9;
-
-    /**
-     * Event for changes of the network signal strengths (cellular) always reported from modem,
-     * even in some situations such as the screen of the device is off.
-     *
-     * @see AlwaysReportedSignalStrengthChangedListener#onSignalStrengthsChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
-    public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10;
-
-    /**
-     * Event for changes to observed cell info.
-     *
-     * @see CellInfoChangedListener#onCellInfoChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-    public static final int EVENT_CELL_INFO_CHANGED = 11;
-
-    /**
-     * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
-     * background and foreground calls.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see PreciseCallStateChangedListener#onPreciseCallStateChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12;
-
-    /**
-     * Event for {@link PreciseDataConnectionState} on the data connection (cellular).
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see PreciseDataConnectionStateChangedListener#onPreciseDataConnectionStateChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13;
-
-    /**
-     * Event for real time info for all data connections (cellular)).
-     *
-     * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
-     *
-     * @deprecated Use {@link TelephonyManager#requestModemActivityInfo}
-     * @hide
-     */
-    @Deprecated
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14;
-
-    /**
-     * Event for OEM hook raw event
-     *
-     * @see #onOemHookRawEvent
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public static final int EVENT_OEM_HOOK_RAW = 15;
-
-    /**
-     * Event for changes to the SRVCC state of the active call.
-     *
-     * @see SrvccStateChangedListener#onSrvccStateChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public static final int EVENT_SRVCC_STATE_CHANGED = 16;
-
-    /**
-     * Event for carrier network changes indicated by a carrier app.
-     *
-     * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
-     * @see CarrierNetworkChangeListener#onCarrierNetworkChange
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_CARRIER_NETWORK_CHANGED = 17;
-
-    /**
-     * Event for changes to the sim voice activation state
-     *
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
-     *
-     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
-     * fully activated
-     *
-     * @see VoiceActivationStateChangedListener#onVoiceActivationStateChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18;
-
-    /**
-     * Event for changes to the sim data activation state
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
-     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
-     *
-     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
-     * fully activated
-     *
-     * @see DataActivationStateChangedListener#onDataActivationStateChanged
-     * @hide
-     */
-    @SystemApi
-    public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19;
-
-    /**
-     *  Event for changes to the user mobile data state
-     *
-     *  @see UserMobileDataStateChangedListener#onUserMobileDataStateChanged
-     *
-     *  @hide
-     */
-    @SystemApi
-    public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20;
-
-    /**
-     *  Event for display info changed event.
-     *
-     *  @see DisplayInfoChangedListener#onDisplayInfoChanged
-     *
-     *  @hide
-     */
-    @SystemApi
-    public static final int EVENT_DISPLAY_INFO_CHANGED = 21;
-
-    /**
-     *  Event for changes to the phone capability.
-     *
-     *  @see PhoneCapabilityChangedListener#onPhoneCapabilityChanged
-     *
-     *  @hide
-     */
-    @SystemApi
-    public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22;
-
-    /**
-     *  Event for changes to active data subscription ID. Active data subscription is
-     *  the current subscription used to setup Cellular Internet data. For example,
-     *  it could be the current active opportunistic subscription in use, or the
-     *  subscription user selected as default data subscription in DSDS mode.
-     *
-     *  <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
-     *  app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     *  @see ActiveDataSubscriptionIdChangedListener#onActiveDataSubscriptionIdChanged
-     *
-     *  @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23;
-
-    /**
-     *  Event for changes to the radio power state.
-     *
-     *  @see RadioPowerStateChangedListener#onRadioPowerStateChanged
-     *
-     *  @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24;
-
-    /**
-     * Event for changes to emergency number list based on all active subscriptions.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
-     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     *  @see EmergencyNumberListChangedListener#onEmergencyNumberListChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25;
-
-    /**
-     * Event for call disconnect causes which contains {@link DisconnectCause} and
-     * {@link PreciseDisconnectCause}.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     *  @see CallDisconnectCauseChangedListener#onCallDisconnectCauseChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26;
-
-    /**
-     * Event for changes to the call attributes of a currently active call.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see CallAttributesChangedListener#onCallAttributesChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27;
-
-    /**
-     * Event for IMS call disconnect causes which contains
-     * {@link android.telephony.ims.ImsReasonInfo}
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see ImsCallDisconnectCauseChangedListener#onImsCallDisconnectCauseChanged(ImsReasonInfo)
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28;
-
-    /**
-     * Event for the emergency number placed from an outgoing call.
-     *
-     * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-    public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29;
-
-    /**
-     * Event for the emergency number placed from an outgoing SMS.
-     *
-     * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-    public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30;
-
-    /**
-     * Event for registration failures.
-     *
-     * Event for indications that a registration procedure has failed in either the CS or PS
-     * domain. This indication does not necessarily indicate a change of service state, which should
-     * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
-     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
-     * of whether the calling app has carrier privileges.
-     *
-     * @see RegistrationFailedListener#onRegistrationFailed
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            Manifest.permission.READ_PRECISE_PHONE_STATE,
-            Manifest.permission.ACCESS_FINE_LOCATION
-    })
-    public static final int EVENT_REGISTRATION_FAILURE = 31;
-
-    /**
-     * Event for Barring Information for the current registered / camped cell.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
-     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
-     * of whether the calling app has carrier privileges.
-     *
-     * @see BarringInfoChangedListener#onBarringInfoChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            Manifest.permission.READ_PRECISE_PHONE_STATE,
-            Manifest.permission.ACCESS_FINE_LOCATION
-    })
-    public static final int EVENT_BARRING_INFO_CHANGED = 32;
-
-    /**
-     * Event for changes to the physical channel configuration.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see PhysicalChannelConfigChangedListener#onPhysicalChannelConfigChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33;
-
-
-    /**
-     * Event for changes to the data enabled.
-     *
-     * Event for indications that the enabled status of current data has changed.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-     * or the calling app has carrier privileges
-     * (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see DataEnabledChangedListener#onDataEnabledChanged
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-    public static final int EVENT_DATA_ENABLED_CHANGED = 34;
-
-    /**
-     * Event for changes to allowed network list based on all active subscriptions.
-     *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
-     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @hide
-     * @see AllowedNetworkTypesChangedListener#onAllowedNetworkTypesChanged
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35;
-
-    /** @hide */
-    @IntDef(prefix = { "EVENT_" }, value = {
-            EVENT_SERVICE_STATE_CHANGED,
-            EVENT_SIGNAL_STRENGTH_CHANGED,
-            EVENT_MESSAGE_WAITING_INDICATOR_CHANGED,
-            EVENT_CALL_FORWARDING_INDICATOR_CHANGED,
-            EVENT_CELL_LOCATION_CHANGED,
-            EVENT_CALL_STATE_CHANGED,
-            EVENT_DATA_CONNECTION_STATE_CHANGED,
-            EVENT_DATA_ACTIVITY_CHANGED,
-            EVENT_SIGNAL_STRENGTHS_CHANGED,
-            EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED,
-            EVENT_CELL_INFO_CHANGED,
-            EVENT_PRECISE_CALL_STATE_CHANGED,
-            EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED,
-            EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED,
-            EVENT_OEM_HOOK_RAW,
-            EVENT_SRVCC_STATE_CHANGED,
-            EVENT_CARRIER_NETWORK_CHANGED,
-            EVENT_VOICE_ACTIVATION_STATE_CHANGED,
-            EVENT_DATA_ACTIVATION_STATE_CHANGED,
-            EVENT_USER_MOBILE_DATA_STATE_CHANGED,
-            EVENT_DISPLAY_INFO_CHANGED,
-            EVENT_PHONE_CAPABILITY_CHANGED,
-            EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED,
-            EVENT_RADIO_POWER_STATE_CHANGED,
-            EVENT_EMERGENCY_NUMBER_LIST_CHANGED,
-            EVENT_CALL_DISCONNECT_CAUSE_CHANGED,
-            EVENT_CALL_ATTRIBUTES_CHANGED,
-            EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED,
-            EVENT_OUTGOING_EMERGENCY_CALL,
-            EVENT_OUTGOING_EMERGENCY_SMS,
-            EVENT_REGISTRATION_FAILURE,
-            EVENT_BARRING_INFO_CHANGED,
-            EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED,
-            EVENT_DATA_ENABLED_CHANGED,
-            EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TelephonyEvent {}
-
     /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -1085,19 +515,16 @@
     /**
      * @hide
      */
-    //TODO: The maxTargetSdk should be S if the build time tool updates it.
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     @UnsupportedAppUsage(
             maxTargetSdk = Build.VERSION_CODES.R,
-            publicAlternatives = "Use {@code TelephonyManager#registerPhoneStateListener(" +
-                    "Executor, PhoneStateListener)} instead")
-    public IPhoneStateListener callback;
+            publicAlternatives = "Use {@code TelephonyManager#registerTelephonyCallback(" +
+                    "Executor, TelephonyCallback)} instead")
+    public final IPhoneStateListener callback;
 
     /**
      * Create a PhoneStateListener for the Phone with the default subscription.
-     * If this is created for use with deprecated API
-     * {@link TelephonyManager#listen(PhoneStateListener, int)}, then this class requires
-     * Looper.myLooper() not return null.
+     * This class requires Looper.myLooper() not return null.
      */
     public PhoneStateListener() {
         this(null, Looper.myLooper());
@@ -1135,10 +562,7 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public PhoneStateListener(Integer subId, Looper looper) {
-        if (looper != null) {
-            setExecutor(new HandlerExecutor(new Handler(looper)));
-        }
-        mSubId = subId;
+        this(subId, new HandlerExecutor(new Handler(looper)));
         if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
                 >= Build.VERSION_CODES.Q) {
             throw new IllegalArgumentException("PhoneStateListener with subId: "
@@ -1153,783 +577,18 @@
      * The Executor must not be null.
      *
      * @param executor a non-null Executor that will execute callbacks for the PhoneStateListener.
-     * @deprecated Use
-     * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} instead.
      */
     @Deprecated
     public PhoneStateListener(@NonNull Executor executor) {
-        setExecutor(executor);
-        mSubId = null;
+        this(null, executor);
     }
 
-    private @NonNull Executor mExecutor;
-
-    /**
-     * @hide
-     */
-    public void setExecutor(@NonNull @CallbackExecutor Executor executor) {
-        if (executor == null) {
+    private PhoneStateListener(Integer subId, Executor e) {
+        if (e == null) {
             throw new IllegalArgumentException("PhoneStateListener Executor must be non-null");
         }
-        mExecutor = executor;
-        callback = new IPhoneStateListenerStub(this, mExecutor);
-    }
-
-    /**
-     * @hide
-     */
-    public boolean isExecutorSet() {
-        return mExecutor != null;
-    }
-
-    /**
-     * Interface for service state listener.
-     */
-    public interface ServiceStateChangedListener {
-        /**
-         * Callback invoked when device service state changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * The instance of {@link ServiceState} passed as an argument here will have various
-         * levels of location information stripped from it depending on the location permissions
-         * that your app holds.
-         * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
-         * receive all the information in {@link ServiceState}.
-         *
-         * @see ServiceState#STATE_EMERGENCY_ONLY
-         * @see ServiceState#STATE_IN_SERVICE
-         * @see ServiceState#STATE_OUT_OF_SERVICE
-         * @see ServiceState#STATE_POWER_OFF
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onServiceStateChanged(@NonNull ServiceState serviceState);
-    }
-
-    /**
-     * Interface for message waiting indicator listener.
-     */
-    public interface MessageWaitingIndicatorChangedListener {
-        /**
-         * Callback invoked when the message-waiting indicator changes on the registered
-         * subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onMessageWaitingIndicatorChanged(boolean mwi);
-    }
-
-    /**
-     * Interface for call-forwarding indicator listener.
-     */
-    public interface CallForwardingIndicatorChangedListener {
-        /**
-         * Callback invoked when the call-forwarding indicator changes on the registered
-         * subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onCallForwardingIndicatorChanged(boolean cfi);
-    }
-
-    /**
-     * Interface for device cell location listener.
-     */
-    public interface CellLocationChangedListener {
-        /**
-         * Callback invoked when device cell location changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-        public void onCellLocationChanged(@NonNull CellLocation location);
-    }
-
-    /**
-     * Interface for call state listener.
-     */
-    public interface CallStateChangedListener {
-        /**
-         * Callback invoked when device call state changes.
-         * <p>
-         * Reports the state of Telephony (mobile) calls on the device for the registered s
-         * ubscription.
-         * <p>
-         * Note: the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         * <p>
-         * Note: The state returned here may differ from that returned by
-         * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that
-         * calling {@link TelephonyManager#getCallState()} from within this callback may return a
-         * different state than the callback reports.
-         *
-         * @param state call state
-         * @param phoneNumber call phone number. If application does not have
-         * {@link android.Manifest.permission#READ_CALL_LOG} permission or carrier
-         * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
-         * passed as an argument.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
-        public void onCallStateChanged(@CallState int state, @Nullable String phoneNumber);
-    }
-
-    /**
-     * Interface for data connection state listener.
-     */
-    public interface DataConnectionStateChangedListener {
-        /**
-         * Callback invoked when connection state changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @see TelephonyManager#DATA_DISCONNECTED
-         * @see TelephonyManager#DATA_CONNECTING
-         * @see TelephonyManager#DATA_CONNECTED
-         * @see TelephonyManager#DATA_SUSPENDED
-         *
-         * @param state is the current state of data connection.
-         * @param networkType is the current network type of data connection.
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataConnectionStateChanged(@DataState int state,
-                                                 @NetworkType int networkType);
-    }
-
-    /**
-     * Interface for data activity state listener.
-     */
-    public interface DataActivityListener {
-        /**
-         * Callback invoked when data activity state changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @see TelephonyManager#DATA_ACTIVITY_NONE
-         * @see TelephonyManager#DATA_ACTIVITY_IN
-         * @see TelephonyManager#DATA_ACTIVITY_OUT
-         * @see TelephonyManager#DATA_ACTIVITY_INOUT
-         * @see TelephonyManager#DATA_ACTIVITY_DORMANT
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataActivity(@DataActivityType int direction);
-    }
-
-    /**
-     * Interface for network signal strengths listener.
-     */
-    public interface SignalStrengthsChangedListener {
-        /**
-         * Callback invoked when network signal strengths changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
-    }
-
-    /**
-     * Interface for network signal strengths listener which always reported from modem.
-     */
-    public interface AlwaysReportedSignalStrengthChangedListener {
-        /**
-         * Callback always invoked from modem when network signal strengths changes on the
-         * registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
-        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
-    }
-
-    /**
-     * Interface for cell info listener.
-     */
-    public interface CellInfoChangedListener {
-        /**
-         * Callback invoked when a observed cell info has changed or new cells have been added
-         * or removed on the registered subscription.
-         * Note, the registration subscription ID s from {@link TelephonyManager} object
-         * which registersPhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param cellInfo is the list of currently visible cells.
-         */
-        @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-        public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo);
-    }
-
-    /**
-     * Interface for precise device call state listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface PreciseCallStateChangedListener {
-        /**
-         * Callback invoked when precise device call state changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param callState {@link PreciseCallState}
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPreciseCallStateChanged(@NonNull PreciseCallState callState);
-    }
-
-    /**
-     * Interface for call disconnect cause listener.
-     */
-    public interface CallDisconnectCauseChangedListener {
-        /**
-         * Callback invoked when call disconnect cause changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param disconnectCause {@link DisconnectCause}.
-         * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause,
-                @PreciseDisconnectCauses int preciseDisconnectCause);
-    }
-
-    /**
-     * Interface for IMS call disconnect cause listener.
-     */
-    public interface ImsCallDisconnectCauseChangedListener {
-        /**
-         * Callback invoked when IMS call disconnect cause changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
-         *
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo);
-    }
-
-    /**
-     * Interface for precise data connection state listener.
-     */
-    public interface PreciseDataConnectionStateChangedListener {
-        /**
-         * Callback providing update about the default/internet data connection on the registered
-         * subscription.
-         *
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-         * or the calling app has carrier privileges
-         * (see {@link TelephonyManager#hasCarrierPrivileges}).
-         *
-         * @param dataConnectionState {@link PreciseDataConnectionState}
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPreciseDataConnectionStateChanged(
-                @NonNull PreciseDataConnectionState dataConnectionState);
-    }
-
-    /**
-     * Interface for Single Radio Voice Call Continuity listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface SrvccStateChangedListener {
-        /**
-         * Callback invoked when there has been a change in the Single Radio Voice Call Continuity
-         * (SRVCC) state for the currently active call on the registered subscription.
-         *
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onSrvccStateChanged(@SrvccState int srvccState);
-    }
-
-    /**
-     * Interface for SIM voice activation state listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface VoiceActivationStateChangedListener {
-        /**
-         * Callback invoked when the SIM voice activation state has changed on the registered
-         * subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param state is the current SIM voice activation state
-         */
-        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onVoiceActivationStateChanged(@SimActivationState int state);
-
-    }
-
-    /**
-     * Interface for SIM data activation state listener.
-     */
-    public interface DataActivationStateChangedListener {
-        /**
-         * Callback invoked when the SIM data activation state has changed on the registered
-         * subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param state is the current SIM data activation state
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataActivationStateChanged(@SimActivationState int state);
-    }
-
-    /**
-     * Interface for user mobile data state listener.
-     */
-    public interface UserMobileDataStateChangedListener {
-        /**
-         * Callback invoked when the user mobile data state has changed on the registered
-         * subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param enabled indicates whether the current user mobile data state is enabled or
-         *                disabled.
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onUserMobileDataStateChanged(boolean enabled);
-    }
-
-    /**
-     * Interface for display info listener.
-     */
-    public interface DisplayInfoChangedListener {
-        /**
-         * Callback invoked when the display info has changed on the registered subscription.
-         * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user
-         * based on carrier policy.
-         *
-         * @param telephonyDisplayInfo The display information.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
-    }
-
-    /**
-     * Interface for the current emergency number list listener.
-     */
-    public interface EmergencyNumberListChangedListener {
-        /**
-         * Callback invoked when the current emergency number list has changed on the registered
-         * subscription.
-         *
-         * Note, the registered subscription is associated with {@link TelephonyManager} object
-         * on which
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}
-         * was called.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * given subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param emergencyNumberList Map associating all active subscriptions on the device with
-         *                            the list of emergency numbers originating from that
-         *                            subscription.
-         *                            If there are no active subscriptions, the map will contain a
-         *                            single entry with
-         *                            {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as
-         *                            the key and a list of emergency numbers as the value. If no
-         *                            emergency number information is available, the value will be
-         *                            empty.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onEmergencyNumberListChanged(
-                @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList);
-    }
-
-    /**
-     * Interface for outgoing emergency call listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface OutgoingEmergencyCallListener {
-        /**
-         * Callback invoked when an outgoing call is placed to an emergency number.
-         *
-         * This method will be called when an emergency call is placed on any subscription
-         * (including the no-SIM case), regardless of which subscription this listener was
-         * registered on.
-         *
-         * The default implementation of this method calls
-         * {@link #onOutgoingEmergencyCall(EmergencyNumber)} for backwards compatibility purposes.
-         * Do not call {@code super(...)} from within your implementation unless you want
-         * {@link #onOutgoingEmergencyCall(EmergencyNumber)} to be called as well.
-         *
-         * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was
-         *                              placed to.
-         * @param subscriptionId The subscription ID used to place the emergency call. If the
-         *                       emergency call was placed without a valid subscription
-         *                       (e.g. when there are no SIM cards in the device), this will be
-         *                       equal to {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
-         */
-        @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-        public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
-                                     int subscriptionId);
-    }
-
-    /**
-     * Interface for outgoing emergency sms listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface OutgoingEmergencySmsListener {
-        /**
-         * Smsback invoked when an outgoing sms is sent to an emergency number.
-         *
-         * This method will be called when an emergency sms is sent on any subscription,
-         * regardless of which subscription this listener was registered on.
-         *
-         * The default implementation of this method calls
-         * {@link #onOutgoingEmergencySms(EmergencyNumber)} for backwards compatibility purposes. Do
-         * not call {@code super(...)} from within your implementation unless you want
-         * {@link #onOutgoingEmergencySms(EmergencyNumber)} to be called as well.
-         *
-         * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
-         * @param subscriptionId The subscription ID used to send the emergency sms.
-         */
-        @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-        public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
-                                    int subscriptionId);
-    }
-
-    /**
-     * Interface for phone capability listener.
-     * @hide
-     */
-    @SystemApi
-    public interface PhoneCapabilityChangedListener {
-        /**
-         * Callback invoked when phone capability changes.
-         * Note, this callback triggers regardless of registered subscription.
-         *
-         * @param capability the new phone capability
-         */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability);
-    }
-
-    /**
-     * Interface for active data subscription ID listener.
-     */
-    public interface ActiveDataSubscriptionIdChangedListener {
-        /**
-         * Callback invoked when active data subscription ID changes.
-         * Note, this callback triggers regardless of registered subscription.
-         *
-         * @param subId current subscription used to setup Cellular Internet data.
-         *              For example, it could be the current active opportunistic subscription
-         *              in use, or the subscription user selected as default data subscription in
-         *              DSDS mode.
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onActiveDataSubscriptionIdChanged(int subId);
-    }
-
-    /**
-     * Interface for modem radio power state listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface RadioPowerStateChangedListener {
-        /**
-         * Callback invoked when modem radio power state changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param state the modem radio power state
-         */
-        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onRadioPowerStateChanged(@RadioPowerState int state);
-    }
-
-    /**
-     * Interface for carrier network listener.
-     */
-    public interface CarrierNetworkChangeListener {
-        /**
-         * Callback invoked when telephony has received notice from a carrier
-         * app that a network action that could result in connectivity loss
-         * has been requested by an app using
-         * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)}
-         *
-         * This is optional and is only used to allow the system to provide alternative UI while
-         * telephony is performing an action that may result in intentional, temporary network
-         * lack of connectivity.
-         *
-         * Note, this callback is pinned to the registered subscription and will be invoked when
-         * the notifying carrier app has carrier privilege rule on the registered
-         * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges}
-         *
-         * @param active If the carrier network change is or shortly will be active,
-         *               {@code true} indicate that showing alternative UI, {@code false} otherwise.
-         */
-        public void onCarrierNetworkChange(boolean active);
-    }
-
-    /**
-     * Interface for registration failures listener.
-     */
-    public interface RegistrationFailedListener {
-        /**
-         * Report that Registration or a Location/Routing/Tracking Area update has failed.
-         *
-         * <p>Indicate whenever a registration procedure, including a location, routing, or tracking
-         * area update fails. This includes procedures that do not necessarily result in a change of
-         * the modem's registration status. If the modem's registration status changes, that is
-         * reflected in the onNetworkStateChanged() and subsequent
-         * get{Voice/Data}RegistrationState().
-         *
-         * <p>Because registration failures are ephemeral, this callback is not sticky.
-         * Registrants will not receive the most recent past value when registering.
-         *
-         * @param cellIdentity the CellIdentity, which must include the globally unique identifier
-         *        for the cell (for example, all components of the CGI or ECGI).
-         * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the
-         *         cell that was chosen for the failed registration attempt.
-         * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
-         * @param causeCode the primary failure cause code of the procedure.
-         *        For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
-         *        For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
-         *        For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
-         *        For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
-         *        Integer.MAX_VALUE if this value is unused.
-         * @param additionalCauseCode the cause code of any secondary/combined procedure
-         *                            if appropriate. For UMTS, if a combined attach succeeds for
-         *                            PS only, then the GMM cause code shall be included as an
-         *                            additionalCauseCode. For LTE (ESM), cause codes are in
-         *                            TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
-         */
-        @RequiresPermission(allOf = {
-                Manifest.permission.READ_PRECISE_PHONE_STATE,
-                Manifest.permission.ACCESS_FINE_LOCATION
-        })
-        public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
-                                         @NonNull String chosenPlmn, @Domain int domain,
-                                         int causeCode, int additionalCauseCode);
-    }
-
-    /**
-     * Interface for the current allowed network type list listener. This list involves values of
-     * allowed network type for each of reasons.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface AllowedNetworkTypesChangedListener {
-        /**
-         * Callback invoked when the current allowed network type list has changed on the
-         * registered subscription.
-         * Note, the registered subscription is associated with {@link TelephonyManager} object
-         * on which
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}
-         * was called.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * given subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param allowedNetworkTypesList Map associating all allowed network type reasons
-         * ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER},
-         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER},
-         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}, and
-         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}) with reason's allowed
-         * network type values.
-         * For example:
-         * map{{TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER, long type value},
-         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER, long type value},
-         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER, long type value},
-         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}}
-         */
-        @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        void onAllowedNetworkTypesChanged(
-                @NonNull Map<Integer, Long> allowedNetworkTypesList);
-    }
-
-    /**
-     * Interface for call attributes listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface CallAttributesChangedListener {
-        /**
-         * Callback invoked when the call attributes changes on the registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers PhoneStateListener by
-         * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         *
-         * @param callAttributes the call attributes
-         */
-        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-        void onCallAttributesChanged(@NonNull CallAttributes callAttributes);
-    }
-
-    /**
-     * Interface for barring information listener.
-     */
-    public interface BarringInfoChangedListener {
-        /**
-         * Report updated barring information for the current camped/registered cell.
-         *
-         * <p>Barring info is provided for all services applicable to the current camped/registered
-         * cell, for the registered PLMN and current access class/access category.
-         *
-         * @param barringInfo for all services on the current cell.
-         * @see android.telephony.BarringInfo
-         */
-        @RequiresPermission(allOf = {
-                Manifest.permission.READ_PRECISE_PHONE_STATE,
-                Manifest.permission.ACCESS_FINE_LOCATION
-        })
-        public void onBarringInfoChanged(@NonNull BarringInfo barringInfo);
-    }
-
-    /**
-     * Interface for current physical channel configuration listener.
-     * @hide
-     */
-    @SystemApi
-    public interface PhysicalChannelConfigChangedListener {
-        /**
-         * Callback invoked when the current physical channel configuration has changed
-         *
-         * @param configs List of the current {@link PhysicalChannelConfig}s
-         */
-        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs);
-    }
-
-    /**
-     * Interface for data enabled listener.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface DataEnabledChangedListener {
-        /**
-         * Callback invoked when the data enabled changes.
-         *
-         * @param enabled {@code true} if data is enabled, otherwise disabled.
-         * @param reason Reason for data enabled/disabled.
-         *               See {@link TelephonyManager.DataEnabledReason}.
-         */
-        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onDataEnabledChanged(boolean enabled,
-                                  @DataEnabledReason int reason);
+        mSubId = subId;
+        callback = new IPhoneStateListenerStub(this, e);
     }
 
     /**
@@ -1950,7 +609,9 @@
      * @see ServiceState#STATE_IN_SERVICE
      * @see ServiceState#STATE_OUT_OF_SERVICE
      * @see ServiceState#STATE_POWER_OFF
+     * @deprecated Use {@link TelephonyCallback.ServiceStateListener} instead.
      */
+    @Deprecated
     public void onServiceStateChanged(ServiceState serviceState) {
         // default implementation empty
     }
@@ -1983,7 +644,10 @@
      * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
      * subId. Otherwise, this callback applies to
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead.
      */
+    @Deprecated
     public void onMessageWaitingIndicatorChanged(boolean mwi) {
         // default implementation empty
     }
@@ -1996,7 +660,10 @@
      * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
      * subId. Otherwise, this callback applies to
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead.
      */
+    @Deprecated
     public void onCallForwardingIndicatorChanged(boolean cfi) {
         // default implementation empty
     }
@@ -2009,7 +676,10 @@
      * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
      * subId. Otherwise, this callback applies to
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead.
      */
+    @Deprecated
     public void onCellLocationChanged(CellLocation location) {
         // default implementation empty
     }
@@ -2036,7 +706,10 @@
      * {@link android.Manifest.permission#READ_CALL_LOG READ_CALL_LOG} permission or carrier
      * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
      * passed as an argument.
+     *
+     * @deprecated Use {@link TelephonyCallback.CallStateListener} instead.
      */
+    @Deprecated
     public void onCallStateChanged(@CallState int state, String phoneNumber) {
         // default implementation empty
     }
@@ -2054,14 +727,19 @@
      * @see TelephonyManager#DATA_CONNECTING
      * @see TelephonyManager#DATA_CONNECTED
      * @see TelephonyManager#DATA_SUSPENDED
+     * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead.
      */
+    @Deprecated
     public void onDataConnectionStateChanged(int state) {
         // default implementation empty
     }
 
     /**
      * same as above, but with the network type.  Both called.
+     *
+     * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead.
      */
+    @Deprecated
     public void onDataConnectionStateChanged(int state, int networkType) {
         // default implementation empty
     }
@@ -2080,7 +758,9 @@
      * @see TelephonyManager#DATA_ACTIVITY_OUT
      * @see TelephonyManager#DATA_ACTIVITY_INOUT
      * @see TelephonyManager#DATA_ACTIVITY_DORMANT
+     * @deprecated Use {@link TelephonyCallback.DataActivityListener} instead.
      */
+    @Deprecated
     public void onDataActivity(int direction) {
         // default implementation empty
     }
@@ -2093,7 +773,10 @@
      * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
      * subId. Otherwise, this callback applies to
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead.
      */
+    @Deprecated
     public void onSignalStrengthsChanged(SignalStrength signalStrength) {
         // default implementation empty
     }
@@ -2109,7 +792,9 @@
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
      * @param cellInfo is the list of currently visible cells.
+     * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead.
      */
+    @Deprecated
     public void onCellInfoChanged(List<CellInfo> cellInfo) {
         // default implementation empty
     }
@@ -2122,11 +807,14 @@
      * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
      * subId. Otherwise, this callback applies to
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
      * @param callState {@link PreciseCallState}
      * @hide
+     * @deprecated Use {@link TelephonyCallback.PreciseCallStateListener} instead.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
     @SystemApi
+    @Deprecated
     public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) {
         // default implementation empty
     }
@@ -2142,9 +830,10 @@
      *
      * @param disconnectCause {@link DisconnectCause}.
      * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
-     *
+     * @deprecated Use {@link TelephonyCallback.CallDisconnectCauseListener} instead.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @Deprecated
     public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause,
             @PreciseDisconnectCauses int preciseDisconnectCause) {
         // default implementation empty
@@ -2160,9 +849,10 @@
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
      * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
-     *
+     * @deprecated Use {@link TelephonyCallback.ImsCallDisconnectCauseListener} instead.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @Deprecated
     public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) {
         // default implementation empty
     }
@@ -2183,8 +873,10 @@
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @param dataConnectionState {@link PreciseDataConnectionState}
+     * @deprecated Use {@link TelephonyCallback.PreciseDataConnectionStateListener} instead.
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @Deprecated
     public void onPreciseDataConnectionStateChanged(
             @NonNull PreciseDataConnectionState dataConnectionState) {
         // default implementation empty
@@ -2200,8 +892,10 @@
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
      * @hide
+     * @deprecated Use {@link TelephonyManager#requestModemActivityInfo}
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @Deprecated
     public void onDataConnectionRealTimeInfoChanged(
             DataConnectionRealTimeInfo dcRtInfo) {
         // default implementation empty
@@ -2219,11 +913,12 @@
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
      * @hide
+     * @deprecated Use {@link TelephonyCallback.SrvccStateListener} instead.
      */
     @SystemApi
+    @Deprecated
     public void onSrvccStateChanged(@SrvccState int srvccState) {
         // default implementation empty
-
     }
 
     /**
@@ -2238,8 +933,10 @@
      *
      * @param state is the current SIM voice activation state
      * @hide
+     * @deprecated Use {@link TelephonyCallback.VoiceActivationStateListener} instead.
      */
     @SystemApi
+    @Deprecated
     public void onVoiceActivationStateChanged(@SimActivationState int state) {
         // default implementation empty
     }
@@ -2256,7 +953,9 @@
      *
      * @param state is the current SIM data activation state
      * @hide
+     * @deprecated Use {@link TelephonyCallback.DataActivationStateListener} instead.
      */
+    @Deprecated
     public void onDataActivationStateChanged(@SimActivationState int state) {
         // default implementation empty
     }
@@ -2271,7 +970,9 @@
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
      * @param enabled indicates whether the current user mobile data state is enabled or disabled.
+     * @deprecated Use {@link TelephonyCallback.UserMobileDataStateListener} instead.
      */
+    @Deprecated
     public void onUserMobileDataStateChanged(boolean enabled) {
         // default implementation empty
     }
@@ -2285,8 +986,10 @@
      * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @param telephonyDisplayInfo The display information.
+     * @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @Deprecated
     public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
         // default implementation empty
     }
@@ -2309,7 +1012,9 @@
      *                            {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as
      *                            the key and a list of emergency numbers as the value. If no
      *                            emergency number information is available, the value will be null.
+     * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead.
      */
+    @Deprecated
     public void onEmergencyNumberListChanged(
             @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
         // default implementation empty
@@ -2322,7 +1027,6 @@
      * the no-SIM case), regardless of which subscription this listener was registered on.
      *
      * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was placed to.
-     *
      * @deprecated Use {@link #onOutgoingEmergencyCall(EmergencyNumber, int)}.
      * @hide
      */
@@ -2349,8 +1053,10 @@
      *                       are no SIM cards in the device), this will be equal to
      *                       {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
      * @hide
+     * @deprecated Use {@link TelephonyCallback.OutgoingEmergencyCallListener} instead.
      */
     @SystemApi
+    @Deprecated
     public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
             int subscriptionId) {
         // Default implementation for backwards compatibility
@@ -2365,6 +1071,7 @@
      *
      * @deprecated Use {@link #onOutgoingEmergencySms(EmergencyNumber, int)}.
      * @hide
+     * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead.
      */
     @SystemApi
     @Deprecated
@@ -2386,8 +1093,10 @@
      * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
      * @param subscriptionId The subscription ID used to send the emergency sms.
      * @hide
+     * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead.
      */
     @SystemApi
+    @Deprecated
     public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
             int subscriptionId) {
         // Default implementation for backwards compatibility
@@ -2397,8 +1106,7 @@
     /**
      * Callback invoked when OEM hook raw event is received on the registered subscription.
      * Note, the registration subId comes from {@link TelephonyManager} object which registers
-     * PhoneStateListener by
-     * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
      * If this TelephonyManager object was created with
      * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
      * subId. Otherwise, this callback applies to
@@ -2407,8 +1115,10 @@
      * Requires the READ_PRIVILEGED_PHONE_STATE permission.
      * @param rawData is the byte array of the OEM hook raw data.
      * @hide
+     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @Deprecated
     public void onOemHookRawEvent(byte[] rawData) {
         // default implementation empty
     }
@@ -2419,7 +1129,9 @@
      *
      * @param capability the new phone capability
      * @hide
+     * @deprecated Use {@link TelephonyCallback.PhoneCapabilityListener} instead.
      */
+    @Deprecated
     public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability) {
         // default implementation empty
     }
@@ -2432,7 +1144,9 @@
      * @param subId current subscription used to setup Cellular Internet data.
      *              For example, it could be the current active opportunistic subscription in use,
      *              or the subscription user selected as default data subscription in DSDS mode.
+     * @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead.
      */
+    @Deprecated
     public void onActiveDataSubscriptionIdChanged(int subId) {
         // default implementation empty
     }
@@ -2449,8 +1163,10 @@
      * Requires the READ_PRECISE_PHONE_STATE permission.
      * @param callAttributes the call attributes
      * @hide
+     * @deprecated Use {@link TelephonyCallback.CallAttributesListener} instead.
      */
     @SystemApi
+    @Deprecated
     public void onCallAttributesChanged(@NonNull CallAttributes callAttributes) {
         // default implementation empty
     }
@@ -2468,8 +1184,10 @@
      *
      * @param state the modem radio power state
      * @hide
+     * @deprecated Use {@link TelephonyCallback.RadioPowerStateListener} instead.
      */
     @SystemApi
+    @Deprecated
     public void onRadioPowerStateChanged(@RadioPowerState int state) {
         // default implementation empty
     }
@@ -2487,9 +1205,10 @@
      * @param active Whether the carrier network change is or shortly
      *               will be active. This value is true to indicate
      *               showing alternative UI and false to stop.
-     *
      * @hide
+     * @deprecated Use {@link TelephonyCallback.CarrierNetworkListener} instead.
      */
+    @Deprecated
     public void onCarrierNetworkChange(boolean active) {
         // default implementation empty
     }
@@ -2520,7 +1239,9 @@
      *        For UMTS, if a combined attach succeeds for PS only, then the GMM cause code shall be
      *        included as an additionalCauseCode. For LTE (ESM), cause codes are in
      *        TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+     * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead.
      */
+    @Deprecated
     public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
             int domain, int causeCode, int additionalCauseCode) {
         // default implementation empty
@@ -2533,9 +1254,10 @@
      * cell, for the registered PLMN and current access class/access category.
      *
      * @param barringInfo for all services on the current cell.
-     *
      * @see android.telephony.BarringInfo
+     * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead.
      */
+    @Deprecated
     public void onBarringInfoChanged(@NonNull BarringInfo barringInfo) {
         // default implementation empty
     }
@@ -2831,7 +1553,7 @@
 
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onRegistrationFailed(
-                                cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
+                            cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
             // default implementation empty
         }
 
@@ -2844,33 +1566,15 @@
         }
 
         public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) {
-            PhysicalChannelConfigChangedListener listener =
-                    (PhysicalChannelConfigChangedListener) mPhoneStateListenerWeakRef.get();
-            if (listener == null) return;
-
-            Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged(
-                            configs)));
+            // default implementation empty
         }
 
         public void onDataEnabledChanged(boolean enabled, @DataEnabledReason int reason) {
-            DataEnabledChangedListener listener =
-                    (DataEnabledChangedListener) mPhoneStateListenerWeakRef.get();
-            if (listener == null) return;
-
-            Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(() -> listener.onDataEnabledChanged(
-                            enabled, reason)));
+            // default implementation empty
         }
 
         public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
-            AllowedNetworkTypesChangedListener listener =
-                    (AllowedNetworkTypesChangedListener) mPhoneStateListenerWeakRef.get();
-            if (listener == null) return;
-
-            Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(
-                            () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList)));
+            // default implementation empty
         }
     }
 
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
new file mode 100644
index 0000000..a2584cae
--- /dev/null
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -0,0 +1,1710 @@
+/*
+ * 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;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Binder;
+import android.os.Build;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsReasonInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IPhoneStateListener;
+
+import dalvik.system.VMRuntime;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * A callback class for monitoring changes in specific telephony states
+ * on the device, including service state, signal strength, message
+ * waiting indicator (voicemail), and others.
+ * <p>
+ * To register a callback, use a {@link TelephonyCallback} which implements interfaces regarding
+ * EVENT_*. For example,
+ * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+ * {@link TelephonyCallback.ServiceStateListener}.
+ * <p>
+ * Then override the methods for the state that you wish to receive updates for, and
+ * pass the executor and your TelephonyCallback object to
+ * {@link TelephonyManager#registerTelephonyCallback}.
+ * Methods are called when the state changes, as well as once on initial registration.
+ * <p>
+ * Note that access to some telephony information is
+ * permission-protected. Your application won't receive updates for protected
+ * information unless it has the appropriate permissions declared in
+ * its manifest file. Where permissions apply, they are noted in the
+ * appropriate sub-interfaces.
+ */
+public class TelephonyCallback {
+
+    /**
+     * Experiment flag to set the per-pid registration limit for TelephonyCallback
+     *
+     * Limit on registrations of {@link TelephonyCallback}s on a per-pid basis. When this limit is
+     * exceeded, any calls to {@link TelephonyManager#registerTelephonyCallback} will fail with an
+     * {@link IllegalStateException}.
+     *
+     * {@link android.os.Process#PHONE_UID}, {@link android.os.Process#SYSTEM_UID}, and the uid that
+     * TelephonyRegistry runs under are exempt from this limit.
+     *
+     * If the value of the flag is less than 1, enforcement of the limit will be disabled.
+     * @hide
+     */
+    public static final String FLAG_PER_PID_REGISTRATION_LIMIT =
+            "phone_state_listener_per_pid_registration_limit";
+
+    /**
+     * Default value for the per-pid registration limit.
+     * See {@link #FLAG_PER_PID_REGISTRATION_LIMIT}.
+     * @hide
+     */
+    public static final int DEFAULT_PER_PID_REGISTRATION_LIMIT = 50;
+
+    /**
+     * This change enables a limit on the number of {@link TelephonyCallback} objects any process
+     * may register via {@link TelephonyManager#registerTelephonyCallback}. The default limit is 50,
+     * which may change via remote device config updates.
+     *
+     * This limit is enforced via an {@link IllegalStateException} thrown from
+     * {@link TelephonyManager#registerTelephonyCallback} when the offending process attempts to
+     * register one too many callbacks.
+     *
+     * @hide
+     */
+    @ChangeId
+    public static final long PHONE_STATE_LISTENER_LIMIT_CHANGE_ID = 150880553L;
+
+    /**
+     * Event for changes to the network service state (cellular).
+     *
+     * @hide
+     * @see ServiceStateListener#onServiceStateChanged
+     * @see ServiceState
+     */
+    @SystemApi
+    public static final int EVENT_SERVICE_STATE_CHANGED = 1;
+
+    /**
+     * Event for changes to the network signal strength (cellular).
+     *
+     * @hide
+     * @see SignalStrengthsListener#onSignalStrengthsChanged
+     */
+    @SystemApi
+    public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2;
+
+    /**
+     * Event for changes to the message-waiting indicator.
+     * <p>
+     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
+     * the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     * <p>
+     * Example: The status bar uses this to determine when to display the
+     * voicemail icon.
+     *
+     * @hide
+     * @see MessageWaitingIndicatorListener#onMessageWaitingIndicatorChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3;
+
+    /**
+     * Event for changes to the call-forwarding indicator.
+     * <p>
+     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
+     * the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see CallForwardingIndicatorListener#onCallForwardingIndicatorChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4;
+
+    /**
+     * Event for changes to the device's cell location. Note that
+     * this will result in frequent listeners to the listener.
+     * <p>
+     * If you need regular location updates but want more control over
+     * the update interval or location precision, you can set up a callback
+     * through the {@link android.location.LocationManager location manager}
+     * instead.
+     *
+     * @hide
+     * @see CellLocationListener#onCellLocationChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+    public static final int EVENT_CELL_LOCATION_CHANGED = 5;
+
+    /**
+     * Event for changes to the device call state.
+     *
+     * @hide
+     * @see CallStateListener#onCallStateChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
+    public static final int EVENT_CALL_STATE_CHANGED = 6;
+
+    /**
+     * Event for changes to the data connection state (cellular).
+     *
+     * @hide
+     * @see DataConnectionStateListener#onDataConnectionStateChanged
+     */
+    @SystemApi
+    public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7;
+
+    /**
+     * Event for changes to the direction of data traffic on the data
+     * connection (cellular).
+     * <p>
+     * Example: The status bar uses this to display the appropriate
+     * data-traffic icon.
+     *
+     * @hide
+     * @see DataActivityListener#onDataActivity
+     */
+    @SystemApi
+    public static final int EVENT_DATA_ACTIVITY_CHANGED = 8;
+
+    /**
+     * Event for changes to the network signal strengths (cellular).
+     * <p>
+     * Example: The status bar uses this to control the signal-strength
+     * icon.
+     *
+     * @hide
+     * @see SignalStrengthsListener#onSignalStrengthsChanged
+     */
+    @SystemApi
+    public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9;
+
+    /**
+     * Event for changes of the network signal strengths (cellular) always reported from modem,
+     * even in some situations such as the screen of the device is off.
+     *
+     * @hide
+     * @see AlwaysReportedSignalStrengthListener#onSignalStrengthsChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
+    public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10;
+
+    /**
+     * Event for changes to observed cell info.
+     *
+     * @hide
+     * @see CellInfoListener#onCellInfoChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+    public static final int EVENT_CELL_INFO_CHANGED = 11;
+
+    /**
+     * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
+     * background and foreground calls.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see PreciseCallStateListener#onPreciseCallStateChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12;
+
+    /**
+     * Event for {@link PreciseDataConnectionState} on the data connection (cellular).
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see PreciseDataConnectionStateListener#onPreciseDataConnectionStateChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13;
+
+    /**
+     * Event for real time info for all data connections (cellular)).
+     *
+     * @hide
+     * @see PhoneStateListener#onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
+     * @deprecated Use {@link TelephonyManager#requestModemActivityInfo}
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14;
+
+    /**
+     * Event for OEM hook raw event
+     *
+     * @hide
+     * @see PhoneStateListener#onOemHookRawEvent
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_OEM_HOOK_RAW = 15;
+
+    /**
+     * Event for changes to the SRVCC state of the active call.
+     *
+     * @hide
+     * @see SrvccStateListener#onSrvccStateChanged
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_SRVCC_STATE_CHANGED = 16;
+
+    /**
+     * Event for carrier network changes indicated by a carrier app.
+     *
+     * @hide
+     * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
+     * @see CarrierNetworkListener#onCarrierNetworkChange
+     */
+    @SystemApi
+    public static final int EVENT_CARRIER_NETWORK_CHANGED = 17;
+
+    /**
+     * Event for changes to the sim voice activation state
+     *
+     * @hide
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     * <p>
+     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
+     * fully activated
+     * @see VoiceActivationStateListener#onVoiceActivationStateChanged
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18;
+
+    /**
+     * Event for changes to the sim data activation state
+     *
+     * @hide
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     * <p>
+     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
+     * fully activated
+     * @see DataActivationStateListener#onDataActivationStateChanged
+     */
+    @SystemApi
+    public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19;
+
+    /**
+     * Event for changes to the user mobile data state
+     *
+     * @hide
+     * @see UserMobileDataStateListener#onUserMobileDataStateChanged
+     */
+    @SystemApi
+    public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20;
+
+    /**
+     * Event for display info changed event.
+     *
+     * @hide
+     * @see DisplayInfoListener#onDisplayInfoChanged
+     */
+    @SystemApi
+    public static final int EVENT_DISPLAY_INFO_CHANGED = 21;
+
+    /**
+     * Event for changes to the phone capability.
+     *
+     * @hide
+     * @see PhoneCapabilityListener#onPhoneCapabilityChanged
+     */
+    @SystemApi
+    public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22;
+
+    /**
+     * Event for changes to active data subscription ID. Active data subscription is
+     * the current subscription used to setup Cellular Internet data. For example,
+     * it could be the current active opportunistic subscription in use, or the
+     * subscription user selected as default data subscription in DSDS mode.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see ActiveDataSubscriptionIdListener#onActiveDataSubscriptionIdChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23;
+
+    /**
+     * Event for changes to the radio power state.
+     *
+     * @hide
+     * @see RadioPowerStateListener#onRadioPowerStateChanged
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24;
+
+    /**
+     * Event for changes to emergency number list based on all active subscriptions.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see EmergencyNumberListListener#onEmergencyNumberListChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25;
+
+    /**
+     * Event for call disconnect causes which contains {@link DisconnectCause} and
+     * {@link PreciseDisconnectCause}.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see CallDisconnectCauseListener#onCallDisconnectCauseChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26;
+
+    /**
+     * Event for changes to the call attributes of a currently active call.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see CallAttributesListener#onCallAttributesChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27;
+
+    /**
+     * Event for IMS call disconnect causes which contains
+     * {@link android.telephony.ims.ImsReasonInfo}
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see ImsCallDisconnectCauseListener#onImsCallDisconnectCauseChanged(ImsReasonInfo)
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28;
+
+    /**
+     * Event for the emergency number placed from an outgoing call.
+     *
+     * @hide
+     * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29;
+
+    /**
+     * Event for the emergency number placed from an outgoing SMS.
+     *
+     * @hide
+     * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30;
+
+    /**
+     * Event for registration failures.
+     * <p>
+     * Event for indications that a registration procedure has failed in either the CS or PS
+     * domain. This indication does not necessarily indicate a change of service state, which should
+     * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+     * of whether the calling app has carrier privileges.
+     *
+     * @hide
+     * @see RegistrationFailedListener#onRegistrationFailed
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    public static final int EVENT_REGISTRATION_FAILURE = 31;
+
+    /**
+     * Event for Barring Information for the current registered / camped cell.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+     * of whether the calling app has carrier privileges.
+     *
+     * @hide
+     * @see BarringInfoListener#onBarringInfoChanged
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    public static final int EVENT_BARRING_INFO_CHANGED = 32;
+
+    /**
+     * Event for changes to the physical channel configuration.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see PhysicalChannelConfigListener#onPhysicalChannelConfigChanged
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33;
+
+
+    /**
+     * Event for changes to the data enabled.
+     * <p>
+     * Event for indications that the enabled status of current data has changed.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see DataEnabledListener#onDataEnabledChanged
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_DATA_ENABLED_CHANGED = 34;
+
+    /**
+     * Event for changes to allowed network list based on all active subscriptions.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see AllowedNetworkTypesListener#onAllowedNetworkTypesChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"EVENT_"}, value = {
+            EVENT_SERVICE_STATE_CHANGED,
+            EVENT_SIGNAL_STRENGTH_CHANGED,
+            EVENT_MESSAGE_WAITING_INDICATOR_CHANGED,
+            EVENT_CALL_FORWARDING_INDICATOR_CHANGED,
+            EVENT_CELL_LOCATION_CHANGED,
+            EVENT_CALL_STATE_CHANGED,
+            EVENT_DATA_CONNECTION_STATE_CHANGED,
+            EVENT_DATA_ACTIVITY_CHANGED,
+            EVENT_SIGNAL_STRENGTHS_CHANGED,
+            EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED,
+            EVENT_CELL_INFO_CHANGED,
+            EVENT_PRECISE_CALL_STATE_CHANGED,
+            EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED,
+            EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED,
+            EVENT_OEM_HOOK_RAW,
+            EVENT_SRVCC_STATE_CHANGED,
+            EVENT_CARRIER_NETWORK_CHANGED,
+            EVENT_VOICE_ACTIVATION_STATE_CHANGED,
+            EVENT_DATA_ACTIVATION_STATE_CHANGED,
+            EVENT_USER_MOBILE_DATA_STATE_CHANGED,
+            EVENT_DISPLAY_INFO_CHANGED,
+            EVENT_PHONE_CAPABILITY_CHANGED,
+            EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED,
+            EVENT_RADIO_POWER_STATE_CHANGED,
+            EVENT_EMERGENCY_NUMBER_LIST_CHANGED,
+            EVENT_CALL_DISCONNECT_CAUSE_CHANGED,
+            EVENT_CALL_ATTRIBUTES_CHANGED,
+            EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED,
+            EVENT_OUTGOING_EMERGENCY_CALL,
+            EVENT_OUTGOING_EMERGENCY_SMS,
+            EVENT_REGISTRATION_FAILURE,
+            EVENT_BARRING_INFO_CHANGED,
+            EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED,
+            EVENT_DATA_ENABLED_CHANGED,
+            EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TelephonyEvent {
+    }
+
+    /**
+     * @hide
+     */
+    //TODO: The maxTargetSdk should be S if the build time tool updates it.
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public IPhoneStateListener callback;
+
+    /**
+     * @hide
+     */
+    public void init(@NonNull @CallbackExecutor Executor executor) {
+        if (executor == null) {
+            throw new IllegalArgumentException("TelephonyCallback Executor must be non-null");
+        }
+        callback = new IPhoneStateListenerStub(this, executor);
+    }
+
+    /**
+     * Interface for service state listener.
+     */
+    public interface ServiceStateListener {
+        /**
+         * Callback invoked when device service state changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         * <p>
+         * The instance of {@link ServiceState} passed as an argument here will have various
+         * levels of location information stripped from it depending on the location permissions
+         * that your app holds.
+         * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
+         * receive all the information in {@link ServiceState}.
+         *
+         * @see ServiceState#STATE_EMERGENCY_ONLY
+         * @see ServiceState#STATE_IN_SERVICE
+         * @see ServiceState#STATE_OUT_OF_SERVICE
+         * @see ServiceState#STATE_POWER_OFF
+         */
+        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+        public void onServiceStateChanged(@NonNull ServiceState serviceState);
+    }
+
+    /**
+     * Interface for message waiting indicator listener.
+     */
+    public interface MessageWaitingIndicatorListener {
+        /**
+         * Callback invoked when the message-waiting indicator changes on the registered
+         * subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+        public void onMessageWaitingIndicatorChanged(boolean mwi);
+    }
+
+    /**
+     * Interface for call-forwarding indicator listener.
+     */
+    public interface CallForwardingIndicatorListener {
+        /**
+         * Callback invoked when the call-forwarding indicator changes on the registered
+         * subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+        public void onCallForwardingIndicatorChanged(boolean cfi);
+    }
+
+    /**
+     * Interface for device cell location listener.
+     */
+    public interface CellLocationListener {
+        /**
+         * Callback invoked when device cell location changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         */
+        @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+        public void onCellLocationChanged(@NonNull CellLocation location);
+    }
+
+    /**
+     * Interface for call state listener.
+     */
+    public interface CallStateListener {
+        /**
+         * Callback invoked when device call state changes.
+         * <p>
+         * Reports the state of Telephony (mobile) calls on the device for the registered
+         * subscription.
+         * <p>
+         * Note: the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         * <p>
+         * Note: The state returned here may differ from that returned by
+         * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that
+         * calling {@link TelephonyManager#getCallState()} from within this callback may return a
+         * different state than the callback reports.
+         *
+         * @param state       call state
+         * @param phoneNumber call phone number. If application does not have
+         *                    {@link android.Manifest.permission#READ_CALL_LOG} permission or
+         *                    carrier
+         *                    privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an
+         *                    empty string will be
+         *                    passed as an argument.
+         */
+        @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
+        public void onCallStateChanged(@Annotation.CallState int state,
+            @Nullable String phoneNumber);
+    }
+
+    /**
+     * Interface for data connection state listener.
+     */
+    public interface DataConnectionStateListener {
+        /**
+         * Callback invoked when connection state changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param state       is the current state of data connection.
+         * @param networkType is the current network type of data connection.
+         * @see TelephonyManager#DATA_DISCONNECTED
+         * @see TelephonyManager#DATA_CONNECTING
+         * @see TelephonyManager#DATA_CONNECTED
+         * @see TelephonyManager#DATA_SUSPENDED
+         */
+        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+        public void onDataConnectionStateChanged(@TelephonyManager.DataState int state,
+            @Annotation.NetworkType int networkType);
+    }
+
+    /**
+     * Interface for data activity state listener.
+     */
+    public interface DataActivityListener {
+        /**
+         * Callback invoked when data activity state changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @see TelephonyManager#DATA_ACTIVITY_NONE
+         * @see TelephonyManager#DATA_ACTIVITY_IN
+         * @see TelephonyManager#DATA_ACTIVITY_OUT
+         * @see TelephonyManager#DATA_ACTIVITY_INOUT
+         * @see TelephonyManager#DATA_ACTIVITY_DORMANT
+         */
+        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+        public void onDataActivity(@Annotation.DataActivityType int direction);
+    }
+
+    /**
+     * Interface for network signal strengths listener.
+     */
+    public interface SignalStrengthsListener {
+        /**
+         * Callback invoked when network signal strengths changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         */
+        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
+    }
+
+    /**
+     * Interface for network signal strengths callback which always reported from modem.
+     */
+    public interface AlwaysReportedSignalStrengthListener {
+        /**
+         * Callback always invoked from modem when network signal strengths changes on the
+         * registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         */
+        @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
+        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
+    }
+
+    /**
+     * Interface for cell info listener.
+     */
+    public interface CellInfoListener {
+        /**
+         * Callback invoked when a observed cell info has changed or new cells have been added
+         * or removed on the registered subscription.
+         * Note, the registration subscription ID s from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param cellInfo is the list of currently visible cells.
+         */
+        @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+        public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo);
+    }
+
+    /**
+     * Interface for precise device call state listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface PreciseCallStateListener {
+        /**
+         * Callback invoked when precise device call state changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param callState {@link PreciseCallState}
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+        public void onPreciseCallStateChanged(@NonNull PreciseCallState callState);
+    }
+
+    /**
+     * Interface for call disconnect cause listener.
+     */
+    public interface CallDisconnectCauseListener {
+        /**
+         * Callback invoked when call disconnect cause changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param disconnectCause        {@link DisconnectCause}.
+         * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+        public void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause,
+            @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
+    }
+
+    /**
+     * Interface for IMS call disconnect cause listener.
+     */
+    public interface ImsCallDisconnectCauseListener {
+        /**
+         * Callback invoked when IMS call disconnect cause changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+        public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo);
+    }
+
+    /**
+     * Interface for precise data connection state listener.
+     */
+    public interface PreciseDataConnectionStateListener {
+        /**
+         * Callback providing update about the default/internet data connection on the registered
+         * subscription.
+         * <p>
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+         * or the calling app has carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}).
+         *
+         * @param dataConnectionState {@link PreciseDataConnectionState}
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+        public void onPreciseDataConnectionStateChanged(
+            @NonNull PreciseDataConnectionState dataConnectionState);
+    }
+
+    /**
+     * Interface for Single Radio Voice Call Continuity listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface SrvccStateListener {
+        /**
+         * Callback invoked when there has been a change in the Single Radio Voice Call Continuity
+         * (SRVCC) state for the currently active call on the registered subscription.
+         * <p>
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        public void onSrvccStateChanged(@Annotation.SrvccState int srvccState);
+    }
+
+    /**
+     * Interface for SIM voice activation state listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface VoiceActivationStateListener {
+        /**
+         * Callback invoked when the SIM voice activation state has changed on the registered
+         * subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param state is the current SIM voice activation state
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        public void onVoiceActivationStateChanged(@Annotation.SimActivationState int state);
+
+    }
+
+    /**
+     * Interface for SIM data activation state listener.
+     */
+    public interface DataActivationStateListener {
+        /**
+         * Callback invoked when the SIM data activation state has changed on the registered
+         * subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param state is the current SIM data activation state
+         */
+        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+        public void onDataActivationStateChanged(@Annotation.SimActivationState int state);
+    }
+
+    /**
+     * Interface for user mobile data state listener.
+     */
+    public interface UserMobileDataStateListener {
+        /**
+         * Callback invoked when the user mobile data state has changed on the registered
+         * subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param enabled indicates whether the current user mobile data state is enabled or
+         *                disabled.
+         */
+        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+        public void onUserMobileDataStateChanged(boolean enabled);
+    }
+
+    /**
+     * Interface for display info listener.
+     */
+    public interface DisplayInfoListener {
+        /**
+         * Callback invoked when the display info has changed on the registered subscription.
+         * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user
+         * based on carrier policy.
+         *
+         * @param telephonyDisplayInfo The display information.
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+        public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
+    }
+
+    /**
+     * Interface for the current emergency number list listener.
+     */
+    public interface EmergencyNumberListListener {
+        /**
+         * Callback invoked when the current emergency number list has changed on the registered
+         * subscription.
+         * <p>
+         * Note, the registered subscription is associated with {@link TelephonyManager} object
+         * on which
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
+         * was called.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * given subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param emergencyNumberList Map associating all active subscriptions on the device with
+         *                            the list of emergency numbers originating from that
+         *                            subscription.
+         *                            If there are no active subscriptions, the map will contain a
+         *                            single entry with
+         *                            {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as
+         *                            the key and a list of emergency numbers as the value. If no
+         *                            emergency number information is available, the value will be
+         *                            empty.
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+        public void onEmergencyNumberListChanged(
+            @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList);
+    }
+
+    /**
+     * Interface for outgoing emergency call listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface OutgoingEmergencyCallListener {
+        /**
+         * Callback invoked when an outgoing call is placed to an emergency number.
+         * <p>
+         * This method will be called when an emergency call is placed on any subscription
+         * (including the no-SIM case), regardless of which subscription this callback was
+         * registered on.
+         * <p>
+         *
+         * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was
+         *                              placed to.
+         * @param subscriptionId        The subscription ID used to place the emergency call. If the
+         *                              emergency call was placed without a valid subscription
+         *                              (e.g. when there are no SIM cards in the device), this
+         *                              will be
+         *                              equal to
+         *                              {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+         */
+        @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+        public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+            int subscriptionId);
+    }
+
+    /**
+     * Interface for outgoing emergency sms listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface OutgoingEmergencySmsListener {
+        /**
+         * Smsback invoked when an outgoing sms is sent to an emergency number.
+         * <p>
+         * This method will be called when an emergency sms is sent on any subscription,
+         * regardless of which subscription this callback was registered on.
+         *
+         * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
+         * @param subscriptionId      The subscription ID used to send the emergency sms.
+         */
+        @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+        public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+            int subscriptionId);
+    }
+
+    /**
+     * Interface for phone capability listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface PhoneCapabilityListener {
+        /**
+         * Callback invoked when phone capability changes.
+         * Note, this callback triggers regardless of registered subscription.
+         *
+         * @param capability the new phone capability
+         */
+        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+        public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability);
+    }
+
+    /**
+     * Interface for active data subscription ID listener.
+     */
+    public interface ActiveDataSubscriptionIdListener {
+        /**
+         * Callback invoked when active data subscription ID changes.
+         * Note, this callback triggers regardless of registered subscription.
+         *
+         * @param subId current subscription used to setup Cellular Internet data.
+         *              For example, it could be the current active opportunistic subscription
+         *              in use, or the subscription user selected as default data subscription in
+         *              DSDS mode.
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+        public void onActiveDataSubscriptionIdChanged(int subId);
+    }
+
+    /**
+     * Interface for modem radio power state listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface RadioPowerStateListener {
+        /**
+         * Callback invoked when modem radio power state changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param state the modem radio power state
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        public void onRadioPowerStateChanged(@Annotation.RadioPowerState int state);
+    }
+
+    /**
+     * Interface for carrier network listener.
+     */
+    public interface CarrierNetworkListener {
+        /**
+         * Callback invoked when telephony has received notice from a carrier
+         * app that a network action that could result in connectivity loss
+         * has been requested by an app using
+         * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)}
+         * <p>
+         * This is optional and is only used to allow the system to provide alternative UI while
+         * telephony is performing an action that may result in intentional, temporary network
+         * lack of connectivity.
+         * <p>
+         * Note, this callback is pinned to the registered subscription and will be invoked when
+         * the notifying carrier app has carrier privilege rule on the registered
+         * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges}
+         *
+         * @param active If the carrier network change is or shortly will be active,
+         *               {@code true} indicate that showing alternative UI, {@code false} otherwise.
+         */
+        public void onCarrierNetworkChange(boolean active);
+    }
+
+    /**
+     * Interface for registration failures listener.
+     */
+    public interface RegistrationFailedListener {
+        /**
+         * Report that Registration or a Location/Routing/Tracking Area update has failed.
+         *
+         * <p>Indicate whenever a registration procedure, including a location, routing, or tracking
+         * area update fails. This includes procedures that do not necessarily result in a change of
+         * the modem's registration status. If the modem's registration status changes, that is
+         * reflected in the onNetworkStateChanged() and subsequent
+         * get{Voice/Data}RegistrationState().
+         *
+         * <p>Because registration failures are ephemeral, this callback is not sticky.
+         * Registrants will not receive the most recent past value when registering.
+         *
+         * @param cellIdentity        the CellIdentity, which must include the globally unique
+         *                            identifier
+         *                            for the cell (for example, all components of the CGI or ECGI).
+         * @param chosenPlmn          a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those
+         *                            broadcast by the
+         *                            cell that was chosen for the failed registration attempt.
+         * @param domain              DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
+         * @param causeCode           the primary failure cause code of the procedure.
+         *                            For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
+         *                            For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
+         *                            For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+         *                            For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
+         *                            Integer.MAX_VALUE if this value is unused.
+         * @param additionalCauseCode the cause code of any secondary/combined procedure
+         *                            if appropriate. For UMTS, if a combined attach succeeds for
+         *                            PS only, then the GMM cause code shall be included as an
+         *                            additionalCauseCode. For LTE (ESM), cause codes are in
+         *                            TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+         */
+        @RequiresPermission(allOf = {
+                Manifest.permission.READ_PRECISE_PHONE_STATE,
+                Manifest.permission.ACCESS_FINE_LOCATION
+        })
+        public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
+            @NonNull String chosenPlmn, @NetworkRegistrationInfo.Domain int domain, int causeCode,
+            int additionalCauseCode);
+    }
+
+    /**
+     * Interface for the current allowed network type list listener. This list involves values of
+     * allowed network type for each of reasons.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface AllowedNetworkTypesListener {
+        /**
+         * Callback invoked when the current allowed network type list has changed on the
+         * registered subscription.
+         * Note, the registered subscription is associated with {@link TelephonyManager} object
+         * on which
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
+         * was called.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * given subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param allowedNetworkTypesList Map associating all allowed network type reasons
+         * ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER},
+         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER},
+         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}, and
+         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}) with reason's allowed
+         * network type values.
+         * For example:
+         * map{{TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER, long type value},
+         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER, long type value},
+         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER, long type value},
+         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}}
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onAllowedNetworkTypesChanged(@NonNull Map<Integer, Long> allowedNetworkTypesList);
+    }
+
+    /**
+     * Interface for call attributes listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface CallAttributesListener {
+        /**
+         * Callback invoked when the call attributes changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param callAttributes the call attributes
+         */
+        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+        void onCallAttributesChanged(@NonNull CallAttributes callAttributes);
+    }
+
+    /**
+     * Interface for barring information listener.
+     */
+    public interface BarringInfoListener {
+        /**
+         * Report updated barring information for the current camped/registered cell.
+         *
+         * <p>Barring info is provided for all services applicable to the current camped/registered
+         * cell, for the registered PLMN and current access class/access category.
+         *
+         * @param barringInfo for all services on the current cell.
+         * @see android.telephony.BarringInfo
+         */
+        @RequiresPermission(allOf = {
+                Manifest.permission.READ_PRECISE_PHONE_STATE,
+                Manifest.permission.ACCESS_FINE_LOCATION
+        })
+        public void onBarringInfoChanged(@NonNull BarringInfo barringInfo);
+    }
+
+    /**
+     * Interface for current physical channel configuration listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface PhysicalChannelConfigListener {
+        /**
+         * Callback invoked when the current physical channel configuration has changed
+         *
+         * @param configs List of the current {@link PhysicalChannelConfig}s
+         */
+        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+        public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs);
+    }
+
+    /**
+     * Interface for data enabled listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface DataEnabledListener {
+        /**
+         * Callback invoked when the data enabled changes.
+         *
+         * @param enabled {@code true} if data is enabled, otherwise disabled.
+         * @param reason  Reason for data enabled/disabled.
+         *                See {@link TelephonyManager.DataEnabledReason}.
+         */
+        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+        public void onDataEnabledChanged(boolean enabled,
+            @TelephonyManager.DataEnabledReason int reason);
+    }
+
+
+    /**
+     * The callback methods need to be called on the handler thread where
+     * this object was created.  If the binder did that for us it'd be nice.
+     * <p>
+     * Using a static class and weak reference here to avoid memory leak caused by the
+     * IPhoneState.Stub callback retaining references to the outside TelephonyCallback:
+     * even caller has been destroyed and "un-registered" the TelephonyCallback, it is still not
+     * eligible for GC given the references coming from:
+     * Native Stack --> TelephonyCallback --> Context (Activity).
+     * memory of caller's context will be collected after GC from service side get triggered
+     */
+    private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub {
+        private WeakReference<TelephonyCallback> mTelephonyCallbackWeakRef;
+        private Executor mExecutor;
+
+        IPhoneStateListenerStub(TelephonyCallback telephonyCallback, Executor executor) {
+            mTelephonyCallbackWeakRef = new WeakReference<TelephonyCallback>(telephonyCallback);
+            mExecutor = executor;
+        }
+
+        public void onServiceStateChanged(ServiceState serviceState) {
+            ServiceStateListener listener = (ServiceStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onServiceStateChanged(serviceState)));
+        }
+
+        public void onSignalStrengthChanged(int asu) {
+            // default implementation empty
+        }
+
+        public void onMessageWaitingIndicatorChanged(boolean mwi) {
+            MessageWaitingIndicatorListener listener =
+                    (MessageWaitingIndicatorListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onMessageWaitingIndicatorChanged(mwi)));
+        }
+
+        public void onCallForwardingIndicatorChanged(boolean cfi) {
+            CallForwardingIndicatorListener listener =
+                    (CallForwardingIndicatorListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallForwardingIndicatorChanged(cfi)));
+        }
+
+        public void onCellLocationChanged(CellIdentity cellIdentity) {
+            // There is no system/public API to create an CellIdentity in system server,
+            // so the server pass a null to indicate an empty initial location.
+            CellLocation location =
+                    cellIdentity == null ? CellLocation.getEmpty() : cellIdentity.asCellLocation();
+            CellLocationListener listener = (CellLocationListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCellLocationChanged(location)));
+        }
+
+        public void onCallStateChanged(int state, String incomingNumber) {
+            CallStateListener listener = (CallStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallStateChanged(state,
+                            incomingNumber)));
+        }
+
+        public void onDataConnectionStateChanged(int state, int networkType) {
+            DataConnectionStateListener listener =
+                    (DataConnectionStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            if (state == TelephonyManager.DATA_DISCONNECTING
+                    && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
+                Binder.withCleanCallingIdentity(
+                        () -> mExecutor.execute(() ->
+                                listener.onDataConnectionStateChanged(
+                                        TelephonyManager.DATA_CONNECTED, networkType)));
+            } else {
+                Binder.withCleanCallingIdentity(
+                        () -> mExecutor.execute(() ->
+                                listener.onDataConnectionStateChanged(state, networkType)));
+            }
+        }
+
+        public void onDataActivity(int direction) {
+            DataActivityListener listener = (DataActivityListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onDataActivity(direction)));
+        }
+
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            SignalStrengthsListener listener =
+                    (SignalStrengthsListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onSignalStrengthsChanged(
+                            signalStrength)));
+        }
+
+        public void onCellInfoChanged(List<CellInfo> cellInfo) {
+            CellInfoListener listener = (CellInfoListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCellInfoChanged(cellInfo)));
+        }
+
+        public void onPreciseCallStateChanged(PreciseCallState callState) {
+            PreciseCallStateListener listener =
+                    (PreciseCallStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onPreciseCallStateChanged(callState)));
+        }
+
+        public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+            CallDisconnectCauseListener listener =
+                    (CallDisconnectCauseListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallDisconnectCauseChanged(
+                            disconnectCause, preciseDisconnectCause)));
+        }
+
+        public void onPreciseDataConnectionStateChanged(
+                PreciseDataConnectionState dataConnectionState) {
+            PreciseDataConnectionStateListener listener =
+                    (PreciseDataConnectionStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onPreciseDataConnectionStateChanged(
+                                    dataConnectionState)));
+        }
+
+        public void onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo dcRtInfo) {
+            // default implementation empty
+        }
+
+        public void onSrvccStateChanged(int state) {
+            SrvccStateListener listener = (SrvccStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onSrvccStateChanged(state)));
+        }
+
+        public void onVoiceActivationStateChanged(int activationState) {
+            VoiceActivationStateListener listener =
+                    (VoiceActivationStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onVoiceActivationStateChanged(activationState)));
+        }
+
+        public void onDataActivationStateChanged(int activationState) {
+            DataActivationStateListener listener =
+                    (DataActivationStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onDataActivationStateChanged(activationState)));
+        }
+
+        public void onUserMobileDataStateChanged(boolean enabled) {
+            UserMobileDataStateListener listener =
+                    (UserMobileDataStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onUserMobileDataStateChanged(enabled)));
+        }
+
+        public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
+            DisplayInfoListener listener = (DisplayInfoListener)mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onDisplayInfoChanged(telephonyDisplayInfo)));
+        }
+
+        public void onOemHookRawEvent(byte[] rawData) {
+            // default implementation empty
+        }
+
+        public void onCarrierNetworkChange(boolean active) {
+            CarrierNetworkListener listener =
+                    (CarrierNetworkListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCarrierNetworkChange(active)));
+        }
+
+        public void onEmergencyNumberListChanged(Map emergencyNumberList) {
+            EmergencyNumberListListener listener =
+                    (EmergencyNumberListListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onEmergencyNumberListChanged(emergencyNumberList)));
+        }
+
+        public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+            int subscriptionId) {
+            OutgoingEmergencyCallListener listener =
+                    (OutgoingEmergencyCallListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onOutgoingEmergencyCall(placedEmergencyNumber,
+                                    subscriptionId)));
+        }
+
+        public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+            int subscriptionId) {
+            OutgoingEmergencySmsListener listener =
+                    (OutgoingEmergencySmsListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onOutgoingEmergencySms(sentEmergencyNumber,
+                                    subscriptionId)));
+        }
+
+        public void onPhoneCapabilityChanged(PhoneCapability capability) {
+            PhoneCapabilityListener listener =
+                    (PhoneCapabilityListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onPhoneCapabilityChanged(capability)));
+        }
+
+        public void onRadioPowerStateChanged(@Annotation.RadioPowerState int state) {
+            RadioPowerStateListener listener =
+                    (RadioPowerStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onRadioPowerStateChanged(state)));
+        }
+
+        public void onCallAttributesChanged(CallAttributes callAttributes) {
+            CallAttributesListener listener =
+                    (CallAttributesListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallAttributesChanged(
+                            callAttributes)));
+        }
+
+        public void onActiveDataSubIdChanged(int subId) {
+            ActiveDataSubscriptionIdListener listener =
+                    (ActiveDataSubscriptionIdListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onActiveDataSubscriptionIdChanged(
+                            subId)));
+        }
+
+        public void onImsCallDisconnectCauseChanged(ImsReasonInfo disconnectCause) {
+            ImsCallDisconnectCauseListener listener =
+                    (ImsCallDisconnectCauseListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onImsCallDisconnectCauseChanged(disconnectCause)));
+        }
+
+        public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
+            @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) {
+            RegistrationFailedListener listener =
+                    (RegistrationFailedListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onRegistrationFailed(
+                            cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
+            // default implementation empty
+        }
+
+        public void onBarringInfoChanged(BarringInfo barringInfo) {
+            BarringInfoListener listener = (BarringInfoListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onBarringInfoChanged(barringInfo)));
+        }
+
+        public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) {
+            PhysicalChannelConfigListener listener =
+                    (PhysicalChannelConfigListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged(
+                            configs)));
+        }
+
+        public void onDataEnabledChanged(boolean enabled,
+            @TelephonyManager.DataEnabledReason int reason) {
+            DataEnabledListener listener =
+                    (DataEnabledListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onDataEnabledChanged(
+                            enabled, reason)));
+        }
+
+        public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+            AllowedNetworkTypesListener listener =
+                    (AllowedNetworkTypesListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList)));
+        }
+    }
+}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 3c355d4..459c6e9 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -209,7 +209,7 @@
     }
 
     /**
-     * To check the SDK version for {@link #listenWithEventList}.
+     * To check the SDK version for {@link #listenFromListener}.
      */
     @ChangeId
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
@@ -221,24 +221,49 @@
      * @param pkg Package name
      * @param featureId Feature ID
      * @param listener Listener providing callback
-     * @param events List events
+     * @param events Events
      * @param notifyNow Whether to notify instantly
      */
-    public void listenWithEventList(int subId, @NonNull String pkg, @NonNull String featureId,
-            @NonNull PhoneStateListener listener, @NonNull int[] events, boolean notifyNow) {
+    public void listenFromListener(int subId, @NonNull String pkg, @NonNull String featureId,
+            @NonNull PhoneStateListener listener, @NonNull int events, boolean notifyNow) {
+        if (listener == null) {
+            throw new IllegalStateException("telephony service is null.");
+        }
+
         try {
+            int[] eventsList = getEventsFromBitmask(events).stream().mapToInt(i -> i).toArray();
             // subId from PhoneStateListener is deprecated Q on forward, use the subId from
             // TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q.
             if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
                 // Since mSubId in PhoneStateListener is deprecated from Q on forward, this is
                 // the only place to set mSubId and its for "informational" only.
-                listener.mSubId = (events.length == 0)
+                listener.mSubId = (eventsList.length == 0)
                         ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
             } else if (listener.mSubId != null) {
                 subId = listener.mSubId;
             }
             sRegistry.listenWithEventList(
-                    subId, pkg, featureId, listener.callback, events, notifyNow);
+                    subId, pkg, featureId, listener.callback, eventsList, notifyNow);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Listen for incoming subscriptions
+     * @param subId Subscription ID
+     * @param pkg Package name
+     * @param featureId Feature ID
+     * @param telephonyCallback Listener providing callback
+     * @param events List events
+     * @param notifyNow Whether to notify instantly
+     */
+    private void listenFromCallback(int subId, @NonNull String pkg, @NonNull String featureId,
+            @NonNull TelephonyCallback telephonyCallback, @NonNull int[] events,
+            boolean notifyNow) {
+        try {
+            sRegistry.listenWithEventList(
+                    subId, pkg, featureId, telephonyCallback.callback, events, notifyNow);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -815,136 +840,137 @@
         }
     }
 
-    public @NonNull Set<Integer> getEventsFromListener(@NonNull PhoneStateListener listener) {
+    public @NonNull Set<Integer> getEventsFromCallback(
+            @NonNull TelephonyCallback telephonyCallback) {
 
         Set<Integer> eventList = new ArraySet<>();
 
-        if (listener instanceof PhoneStateListener.ServiceStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.ServiceStateListener) {
+            eventList.add(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.MessageWaitingIndicatorChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.MessageWaitingIndicatorListener) {
+            eventList.add(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.CallForwardingIndicatorChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.CallForwardingIndicatorListener) {
+            eventList.add(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.CellLocationChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.CellLocationListener) {
+            eventList.add(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.CallStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.CallStateListener) {
+            eventList.add(TelephonyCallback.EVENT_CALL_STATE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.DataConnectionStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.DataConnectionStateListener) {
+            eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.DataActivityListener) {
-            eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.DataActivityListener) {
+            eventList.add(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.SignalStrengthsChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.SignalStrengthsListener) {
+            eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.AlwaysReportedSignalStrengthChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.AlwaysReportedSignalStrengthListener) {
+            eventList.add(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.CellInfoChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.CellInfoListener) {
+            eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.PreciseCallStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.PreciseCallStateListener) {
+            eventList.add(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.CallDisconnectCauseChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.CallDisconnectCauseListener) {
+            eventList.add(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.ImsCallDisconnectCauseChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.ImsCallDisconnectCauseListener) {
+            eventList.add(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.PreciseDataConnectionStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.PreciseDataConnectionStateListener) {
+            eventList.add(TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.SrvccStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.SrvccStateListener) {
+            eventList.add(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.VoiceActivationStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.VoiceActivationStateListener) {
+            eventList.add(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.DataActivationStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.DataActivationStateListener) {
+            eventList.add(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.UserMobileDataStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.UserMobileDataStateListener) {
+            eventList.add(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.DisplayInfoChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.DisplayInfoListener) {
+            eventList.add(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.EmergencyNumberListChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.EmergencyNumberListListener) {
+            eventList.add(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.OutgoingEmergencyCallListener) {
-            eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL);
+        if (telephonyCallback instanceof TelephonyCallback.OutgoingEmergencyCallListener) {
+            eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL);
         }
 
-        if (listener instanceof PhoneStateListener.OutgoingEmergencySmsListener) {
-            eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
+        if (telephonyCallback instanceof TelephonyCallback.OutgoingEmergencySmsListener) {
+            eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS);
         }
 
-        if (listener instanceof PhoneStateListener.PhoneCapabilityChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.PhoneCapabilityListener) {
+            eventList.add(TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.ActiveDataSubscriptionIdChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.ActiveDataSubscriptionIdListener) {
+            eventList.add(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.RadioPowerStateChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.RadioPowerStateListener) {
+            eventList.add(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.CarrierNetworkChangeListener) {
-            eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.CarrierNetworkListener) {
+            eventList.add(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.RegistrationFailedListener) {
-            eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
+        if (telephonyCallback instanceof TelephonyCallback.RegistrationFailedListener) {
+            eventList.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE);
         }
 
-        if (listener instanceof PhoneStateListener.CallAttributesChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.CallAttributesListener) {
+            eventList.add(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.BarringInfoChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.BarringInfoListener) {
+            eventList.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.PhysicalChannelConfigChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.PhysicalChannelConfigListener) {
+            eventList.add(TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.DataEnabledChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_DATA_ENABLED_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.DataEnabledListener) {
+            eventList.add(TelephonyCallback.EVENT_DATA_ENABLED_CHANGED);
         }
 
-        if (listener instanceof PhoneStateListener.AllowedNetworkTypesChangedListener) {
-            eventList.add(PhoneStateListener.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
+        if (telephonyCallback instanceof TelephonyCallback.AllowedNetworkTypesListener) {
+            eventList.add(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
         }
 
         return eventList;
@@ -955,200 +981,183 @@
         Set<Integer> eventList = new ArraySet<>();
 
         if ((eventMask & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
-            eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
-            eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_CALL_STATE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
-            eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
-            eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
-            eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) {
-            eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
-            eventList.add(PhoneStateListener.EVENT_OEM_HOOK_RAW);
+            eventList.add(TelephonyCallback.EVENT_OEM_HOOK_RAW);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) {
-            eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
-            eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
-            eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
-            eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) {
-            eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) {
-            eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL) != 0) {
-            eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL);
+            eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS) != 0) {
-            eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
+            eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_REGISTRATION_FAILURE) != 0) {
-            eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
+            eventList.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE);
         }
 
         if ((eventMask & PhoneStateListener.LISTEN_BARRING_INFO) != 0) {
-            eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+            eventList.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
         }
         return eventList;
 
     }
 
     /**
-     * Registers a listener object to receive notification of changes
-     * in specified telephony states.
+     * Registers a callback object to receive notification of changes in specified telephony states.
      * <p>
-     * To register a listener, pass a {@link PhoneStateListener} which implements
+     * To register a callback, pass a {@link TelephonyCallback} which implements
      * interfaces of events. For example,
-     * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements
-     * {@link PhoneStateListener.ServiceStateChangedListener}.
+     * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+     * {@link TelephonyCallback.ServiceStateListener}.
      *
      * At registration, and when a specified telephony state changes, the telephony manager invokes
-     * the appropriate callback method on the listener object and passes the current (updated)
+     * the appropriate callback method on the callback object and passes the current (updated)
      * values.
      * <p>
      *
      * If this TelephonyManager object has been created with
      * {@link TelephonyManager#createForSubscriptionId}, applies to the given subId.
      * Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
-     * To listen events for multiple subIds, pass a separate listener object to
+     * To register events for multiple subIds, pass a separate callback object to
      * each TelephonyManager object created with {@link TelephonyManager#createForSubscriptionId}.
      *
      * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
      * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
      * {@link SecurityException} will be thrown otherwise.
      *
-     * This API should be used sparingly -- large numbers of listeners will cause system
-     * instability. If a process has registered too many listeners without unregistering them, it
-     * may encounter an {@link IllegalStateException} when trying to register more listeners.
+     * This API should be used sparingly -- large numbers of callbacks will cause system
+     * instability. If a process has registered too many callbacks without unregistering them, it
+     * may encounter an {@link IllegalStateException} when trying to register more callbacks.
      *
-     * @param listener The {@link PhoneStateListener} object to register.
+     * @param callback The {@link TelephonyCallback} object to register.
      */
-    public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, int subId,
-            String pkgName, String attributionTag, @NonNull PhoneStateListener listener,
+    public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor,
+            int subId, String pkgName, String attributionTag, @NonNull TelephonyCallback callback,
             boolean notifyNow) {
-        listener.setExecutor(executor);
-        registerPhoneStateListener(subId, pkgName, attributionTag, listener,
-                getEventsFromListener(listener), notifyNow);
-    }
-
-    public void registerPhoneStateListenerWithEvents(int subId, String pkgName,
-            String attributionTag, @NonNull PhoneStateListener listener, int events,
-            boolean notifyNow) {
-        registerPhoneStateListener(
-                subId, pkgName, attributionTag, listener, getEventsFromBitmask(events), notifyNow);
-    }
-
-    private void registerPhoneStateListener(int subId,
-            String pkgName, String attributionTag, @NonNull PhoneStateListener listener,
-            @NonNull Set<Integer> events, boolean notifyNow) {
-        if (listener == null) {
+        if (callback == null) {
             throw new IllegalStateException("telephony service is null.");
         }
-
-        listenWithEventList(subId, pkgName, attributionTag, listener,
-                events.stream().mapToInt(i -> i).toArray(), notifyNow);
+        callback.init(executor);
+        listenFromCallback(subId, pkgName, attributionTag, callback,
+                getEventsFromCallback(callback).stream().mapToInt(i -> i).toArray(), notifyNow);
     }
 
     /**
-     * Unregister an existing {@link PhoneStateListener}.
+     * Unregister an existing {@link TelephonyCallback}.
      *
-     * @param listener The {@link PhoneStateListener} object to unregister.
+     * @param callback The {@link TelephonyCallback} object to unregister.
      */
-    public void unregisterPhoneStateListener(int subId, String pkgName, String attributionTag,
-                                             @NonNull PhoneStateListener listener,
-                                             boolean notifyNow) {
-        listenWithEventList(subId, pkgName, attributionTag, listener, new int[0], notifyNow);
+    public void unregisterTelephonyCallback(int subId, String pkgName, String attributionTag,
+            @NonNull TelephonyCallback callback, boolean notifyNow) {
+        listenFromCallback(subId, pkgName, attributionTag, callback, new int[0], notifyNow);
     }
 }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 2b577d0..3a33024 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -68,7 +68,7 @@
         DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
 
         DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
-        DEFAULT_FLAGS.put("settings_silky_home", "false");
+        DEFAULT_FLAGS.put("settings_silky_home", "true");
         DEFAULT_FLAGS.put("settings_contextual_home", "false");
         DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
         DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "false");
diff --git a/core/java/android/util/SizeF.java b/core/java/android/util/SizeF.java
index 2edc4a7f..c77a0243 100644
--- a/core/java/android/util/SizeF.java
+++ b/core/java/android/util/SizeF.java
@@ -16,8 +16,12 @@
 
 package android.util;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.Preconditions.checkArgumentFinite;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 /**
  * Immutable class for describing width and height dimensions in some arbitrary
@@ -26,7 +30,7 @@
  * Width and height are finite values stored as a floating point representation.
  * </p>
  */
-public final class SizeF {
+public final class SizeF implements Parcelable {
     /**
      * Create a new immutable SizeF instance.
      *
@@ -161,4 +165,43 @@
 
     private final float mWidth;
     private final float mHeight;
+
+    /**
+     * Parcelable interface methods
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Write this size to the specified parcel. To restore a size from a parcel, use the
+     * {@link #CREATOR}.
+     * @param out The parcel to write the point's coordinates into
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeFloat(mWidth);
+        out.writeFloat(mHeight);
+    }
+
+    public static final @NonNull Creator<SizeF> CREATOR = new Creator<SizeF>() {
+        /**
+         * Return a new size from the data in the specified parcel.
+         */
+        @Override
+        public @NonNull SizeF createFromParcel(@NonNull Parcel in) {
+            float width = in.readFloat();
+            float height = in.readFloat();
+            return new SizeF(width, height);
+        }
+
+        /**
+         * Return an array of sizes of the specified size.
+         */
+        @Override
+        public @NonNull SizeF[] newArray(int size) {
+            return new SizeF[size];
+        }
+    };
 }
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 09452828..e5a137c 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -86,6 +86,12 @@
     // accessibility from hanging
     private static final long REQUEST_PREPARER_TIMEOUT_MS = 500;
 
+    // Callbacks should have the same configuration of the flags below to allow satisfying a pending
+    // node request on prefetch
+    private static final int FLAGS_AFFECTING_REPORTED_DATA =
+            AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+            | AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+
     private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
         new ArrayList<AccessibilityNodeInfo>();
 
@@ -114,6 +120,9 @@
     private AddNodeInfosForViewId mAddNodeInfosForViewId;
 
     @GuardedBy("mLock")
+    private ArrayList<Message> mPendingFindNodeByIdMessages;
+
+    @GuardedBy("mLock")
     private int mNumActiveRequestPreparers;
     @GuardedBy("mLock")
     private List<MessageHolder> mMessagesWaitingForRequestPreparer;
@@ -128,6 +137,7 @@
         mViewRootImpl = viewRootImpl;
         mPrefetcher = new AccessibilityNodePrefetcher();
         mA11yManager = mViewRootImpl.mContext.getSystemService(AccessibilityManager.class);
+        mPendingFindNodeByIdMessages = new ArrayList<>();
     }
 
     private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid,
@@ -177,7 +187,11 @@
         args.arg4 = arguments;
         message.obj = args;
 
-        scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
+        synchronized (mLock) {
+            mPendingFindNodeByIdMessages.add(message);
+            scheduleMessage(message, interrogatingPid, interrogatingTid,
+                    CONSIDER_REQUEST_PREPARERS);
+        }
     }
 
     /**
@@ -315,6 +329,9 @@
     }
 
     private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
+        synchronized (mLock) {
+            mPendingFindNodeByIdMessages.remove(message);
+        }
         final int flags = message.arg1;
 
         SomeArgs args = (SomeArgs) message.obj;
@@ -329,22 +346,58 @@
 
         args.recycle();
 
-        List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
-        infos.clear();
+        View rootView = null;
+        AccessibilityNodeInfo rootNode = null;
         try {
             if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
                 return;
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
-            final View root = findViewByAccessibilityId(accessibilityViewId);
-            if (root != null && isShown(root)) {
-                mPrefetcher.prefetchAccessibilityNodeInfos(
-                        root, virtualDescendantId, flags, infos, arguments);
+            rootView = findViewByAccessibilityId(accessibilityViewId);
+            if (rootView != null && isShown(rootView)) {
+                rootNode = populateAccessibilityNodeInfoForView(
+                        rootView, arguments, virtualDescendantId);
             }
         } finally {
-            updateInfosForViewportAndReturnFindNodeResult(
-                    infos, callback, interactionId, spec, interactiveRegion);
+            updateInfoForViewportAndReturnFindNodeResult(
+                    rootNode == null ? null : AccessibilityNodeInfo.obtain(rootNode),
+                    callback, interactionId, spec, interactiveRegion);
         }
+        ArrayList<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
+        infos.clear();
+        mPrefetcher.prefetchAccessibilityNodeInfos(
+                rootView, rootNode == null ? null : AccessibilityNodeInfo.obtain(rootNode),
+                virtualDescendantId, flags, infos);
+        mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+        updateInfosForViewPort(infos, spec, interactiveRegion);
+        returnPrefetchResult(interactionId, infos, callback);
+        returnPendingFindAccessibilityNodeInfosInPrefetch(rootNode, infos, flags);
+    }
+
+    private AccessibilityNodeInfo populateAccessibilityNodeInfoForView(
+            View view, Bundle arguments, int virtualViewId) {
+        AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+        // Determine if we'll be populating extra data
+        final String extraDataRequested = (arguments == null) ? null
+                : arguments.getString(EXTRA_DATA_REQUESTED_KEY);
+        AccessibilityNodeInfo root = null;
+        if (provider == null) {
+            root = view.createAccessibilityNodeInfo();
+            if (root != null) {
+                if (extraDataRequested != null) {
+                    view.addExtraDataToAccessibilityNodeInfo(root, extraDataRequested, arguments);
+                }
+            }
+        } else {
+            root = provider.createAccessibilityNodeInfo(virtualViewId);
+            if (root != null) {
+                if (extraDataRequested != null) {
+                    provider.addExtraDataToAccessibilityNodeInfo(
+                            virtualViewId, root, extraDataRequested, arguments);
+                }
+            }
+        }
+        return root;
     }
 
     public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
@@ -403,6 +456,7 @@
                 mAddNodeInfosForViewId.reset();
             }
         } finally {
+            mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
             updateInfosForViewportAndReturnFindNodeResult(
                     infos, callback, interactionId, spec, interactiveRegion);
         }
@@ -485,6 +539,7 @@
                 }
             }
         } finally {
+            mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
             updateInfosForViewportAndReturnFindNodeResult(
                     infos, callback, interactionId, spec, interactiveRegion);
         }
@@ -576,6 +631,7 @@
                 }
             }
         } finally {
+            mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
             updateInfoForViewportAndReturnFindNodeResult(
                     focused, callback, interactionId, spec, interactiveRegion);
         }
@@ -630,6 +686,7 @@
                 }
             }
         } finally {
+            mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
             updateInfoForViewportAndReturnFindNodeResult(
                     next, callback, interactionId, spec, interactiveRegion);
         }
@@ -786,33 +843,6 @@
         }
     }
 
-    private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
-            MagnificationSpec spec) {
-        if (infos == null) {
-            return;
-        }
-        final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
-        if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
-            final int infoCount = infos.size();
-            for (int i = 0; i < infoCount; i++) {
-                AccessibilityNodeInfo info = infos.get(i);
-                applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
-            }
-        }
-    }
-
-    private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos,
-            Region interactiveRegion) {
-        if (interactiveRegion == null || infos == null) {
-            return;
-        }
-        final int infoCount = infos.size();
-        for (int i = 0; i < infoCount; i++) {
-            AccessibilityNodeInfo info = infos.get(i);
-            adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
-        }
-    }
-
     private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
             Region interactiveRegion) {
         if (interactiveRegion == null || info == null) {
@@ -833,17 +863,6 @@
         return false;
     }
 
-    private void adjustBoundsInScreenIfNeeded(List<AccessibilityNodeInfo> infos) {
-        if (infos == null || shouldBypassAdjustBoundsInScreen()) {
-            return;
-        }
-        final int infoCount = infos.size();
-        for (int i = 0; i < infoCount; i++) {
-            final AccessibilityNodeInfo info = infos.get(i);
-            adjustBoundsInScreenIfNeeded(info);
-        }
-    }
-
     private void adjustBoundsInScreenIfNeeded(AccessibilityNodeInfo info) {
         if (info == null || shouldBypassAdjustBoundsInScreen()) {
             return;
@@ -891,17 +910,6 @@
         return screenMatrix == null || screenMatrix.isIdentity();
     }
 
-    private void associateLeashedParentIfNeeded(List<AccessibilityNodeInfo> infos) {
-        if (infos == null || shouldBypassAssociateLeashedParent()) {
-            return;
-        }
-        final int infoCount = infos.size();
-        for (int i = 0; i < infoCount; i++) {
-            final AccessibilityNodeInfo info = infos.get(i);
-            associateLeashedParentIfNeeded(info);
-        }
-    }
-
     private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) {
         if (info == null || shouldBypassAssociateLeashedParent()) {
             return;
@@ -975,18 +983,46 @@
         return (appScale != 1.0f || (spec != null && !spec.isNop()));
     }
 
+    private void updateInfosForViewPort(List<AccessibilityNodeInfo> infos, MagnificationSpec spec,
+                                        Region interactiveRegion) {
+        for (int i = 0; i < infos.size(); i++) {
+            updateInfoForViewPort(infos.get(i), spec, interactiveRegion);
+        }
+    }
+
+    private void updateInfoForViewPort(AccessibilityNodeInfo info, MagnificationSpec spec,
+                                       Region interactiveRegion) {
+        associateLeashedParentIfNeeded(info);
+        applyScreenMatrixIfNeeded(info);
+        adjustBoundsInScreenIfNeeded(info);
+        // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
+        // then impact the visibility result, we need to adjust visibility before apply scale.
+        adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
+        applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
+    }
+
     private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos,
             IAccessibilityInteractionConnectionCallback callback, int interactionId,
             MagnificationSpec spec, Region interactiveRegion) {
+        if (infos != null) {
+            updateInfosForViewPort(infos, spec, interactiveRegion);
+        }
+        returnFindNodesResult(infos, callback, interactionId);
+    }
+
+    private void returnFindNodeResult(AccessibilityNodeInfo info,
+                                      IAccessibilityInteractionConnectionCallback callback,
+                                      int interactionId) {
         try {
-            mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
-            associateLeashedParentIfNeeded(infos);
-            applyScreenMatrixIfNeeded(infos);
-            adjustBoundsInScreenIfNeeded(infos);
-            // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
-            // then impact the visibility result, we need to adjust visibility before apply scale.
-            adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
-            applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
+            callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+        } catch (RemoteException re) {
+            /* ignore - the other side will time out */
+        }
+    }
+
+    private void returnFindNodesResult(List<AccessibilityNodeInfo> infos,
+            IAccessibilityInteractionConnectionCallback callback, int interactionId) {
+        try {
             callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
             if (infos != null) {
                 infos.clear();
@@ -996,22 +1032,80 @@
         }
     }
 
+    private void returnPendingFindAccessibilityNodeInfosInPrefetch(AccessibilityNodeInfo rootNode,
+            List<AccessibilityNodeInfo> infos, int flags) {
+
+        AccessibilityNodeInfo satisfiedPendingRequestPrefetchedNode = null;
+        IAccessibilityInteractionConnectionCallback satisfiedPendingRequestCallback = null;
+        int satisfiedPendingRequestInteractionId = AccessibilityInteractionClient.NO_ID;
+
+        synchronized (mLock) {
+            for (int i = 0; i < mPendingFindNodeByIdMessages.size(); i++) {
+                final Message pendingMessage = mPendingFindNodeByIdMessages.get(i);
+                final int pendingFlags = pendingMessage.arg1;
+                if ((pendingFlags & FLAGS_AFFECTING_REPORTED_DATA)
+                        != (flags & FLAGS_AFFECTING_REPORTED_DATA)) {
+                    continue;
+                }
+                SomeArgs args = (SomeArgs) pendingMessage.obj;
+                final int accessibilityViewId = args.argi1;
+                final int virtualDescendantId = args.argi2;
+
+                satisfiedPendingRequestPrefetchedNode = nodeWithIdFromList(rootNode,
+                        infos, AccessibilityNodeInfo.makeNodeId(
+                                accessibilityViewId, virtualDescendantId));
+
+                if (satisfiedPendingRequestPrefetchedNode != null) {
+                    satisfiedPendingRequestCallback =
+                            (IAccessibilityInteractionConnectionCallback) args.arg1;
+                    satisfiedPendingRequestInteractionId = args.argi3;
+                    mHandler.removeMessages(
+                            PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID,
+                            pendingMessage.obj);
+                    args.recycle();
+                    break;
+                }
+            }
+            mPendingFindNodeByIdMessages.clear();
+        }
+
+        if (satisfiedPendingRequestPrefetchedNode != null) {
+            returnFindNodeResult(
+                    AccessibilityNodeInfo.obtain(satisfiedPendingRequestPrefetchedNode),
+                    satisfiedPendingRequestCallback, satisfiedPendingRequestInteractionId);
+        }
+    }
+
+    private AccessibilityNodeInfo nodeWithIdFromList(AccessibilityNodeInfo rootNode,
+            List<AccessibilityNodeInfo> infos, long nodeId) {
+        if (rootNode != null && rootNode.getSourceNodeId() == nodeId) {
+            return rootNode;
+        }
+        for (int j = 0; j < infos.size(); j++) {
+            AccessibilityNodeInfo info = infos.get(j);
+            if (info.getSourceNodeId() == nodeId) {
+                return info;
+            }
+        }
+        return null;
+    }
+
+    private void returnPrefetchResult(int interactionId, List<AccessibilityNodeInfo> infos,
+                                      IAccessibilityInteractionConnectionCallback callback) {
+        if (infos.size() > 0) {
+            try {
+                callback.setPrefetchAccessibilityNodeInfoResult(infos, interactionId);
+            } catch (RemoteException re) {
+                /* ignore - other side isn't too bothered if this doesn't arrive */
+            }
+        }
+    }
+
     private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info,
             IAccessibilityInteractionConnectionCallback callback, int interactionId,
             MagnificationSpec spec, Region interactiveRegion) {
-        try {
-            mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
-            associateLeashedParentIfNeeded(info);
-            applyScreenMatrixIfNeeded(info);
-            adjustBoundsInScreenIfNeeded(info);
-            // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
-            // then impact the visibility result, we need to adjust visibility before apply scale.
-            adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
-            applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
-            callback.setFindAccessibilityNodeInfoResult(info, interactionId);
-        } catch (RemoteException re) {
-                /* ignore - the other side will time out */
-        }
+        updateInfoForViewPort(info, spec, interactiveRegion);
+        returnFindNodeResult(info, callback, interactionId);
     }
 
     private boolean handleClickableSpanActionUiThread(
@@ -1054,56 +1148,45 @@
 
         private final ArrayList<View> mTempViewList = new ArrayList<View>();
 
-        public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
-                List<AccessibilityNodeInfo> outInfos, Bundle arguments) {
+        public void prefetchAccessibilityNodeInfos(View view, AccessibilityNodeInfo root,
+                int virtualViewId, int fetchFlags, List<AccessibilityNodeInfo> outInfos) {
+            if (root == null) {
+                return;
+            }
             AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
-            // Determine if we'll be populating extra data
-            final String extraDataRequested = (arguments == null) ? null
-                    : arguments.getString(EXTRA_DATA_REQUESTED_KEY);
             if (provider == null) {
-                AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
-                if (root != null) {
-                    if (extraDataRequested != null) {
-                        view.addExtraDataToAccessibilityNodeInfo(
-                                root, extraDataRequested, arguments);
-                    }
-                    outInfos.add(root);
-                    if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
-                        prefetchPredecessorsOfRealNode(view, outInfos);
-                    }
-                    if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
-                        prefetchSiblingsOfRealNode(view, outInfos);
-                    }
-                    if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
-                        prefetchDescendantsOfRealNode(view, outInfos);
-                    }
+                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+                    prefetchPredecessorsOfRealNode(view, outInfos);
+                }
+                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+                    prefetchSiblingsOfRealNode(view, outInfos);
+                }
+                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+                    prefetchDescendantsOfRealNode(view, outInfos);
                 }
             } else {
-                final AccessibilityNodeInfo root =
-                        provider.createAccessibilityNodeInfo(virtualViewId);
-                if (root != null) {
-                    if (extraDataRequested != null) {
-                        provider.addExtraDataToAccessibilityNodeInfo(
-                                virtualViewId, root, extraDataRequested, arguments);
-                    }
-                    outInfos.add(root);
-                    if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
-                        prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
-                    }
-                    if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
-                        prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
-                    }
-                    if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
-                        prefetchDescendantsOfVirtualNode(root, provider, outInfos);
-                    }
+                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+                    prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
+                }
+                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+                    prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
+                }
+                if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+                    prefetchDescendantsOfVirtualNode(root, provider, outInfos);
                 }
             }
             if (ENFORCE_NODE_TREE_CONSISTENT) {
-                enforceNodeTreeConsistent(outInfos);
+                enforceNodeTreeConsistent(root, outInfos);
             }
         }
 
-        private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
+        private boolean shouldStopPrefetching(List prefetchededInfos) {
+            return mHandler.hasUserInteractiveMessagesWaiting()
+                    || prefetchededInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE;
+        }
+
+        private void enforceNodeTreeConsistent(
+                AccessibilityNodeInfo root, List<AccessibilityNodeInfo> nodes) {
             LongSparseArray<AccessibilityNodeInfo> nodeMap =
                     new LongSparseArray<AccessibilityNodeInfo>();
             final int nodeCount = nodes.size();
@@ -1114,7 +1197,6 @@
 
             // If the nodes are a tree it does not matter from
             // which node we start to search for the root.
-            AccessibilityNodeInfo root = nodeMap.valueAt(0);
             AccessibilityNodeInfo parent = root;
             while (parent != null) {
                 root = parent;
@@ -1181,9 +1263,11 @@
 
         private void prefetchPredecessorsOfRealNode(View view,
                 List<AccessibilityNodeInfo> outInfos) {
+            if (shouldStopPrefetching(outInfos)) {
+                return;
+            }
             ViewParent parent = view.getParentForAccessibility();
-            while (parent instanceof View
-                    && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+            while (parent instanceof View && !shouldStopPrefetching(outInfos)) {
                 View parentView = (View) parent;
                 AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
                 if (info != null) {
@@ -1195,6 +1279,9 @@
 
         private void prefetchSiblingsOfRealNode(View current,
                 List<AccessibilityNodeInfo> outInfos) {
+            if (shouldStopPrefetching(outInfos)) {
+                return;
+            }
             ViewParent parent = current.getParentForAccessibility();
             if (parent instanceof ViewGroup) {
                 ViewGroup parentGroup = (ViewGroup) parent;
@@ -1204,7 +1291,7 @@
                     parentGroup.addChildrenForAccessibility(children);
                     final int childCount = children.size();
                     for (int i = 0; i < childCount; i++) {
-                        if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                        if (shouldStopPrefetching(outInfos)) {
                             return;
                         }
                         View child = children.get(i);
@@ -1232,7 +1319,7 @@
 
         private void prefetchDescendantsOfRealNode(View root,
                 List<AccessibilityNodeInfo> outInfos) {
-            if (!(root instanceof ViewGroup)) {
+            if (shouldStopPrefetching(outInfos) || !(root instanceof ViewGroup)) {
                 return;
             }
             HashMap<View, AccessibilityNodeInfo> addedChildren =
@@ -1243,7 +1330,7 @@
                 root.addChildrenForAccessibility(children);
                 final int childCount = children.size();
                 for (int i = 0; i < childCount; i++) {
-                    if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                    if (shouldStopPrefetching(outInfos)) {
                         return;
                     }
                     View child = children.get(i);
@@ -1268,7 +1355,7 @@
             } finally {
                 children.clear();
             }
-            if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+            if (!shouldStopPrefetching(outInfos)) {
                 for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
                     View addedChild = entry.getKey();
                     AccessibilityNodeInfo virtualRoot = entry.getValue();
@@ -1290,7 +1377,7 @@
             long parentNodeId = root.getParentNodeId();
             int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
             while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
-                if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                if (shouldStopPrefetching(outInfos)) {
                     return;
                 }
                 final int virtualDescendantId =
@@ -1335,7 +1422,7 @@
                 if (parent != null) {
                     final int childCount = parent.getChildCount();
                     for (int i = 0; i < childCount; i++) {
-                        if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                        if (shouldStopPrefetching(outInfos)) {
                             return;
                         }
                         final long childNodeId = parent.getChildId(i);
@@ -1360,7 +1447,7 @@
             final int initialOutInfosSize = outInfos.size();
             final int childCount = root.getChildCount();
             for (int i = 0; i < childCount; i++) {
-                if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+                if (shouldStopPrefetching(outInfos)) {
                     return;
                 }
                 final long childNodeId = root.getChildId(i);
@@ -1370,7 +1457,7 @@
                     outInfos.add(child);
                 }
             }
-            if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+            if (!shouldStopPrefetching(outInfos)) {
                 final int addedChildCount = outInfos.size() - initialOutInfosSize;
                 for (int i = 0; i < addedChildCount; i++) {
                     AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
@@ -1479,6 +1566,10 @@
         boolean hasAccessibilityCallback(Message message) {
             return message.what < FIRST_NO_ACCESSIBILITY_CALLBACK_MSG ? true : false;
         }
+
+        boolean hasUserInteractiveMessagesWaiting() {
+            return hasMessagesOrCallbacks();
+        }
     }
 
     private final class AddNodeInfosForViewId implements Predicate<View> {
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 59299f6..5c65c65 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -744,7 +744,10 @@
         }
 
         try {
-            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.traceBegin(Trace.TRACE_TAG_VIEW,
+                        "Choreographer#doFrame " + vsyncEventData.id);
+            }
             AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
 
             mFrameInfo.markInputHandlingStart();
diff --git a/core/java/android/view/InputEventAssigner.java b/core/java/android/view/InputEventAssigner.java
new file mode 100644
index 0000000..c159a12
--- /dev/null
+++ b/core/java/android/view/InputEventAssigner.java
@@ -0,0 +1,92 @@
+/*
+ * 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 android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+
+/**
+ * Process input events and assign input event id to a specific frame.
+ *
+ * The assigned input event id is determined by where the current gesture is relative to the vsync.
+ * In the middle of the gesture (we already processed some input events, and already received at
+ * least 1 vsync), the latest InputEvent is assigned to the next frame.
+ * If a gesture just started, then the ACTION_DOWN event will be assigned to the next frame.
+ *
+ * Consider the following sequence:
+ * DOWN -> VSYNC 1 -> MOVE 1 -> MOVE 2 -> VSYNC 2.
+ *
+ * For VSYNC 1, we will assign the "DOWN" input event.
+ * For VSYNC 2, we will assign the "MOVE 2" input event.
+ *
+ * Consider another sequence:
+ * DOWN -> MOVE 1 -> MOVE 2 -> VSYNC 1 -> MOVE 3 -> VSYNC 2.
+ *
+ * For VSYNC 1, we will still assign the "DOWN" input event. That means that "MOVE 1" and "MOVE 2"
+ * events are not attributed to any frame.
+ * For VSYNC 2, the "MOVE 3" input event will be assigned.
+ *
+ * @hide
+ */
+public class InputEventAssigner {
+    private static final String TAG = "InputEventAssigner";
+    private boolean mHasUnprocessedDown = false;
+    private int mEventId = INVALID_INPUT_EVENT_ID;
+
+    /**
+     * Notify InputEventAssigner that the Choreographer callback has been processed. This will reset
+     * the 'down' state to assign the latest input event to the current frame.
+     */
+    public void onChoreographerCallback() {
+        // Mark completion of this frame. Use newest input event from now on.
+        mHasUnprocessedDown = false;
+    }
+
+    /**
+     * Process the provided input event to determine which event id to assign to the current frame.
+     * @param event the input event currently being processed
+     * @return the id of the input event to use for the current frame
+     */
+    public int processEvent(InputEvent event) {
+        if (event instanceof KeyEvent) {
+            // We will not do any special handling for key events
+            return event.getId();
+        }
+
+        if (event instanceof MotionEvent) {
+            MotionEvent motionEvent = (MotionEvent) event;
+            final int action = motionEvent.getActionMasked();
+
+            if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+                mHasUnprocessedDown = false;
+            }
+            if (motionEvent.isFromSource(SOURCE_TOUCHSCREEN) && action == MotionEvent.ACTION_DOWN) {
+                mHasUnprocessedDown = true;
+                mEventId = event.getId();
+                // This will remain 'true' even if we receive a MOVE event, as long as choreographer
+                // hasn't invoked the 'CALLBACK_INPUT' callback.
+            }
+            // Don't update the event id if we haven't processed DOWN yet.
+            if (!mHasUnprocessedDown) {
+                mEventId = event.getId();
+            }
+            return mEventId;
+        }
+
+        throw new IllegalArgumentException("Received unexpected " + event);
+    }
+}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index d67439c..6801c27 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -3589,6 +3589,7 @@
             msg.append(", deviceId=").append(getDeviceId());
             msg.append(", source=0x").append(Integer.toHexString(getSource()));
             msg.append(", displayId=").append(getDisplayId());
+            msg.append(", eventId=").append(getId());
         }
         msg.append(" }");
         return msg.toString();
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 5ce4c50..6c8753b 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -27,7 +27,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.util.AttributeSet;
-import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
 import android.widget.RemoteViews;
 
 import com.android.internal.R;
@@ -42,7 +42,7 @@
  * @hide
  */
 @RemoteViews.RemoteView
-public class NotificationHeaderView extends FrameLayout {
+public class NotificationHeaderView extends RelativeLayout {
     private final int mHeadingEndMargin;
     private final int mTouchableHeight;
     private OnClickListener mExpandClickListener;
@@ -159,7 +159,7 @@
      * @param extraMarginEnd extra margin in px
      */
     public void setTopLineExtraMarginEnd(int extraMarginEnd) {
-        mTopLineView.setHeaderTextMarginEnd(extraMarginEnd + mHeadingEndMargin);
+        mTopLineView.setHeaderTextMarginEnd(extraMarginEnd);
     }
 
     /**
@@ -181,12 +181,15 @@
      * @return extra margin
      */
     public int getTopLineExtraMarginEnd() {
-        return mTopLineView.getHeaderTextMarginEnd() - mHeadingEndMargin;
+        return mTopLineView.getHeaderTextMarginEnd();
     }
 
     /**
      * Get the base margin at the end of the top line view.
      * Add this to {@link #getTopLineExtraMarginEnd()} to get the total margin of the top line.
+     * <p>
+     * NOTE: This method's result is only valid if the expander does not have a number. Currently
+     * only groups headers and conversations have numbers, so this is safe to use by MediaStyle.
      *
      * @return base margin
      */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ebef464..ab7732b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -22188,9 +22188,6 @@
      * and hardware acceleration.
      */
     boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
-        // Clear the overscroll effect:
-        // TODO: Use internal API instead of overriding the existing RenderEffect
-        setRenderEffect(null);
 
         final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
         /* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index c2f17c3..ec23a29 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -310,6 +310,18 @@
      */
     private static final float AMBIGUOUS_GESTURE_MULTIPLIER = 2f;
 
+    /**
+     * The timeout value in milliseconds to adjust the selection span and actions for the selected
+     * text when TextClassifier has been initialized.
+     */
+    private static final int SMART_SELECTION_INITIALIZED_TIMEOUT_IN_MILLISECOND = 200;
+
+    /**
+     * The timeout value in milliseconds to adjust the selection span and actions for the selected
+     * text when TextClassifier has not been initialized.
+     */
+    private static final int SMART_SELECTION_INITIALIZING_TIMEOUT_IN_MILLISECOND = 500;
+
     private final boolean mConstructedWithContext;
     private final int mEdgeSlop;
     private final int mFadingEdgeLength;
@@ -335,6 +347,8 @@
     private final float mHorizontalScrollFactor;
     private final boolean mShowMenuShortcutsWhenKeyboardPresent;
     private final long mScreenshotChordKeyTimeout;
+    private final int mSmartSelectionInitializedTimeout;
+    private final int mSmartSelectionInitializingTimeout;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768915)
     private boolean sHasPermanentMenuKey;
@@ -378,6 +392,8 @@
         // Getter throws if mConstructedWithContext is false so doesn't matter what
         // this value is.
         mMinScalingSpan = 0;
+        mSmartSelectionInitializedTimeout = SMART_SELECTION_INITIALIZED_TIMEOUT_IN_MILLISECOND;
+        mSmartSelectionInitializingTimeout = SMART_SELECTION_INITIALIZING_TIMEOUT_IN_MILLISECOND;
     }
 
     /**
@@ -488,6 +504,11 @@
 
         mScreenshotChordKeyTimeout = res.getInteger(
                 com.android.internal.R.integer.config_screenshotChordKeyTimeout);
+
+        mSmartSelectionInitializedTimeout = res.getInteger(
+                com.android.internal.R.integer.config_smartSelectionInitializedTimeoutMillis);
+        mSmartSelectionInitializingTimeout = res.getInteger(
+                com.android.internal.R.integer.config_smartSelectionInitializingTimeoutMillis);
     }
 
     /**
@@ -1069,6 +1090,24 @@
     }
 
     /**
+     * @return the timeout value in milliseconds to adjust the selection span and actions for the
+     *         selected text when TextClassifier has been initialized.
+     * @hide
+     */
+    public int getSmartSelectionInitializedTimeout() {
+        return mSmartSelectionInitializedTimeout;
+    }
+
+    /**
+     * @return the timeout value in milliseconds to adjust the selection span and actions for the
+     *         selected text when TextClassifier has not been initialized.
+     * @hide
+     */
+    public int getSmartSelectionInitializingTimeout() {
+        return mSmartSelectionInitializingTimeout;
+    }
+
+    /**
      * @return the duration in milliseconds before an end of a long press causes a tooltip to be
      * hidden
      * @hide
diff --git a/core/java/android/view/ViewFrameInfo.java b/core/java/android/view/ViewFrameInfo.java
index d4aaa61..36bf532 100644
--- a/core/java/android/view/ViewFrameInfo.java
+++ b/core/java/android/view/ViewFrameInfo.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.graphics.FrameInfo;
+import android.os.IInputConstants;
 
 /**
  * The timing information of events taking place in ViewRootImpl
@@ -24,32 +25,14 @@
  */
 public class ViewFrameInfo {
     public long drawStart;
-    public long oldestInputEventTime; // the time of the oldest input event consumed for this frame
-    public long newestInputEventTime; // the time of the newest input event consumed for this frame
+
+
     // Various flags set to provide extra metadata about the current frame. See flag definitions
     // inside FrameInfo.
     // @see android.graphics.FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED
     public long flags;
 
-    /**
-     * Update the oldest event time.
-     * @param eventTime the time of the input event
-     */
-    public void updateOldestInputEvent(long eventTime) {
-        if (oldestInputEventTime == 0 || eventTime < oldestInputEventTime) {
-            oldestInputEventTime = eventTime;
-        }
-    }
-
-    /**
-     * Update the newest event time.
-     * @param eventTime the time of the input event
-     */
-    public void updateNewestInputEvent(long eventTime) {
-        if (newestInputEventTime == 0 || eventTime > newestInputEventTime) {
-            newestInputEventTime = eventTime;
-        }
-    }
+    private int mInputEventId;
 
     /**
      * Populate the missing fields using the data from ViewFrameInfo
@@ -58,8 +41,7 @@
     public void populateFrameInfo(FrameInfo frameInfo) {
         frameInfo.frameInfo[FrameInfo.FLAGS] |= flags;
         frameInfo.frameInfo[FrameInfo.DRAW_START] = drawStart;
-        // TODO(b/169866723): Use InputEventAssigner
-        frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID] = newestInputEventTime;
+        frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID] = mInputEventId;
     }
 
     /**
@@ -67,8 +49,7 @@
      */
     public void reset() {
         drawStart = 0;
-        oldestInputEventTime = 0;
-        newestInputEventTime = 0;
+        mInputEventId = IInputConstants.INVALID_INPUT_EVENT_ID;
         flags = 0;
     }
 
@@ -78,4 +59,12 @@
     public void markDrawStart() {
         drawStart = System.nanoTime();
     }
+
+    /**
+     * Assign the value for input event id
+     * @param eventId the id of the input event
+     */
+    public void setInputEvent(int eventId) {
+        mInputEventId = eventId;
+    }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f8e65bd..390e3ae 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -457,6 +457,7 @@
     FallbackEventHandler mFallbackEventHandler;
     final Choreographer mChoreographer;
     protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo();
+    private final InputEventAssigner mInputEventAssigner = new InputEventAssigner();
 
     /**
      * Update the Choreographer's FrameInfo object with the timing information for the current
@@ -8352,16 +8353,7 @@
             Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                     mPendingInputEventCount);
 
-            long eventTime = q.mEvent.getEventTimeNano();
-            long oldestEventTime = eventTime;
-            if (q.mEvent instanceof MotionEvent) {
-                MotionEvent me = (MotionEvent)q.mEvent;
-                if (me.getHistorySize() > 0) {
-                    oldestEventTime = me.getHistoricalEventTimeNano(0);
-                }
-            }
-            mViewFrameInfo.updateOldestInputEvent(oldestEventTime);
-            mViewFrameInfo.updateNewestInputEvent(eventTime);
+            mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));
 
             deliverInputEvent(q);
         }
@@ -8497,6 +8489,11 @@
             consumedBatches = false;
         }
         doProcessInputEvents();
+        if (consumedBatches) {
+            // Must be done after we processed the input events, to mark the completion of the frame
+            // from the input point of view
+            mInputEventAssigner.onChoreographerCallback();
+        }
         return consumedBatches;
     }
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1819da4..7e9a850 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -613,6 +613,10 @@
      * For example, for activities in multi-window mode, the metrics returned are based on the
      * current bounds that the user has selected for the {@link android.app.Activity Activity}'s
      * task.
+     * <p>
+     * In most scenarios, {@link #getCurrentWindowMetrics()} rather than
+     * {@link #getMaximumWindowMetrics()} is the correct API to use, since it ensures values reflect
+     * window size when the app is not fullscreen.
      *
      * @see #getMaximumWindowMetrics()
      * @see WindowMetrics
@@ -624,26 +628,27 @@
     /**
      * Returns the largest {@link WindowMetrics} an app may expect in the current system state.
      * <p>
-     * The metrics describe the size of the largest potential area the window might occupy with
-     * {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets}
-     * such a window would have.
-     * <p>
      * The value of this is based on the largest <b>potential</b> windowing state of the system.
      *
      * For example, for activities in multi-window mode, the metrics returned are based on the
      * what the bounds would be if the user expanded the {@link android.app.Activity Activity}'s
      * task to cover the entire screen.
-     *
+     * <p>
+     * The metrics describe the size of the largest potential area the window might occupy with
+     * {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets}
+     * such a window would have.
+     * <p>
      * Note that this might still be smaller than the size of the physical display if certain areas
      * of the display are not available to windows created in this {@link Context}.
-     * <p>
+     *
      * For example, given that there's a device which have a multi-task mode to limit activities
      * to a half screen. In this case, {@link #getMaximumWindowMetrics()} reports the bounds of
-     * the half screen which the activity is located, while {@link Display#getRealSize(Point)} still
-     * reports the bounds of the whole physical display.
-     *
-     * Despite this, {@link #getMaximumWindowMetrics()} and {@link Display#getRealSize(Point)}
-     * reports the same bounds in general.
+     * the half screen which the activity is located.
+     * <p>
+     * <b>Generally {@link #getCurrentWindowMetrics()} is the correct API to use</b> for choosing
+     * UI layouts. {@link #getMaximumWindowMetrics()} are only appropriate when the application
+     * needs to know the largest possible size it can occupy if the user expands/maximizes it on the
+     * screen.
      *
      * @see #getCurrentWindowMetrics()
      * @see WindowMetrics
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7cc2db1..72d403e 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -149,8 +149,15 @@
         if (((attrs.inputFeatures &
                 WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) {
             try {
-                mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
+                if(mRealWm instanceof IWindowSession.Stub) {
+                    mRealWm.grantInputChannel(displayId,
+                        new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"),
+                        window, mHostInputToken, attrs.flags, attrs.privateFlags, attrs.type,
+                        outInputChannel);
+                } else {
+                    mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
                         attrs.privateFlags, attrs.type, outInputChannel);
+                }
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to grant input to surface: ", e);
             }
@@ -293,8 +300,14 @@
         if ((attrChanges & WindowManager.LayoutParams.FLAGS_CHANGED) != 0
                 && state.mInputChannelToken != null) {
             try {
-                mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc,
-                        attrs.flags, attrs.privateFlags, state.mInputRegion);
+                if(mRealWm instanceof IWindowSession.Stub) {
+                    mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId,
+                            new SurfaceControl(sc, "WindowlessWindowManager.relayout"),
+                            attrs.flags, attrs.privateFlags, state.mInputRegion);
+                } else {
+                    mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc,
+                            attrs.flags, attrs.privateFlags, state.mInputRegion);
+                }
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to update surface input channel: ", e);
             }
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index f63749b..8d1271d 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -23,7 +23,9 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
@@ -113,6 +115,8 @@
 
     private final Object mInstanceLock = new Object();
 
+    private Handler mMainHandler;
+
     private volatile int mInteractionId = -1;
 
     private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
@@ -123,6 +127,11 @@
 
     private Message mSameThreadMessage;
 
+    private int mInteractionIdWaitingForPrefetchResult;
+    private int mConnectionIdWaitingForPrefetchResult;
+    private String[] mPackageNamesForNextPrefetchResult;
+    private Runnable mPrefetchResultRunnable;
+
     /**
      * @return The client for the current thread.
      */
@@ -197,6 +206,10 @@
 
     private AccessibilityInteractionClient() {
         /* reducing constructor visibility */
+        Looper mainLooper = Looper.getMainLooper();
+        if (mainLooper != null) {
+            mMainHandler = new Handler(mainLooper);
+        }
     }
 
     /**
@@ -451,16 +464,16 @@
                     Binder.restoreCallingIdentity(identityToken);
                 }
                 if (packageNames != null) {
-                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
-                            interactionId);
-                    finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
-                            bypassCache, packageNames);
-                    if (infos != null && !infos.isEmpty()) {
-                        for (int i = 1; i < infos.size(); i++) {
-                            infos.get(i).recycle();
-                        }
-                        return infos.get(0);
+                    AccessibilityNodeInfo info =
+                            getFindAccessibilityNodeInfoResultAndClear(interactionId);
+                    if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_MASK) != 0
+                            && info != null) {
+                        setInteractionWaitingForPrefetchResult(interactionId, connectionId,
+                                packageNames);
                     }
+                    finalizeAndCacheAccessibilityNodeInfo(info, connectionId,
+                            bypassCache, packageNames);
+                    return info;
                 }
             } else {
                 if (DEBUG) {
@@ -474,6 +487,15 @@
         return null;
     }
 
+    private void setInteractionWaitingForPrefetchResult(int interactionId, int connectionId,
+            String[] packageNames) {
+        synchronized (mInstanceLock) {
+            mInteractionIdWaitingForPrefetchResult = interactionId;
+            mConnectionIdWaitingForPrefetchResult = connectionId;
+            mPackageNamesForNextPrefetchResult = packageNames;
+        }
+    }
+
     private static String idToString(int accessibilityWindowId, long accessibilityNodeId) {
         return accessibilityWindowId + "/"
                 + AccessibilityNodeInfo.idToString(accessibilityNodeId);
@@ -829,6 +851,59 @@
     }
 
     /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setPrefetchAccessibilityNodeInfoResult(@NonNull List<AccessibilityNodeInfo> infos,
+                                                       int interactionId) {
+        List<AccessibilityNodeInfo> infosCopy = null;
+        int mConnectionIdWaitingForPrefetchResultCopy = -1;
+        String[] mPackageNamesForNextPrefetchResultCopy = null;
+
+        synchronized (mInstanceLock) {
+            if (!infos.isEmpty() && mInteractionIdWaitingForPrefetchResult == interactionId) {
+                if (mMainHandler != null) {
+                    if (mPrefetchResultRunnable != null) {
+                        mMainHandler.removeCallbacks(mPrefetchResultRunnable);
+                        mPrefetchResultRunnable = null;
+                    }
+                    /**
+                     * TODO(b/180957109): AccessibilityCache is prone to deadlocks
+                     * We post caching the prefetched nodes in the main thread. Using the binder
+                     * thread results in "Long monitor contention with owner main" logs where
+                     * service response times may exceed 5 seconds. This is due to the cache calling
+                     * out to the system when refreshing nodes with the lock held.
+                     */
+                    mPrefetchResultRunnable = () -> finalizeAndCacheAccessibilityNodeInfos(
+                            infos, mConnectionIdWaitingForPrefetchResult, false,
+                            mPackageNamesForNextPrefetchResult);
+                    mMainHandler.post(mPrefetchResultRunnable);
+
+                } else {
+                    for (AccessibilityNodeInfo info : infos) {
+                        infosCopy.add(new AccessibilityNodeInfo(info));
+                    }
+                    mConnectionIdWaitingForPrefetchResultCopy =
+                            mConnectionIdWaitingForPrefetchResult;
+                    mPackageNamesForNextPrefetchResultCopy =
+                            new String[mPackageNamesForNextPrefetchResult.length];
+                    for (int i = 0; i < mPackageNamesForNextPrefetchResult.length; i++) {
+                        mPackageNamesForNextPrefetchResultCopy[i] =
+                                mPackageNamesForNextPrefetchResult[i];
+                    }
+                }
+            }
+
+        }
+
+        if (infosCopy != null) {
+            finalizeAndCacheAccessibilityNodeInfos(
+                    infosCopy, mConnectionIdWaitingForPrefetchResultCopy, false,
+                    mPackageNamesForNextPrefetchResultCopy);
+        }
+    }
+
+    /**
      * Gets the result of a request to perform an accessibility action.
      *
      * @param interactionId The interaction id to match the result with the request.
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
index 049bb31..231e75a 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
@@ -47,6 +47,15 @@
         int interactionId);
 
     /**
+     * Sets the result of a prefetch request that returns {@link AccessibilityNodeInfo}s.
+     *
+     * @param root The {@link AccessibilityNodeInfo} for which the prefetching is based off of.
+     * @param infos The result {@link AccessibilityNodeInfo}s.
+     */
+    void setPrefetchAccessibilityNodeInfoResult(
+        in List<AccessibilityNodeInfo> infos, int interactionId);
+
+    /**
      * Sets the result of a request to perform an accessibility action.
      *
      * @param Whether the action was performed.
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index b1b443f..a641110 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -22,6 +22,7 @@
 import android.view.contentcapture.DataRemovalRequest;
 import android.view.contentcapture.DataShareRequest;
 import android.view.contentcapture.IDataShareWriteAdapter;
+import android.view.contentcapture.IContentCaptureOptionsCallback;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
 
@@ -101,4 +102,10 @@
      * Sets whether the default service should be used.
      */
     void setDefaultServiceEnabled(int userId, boolean enabled);
+
+    /**
+     * Registers a listener to handle updates ContentCaptureOptions from server.
+     */
+    void registerContentCaptureOptionsCallback(String packageName,
+                                               in IContentCaptureOptionsCallback callback);
 }
diff --git a/core/java/android/view/contentcapture/IContentCaptureOptionsCallback.aidl b/core/java/android/view/contentcapture/IContentCaptureOptionsCallback.aidl
new file mode 100644
index 0000000..b0f062d
--- /dev/null
+++ b/core/java/android/view/contentcapture/IContentCaptureOptionsCallback.aidl
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+import android.content.ContentCaptureOptions;
+
+/**
+  * Callback for changes to content capture options made by ContentCaptureService.
+  * Callback interface used by IContentCaptureManager to send asynchronous
+  * notifications back to its clients.  Note that this is a
+  * one-way interface so the server does not block waiting for the client.
+  *
+  * @hide
+  */
+oneway interface IContentCaptureOptionsCallback {
+    void setContentCaptureOptions(in ContentCaptureOptions options);
+}
diff --git a/core/java/android/view/displayhash/DisplayHashResultCallback.java b/core/java/android/view/displayhash/DisplayHashResultCallback.java
index 15b29ad..04d29ee 100644
--- a/core/java/android/view/displayhash/DisplayHashResultCallback.java
+++ b/core/java/android/view/displayhash/DisplayHashResultCallback.java
@@ -66,12 +66,19 @@
      */
     int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4;
 
+    /**
+     * The hash algorithm sent to generate the hash was invalid. This means the value is not one
+     * of the supported values in {@link DisplayHashManager#getSupportedHashAlgorithms()}
+     */
+    int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5;
+
     /** @hide */
     @IntDef(prefix = {"DISPLAY_HASH_ERROR_"}, value = {
             DISPLAY_HASH_ERROR_UNKNOWN,
             DISPLAY_HASH_ERROR_INVALID_BOUNDS,
             DISPLAY_HASH_ERROR_MISSING_WINDOW,
-            DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN
+            DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN,
+            DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface DisplayHashErrorCode {
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 2975afc..d0959f9 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -90,6 +90,12 @@
     static final String SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND =
             "system_textclassifier_api_timeout_in_second";
 
+    /**
+     * The max amount of characters before and after the selected text that are passed to the
+     * TextClassifier for the smart selection.
+     */
+    private static final String SMART_SELECTION_TRIM_DELTA = "smart_selection_trim_delta";
+
     private static final String DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = null;
     private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
     private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
@@ -100,6 +106,7 @@
     private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
     private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
     private static final long SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND_DEFAULT = 60;
+    private static final int SMART_SELECTION_TRIM_DELTA_DEFAULT = 120;
 
     @Nullable
     public String getTextClassifierServicePackageOverride() {
@@ -155,6 +162,12 @@
                 SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND_DEFAULT);
     }
 
+    public int getSmartSelectionTrimDelta() {
+        return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                SMART_SELECTION_TRIM_DELTA,
+                SMART_SELECTION_TRIM_DELTA_DEFAULT);
+    }
+
     void dump(IndentingPrintWriter pw) {
         pw.println("TextClassificationConstants:");
         pw.increaseIndent();
@@ -170,6 +183,7 @@
                 getTextClassifierServicePackageOverride()).println();
         pw.print(SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND,
                 getSystemTextClassifierApiTimeoutInSecond()).println();
+        pw.print(SMART_SELECTION_TRIM_DELTA, getSmartSelectionTrimDelta()).println();
         pw.decreaseIndent();
     }
 }
\ No newline at end of file
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 1b62266..dc42ad5 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -29,7 +29,6 @@
 import android.graphics.Paint;
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
-import android.graphics.RenderEffect;
 import android.graphics.RenderNode;
 import android.os.Build;
 import android.util.AttributeSet;
@@ -83,6 +82,8 @@
     public @interface EdgeEffectType {
     }
 
+    private static final float DEFAULT_MAX_STRETCH_INTENSITY = 1.5f;
+
     @SuppressWarnings("UnusedDeclaration")
     private static final String TAG = "EdgeEffect";
 
@@ -128,6 +129,8 @@
 
     private long mStartTime;
     private float mDuration;
+    private float mStretchIntensity = DEFAULT_MAX_STRETCH_INTENSITY;
+    private float mStretchDistance = -1f;
 
     private final Interpolator mInterpolator;
 
@@ -146,6 +149,8 @@
     private float mPullDistance;
 
     private final Rect mBounds = new Rect();
+    private float mWidth;
+    private float mHeight;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769450)
     private final Paint mPaint = new Paint();
     private float mRadius;
@@ -202,6 +207,19 @@
         mBaseGlowScale = h > 0 ? Math.min(oh / h, 1.f) : 1.f;
 
         mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
+
+        mWidth = width;
+        mHeight = height;
+    }
+
+    /**
+     * Configure the distance in pixels to stretch the content. This is only consumed as part
+     * if {@link #setType(int)} is set to {@link #TYPE_STRETCH}
+     * @param stretchDistance Stretch distance in pixels when the target View is overscrolled
+     * @hide
+     */
+    public void setStretchDistance(float stretchDistance) {
+        mStretchDistance = stretchDistance;
     }
 
     /**
@@ -437,6 +455,13 @@
     }
 
     /**
+     * @hide
+     */
+    public void setMaxStretchIntensity(float stretchIntensity) {
+        mStretchIntensity = stretchIntensity;
+    }
+
+    /**
      * Set or clear the blend mode. A blend mode defines how source pixels
      * (generated by a drawing command) are composited with the destination pixels
      * (content of the render target).
@@ -520,23 +545,55 @@
             RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
             if (mTmpMatrix == null) {
                 mTmpMatrix = new Matrix();
-                mTmpPoints = new float[4];
+                mTmpPoints = new float[12];
             }
             //noinspection deprecation
             recordingCanvas.getMatrix(mTmpMatrix);
-            mTmpPoints[0] = mBounds.width() * mDisplacement;
-            mTmpPoints[1] = mDistance * mBounds.height();
-            mTmpPoints[2] = mTmpPoints[0];
-            mTmpPoints[3] = 0;
+
+            mTmpPoints[0] = 0;
+            mTmpPoints[1] = 0; // top-left
+            mTmpPoints[2] = mWidth;
+            mTmpPoints[3] = 0; // top-right
+            mTmpPoints[4] = mWidth;
+            mTmpPoints[5] = mHeight; // bottom-right
+            mTmpPoints[6] = 0;
+            mTmpPoints[7] = mHeight; // bottom-left
+            mTmpPoints[8] = mWidth * mDisplacement;
+            mTmpPoints[9] = 0; // drag start point
+            mTmpPoints[10] = mWidth * mDisplacement;
+            mTmpPoints[11] = mHeight * mDistance; // drag point
             mTmpMatrix.mapPoints(mTmpPoints);
-            float x = mTmpPoints[0] - mTmpPoints[2];
-            float y = mTmpPoints[1] - mTmpPoints[3];
 
             RenderNode renderNode = recordingCanvas.mNode;
 
-            // TODO: use stretchy RenderEffect and use internal API when it is ready
-            // TODO: wrap existing RenderEffect
-            renderNode.setRenderEffect(RenderEffect.createOffsetEffect(x, y));
+            float left = renderNode.getLeft()
+                    + min(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]);
+            float top = renderNode.getTop()
+                    + min(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]);
+            float right = renderNode.getLeft()
+                    + max(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]);
+            float bottom = renderNode.getTop()
+                    + max(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]);
+            // assume rotations of increments of 90 degrees
+            float x = mTmpPoints[10] - mTmpPoints[8];
+            float width = right - left;
+            float vecX = Math.max(-1f, Math.min(1f, x / width));
+            float y = mTmpPoints[11] - mTmpPoints[9];
+            float height = bottom - top;
+            float vecY = Math.max(-1f, Math.min(1f, y / height));
+            renderNode.stretch(
+                    left,
+                    top,
+                    right,
+                    bottom,
+                    vecX * mStretchIntensity,
+                    vecY * mStretchIntensity,
+                    // TODO (njawad/mount) figure out proper stretch distance from UX
+                    //  for now leverage placeholder logic if no stretch distance is provided to
+                    //  consume the displacement ratio times the minimum of the width or height
+                    mStretchDistance > 0 ? mStretchDistance :
+                            (mDisplacement * Math.min(mWidth, mHeight))
+            );
         }
 
         boolean oneLastFrame = false;
@@ -548,6 +605,18 @@
         return mState != STATE_IDLE || oneLastFrame;
     }
 
+    private float min(float f1, float f2, float f3, float f4) {
+        float min = Math.min(f1, f2);
+        min = Math.min(min, f3);
+        return Math.min(min, f4);
+    }
+
+    private float max(float f1, float f2, float f3, float f4) {
+        float max = Math.max(f1, f2);
+        max = Math.max(max, f3);
+        return Math.max(max, f4);
+    }
+
     /**
      * Return the maximum height that the edge effect will be drawn at given the original
      * {@link #setSize(int, int) input size}.
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 23915e0..bf552e2 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -249,6 +249,26 @@
     }
 
     /**
+     * API used for prototyping stretch effect parameters in framework sample apps
+     * @hide
+     */
+    public void setEdgeEffectIntensity(float intensity) {
+        mEdgeGlowLeft.setMaxStretchIntensity(intensity);
+        mEdgeGlowRight.setMaxStretchIntensity(intensity);
+        invalidate();
+    }
+
+    /**
+     * API used for prototyping stretch effect parameters in the framework sample apps
+     * @hide
+     */
+    public void setStretchDistance(float distance) {
+        mEdgeGlowLeft.setStretchDistance(distance);
+        mEdgeGlowRight.setStretchDistance(distance);
+        invalidate();
+    }
+
+    /**
      * Sets the right edge effect color.
      *
      * @param color The color for the right edge effect.
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2cf50bb..dfb2263 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -51,7 +51,6 @@
 import android.graphics.Bitmap;
 import android.graphics.BlendMode;
 import android.graphics.Outline;
-import android.graphics.PointF;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -76,6 +75,7 @@
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
+import android.util.SizeF;
 import android.util.SparseIntArray;
 import android.util.TypedValue;
 import android.util.TypedValue.ComplexDimensionUnit;
@@ -353,7 +353,7 @@
      * Only to be used on children views used in a {@link RemoteViews} with
      * {@link RemoteViews#hasSizedRemoteViews()}.
      */
-    private PointF mIdealSize = null;
+    private SizeF mIdealSize = null;
 
     @ApplyFlags
     private int mApplyFlags = 0;
@@ -3042,11 +3042,11 @@
         return mSizedRemoteViews != null;
     }
 
-    private @Nullable PointF getIdealSize() {
+    private @Nullable SizeF getIdealSize() {
         return mIdealSize;
     }
 
-    private void setIdealSize(@Nullable PointF size) {
+    private void setIdealSize(@Nullable SizeF size) {
         mIdealSize = size;
     }
 
@@ -3094,13 +3094,18 @@
      * Create a new RemoteViews object that will inflate the layout with the closest size
      * specification.
      *
-     * The default remote views in that case is always the smallest one provided.
+     * The default remote views in that case is always the one with the smallest area.
+     *
+     * If the {@link RemoteViews} host provides the size of the view, the layout with the largest
+     * area that fits entirely in the provided size will be used (i.e. the width and height of
+     * the layout must be less than the size of the view, with a 1dp margin to account for
+     * rounding). If no layout fits in the view, the layout with the smallest area will be used.
      *
      * @param remoteViews Mapping of size to layout.
      * @throws IllegalArgumentException if the map is empty, there are more than
      *   MAX_INIT_VIEW_COUNT layouts or the remote views are not all from the same application.
      */
-    public RemoteViews(@NonNull Map<PointF, RemoteViews> remoteViews) {
+    public RemoteViews(@NonNull Map<SizeF, RemoteViews> remoteViews) {
         if (remoteViews.isEmpty()) {
             throw new IllegalArgumentException("The set of RemoteViews cannot be empty");
         }
@@ -3135,8 +3140,8 @@
         RemoteViews smallestView = null;
         while (remoteViews.hasNext()) {
             RemoteViews view = remoteViews.next();
-            PointF size = view.getIdealSize();
-            float newViewArea = size.x * size.y;
+            SizeF size = view.getIdealSize();
+            float newViewArea = size.getWidth() * size.getHeight();
             if (smallestView != null && !view.hasSameAppInfo(smallestView.mApplication)) {
                 throw new IllegalArgumentException(
                         "All RemoteViews must share the same package and user");
@@ -3239,7 +3244,7 @@
         if (mode == MODE_NORMAL) {
             mApplication = parcel.readInt() == 0 ? info :
                     ApplicationInfo.CREATOR.createFromParcel(parcel);
-            mIdealSize = parcel.readInt() == 0 ? null : PointF.CREATOR.createFromParcel(parcel);
+            mIdealSize = parcel.readInt() == 0 ? null : SizeF.CREATOR.createFromParcel(parcel);
             mLayoutId = parcel.readInt();
             mLightBackgroundLayoutId = parcel.readInt();
 
@@ -4625,9 +4630,9 @@
      *
      * This is particularly useful when we only care about the ordering of the distances.
      */
-    private static float squareDistance(PointF p1, PointF p2) {
-        float dx = p1.x - p2.x;
-        float dy = p1.y - p2.y;
+    private static float squareDistance(SizeF p1, SizeF p2) {
+        float dx = p1.getWidth() - p2.getWidth();
+        float dy = p1.getHeight() - p2.getHeight();
         return dx * dx + dy * dy;
     }
 
@@ -4637,31 +4642,17 @@
      * A layout fits on a widget if the widget size is known (i.e. not null) and both dimensions
      * are smaller than the ones of the widget, adding some padding to account for rounding errors.
      */
-    private static boolean fitsIn(PointF sizeLayout, @Nullable PointF sizeWidget) {
-        return sizeWidget != null && (Math.ceil(sizeWidget.x) + 1 > sizeLayout.x)
-                && (Math.ceil(sizeWidget.y) + 1 > sizeLayout.y);
+    private static boolean fitsIn(SizeF sizeLayout, @Nullable SizeF sizeWidget) {
+        return sizeWidget != null && (Math.ceil(sizeWidget.getWidth()) + 1 > sizeLayout.getWidth())
+                && (Math.ceil(sizeWidget.getHeight()) + 1 > sizeLayout.getHeight());
     }
 
-    /**
-     * Returns the most appropriate {@link RemoteViews} given the context and, if not null, the
-     * size of the widget.
-     *
-     * If {@link RemoteViews#hasSizedRemoteViews()} returns true, the most appropriate view is
-     * the one that fits in the widget (according to {@link RemoteViews#fitsIn}) and has the
-     * diagonal the most similar to the widget. If no layout fits or the size of the widget is
-     * not specified, the one with the smallest area will be chosen.
-     */
-    private RemoteViews getRemoteViewsToApply(@NonNull Context context,
-            @Nullable PointF widgetSize) {
-        if (!hasSizedRemoteViews()) {
-            // If there isn't multiple remote views, fall back on the previous methods.
-            return getRemoteViewsToApply(context);
-        }
+    private RemoteViews findBestFitLayout(@NonNull SizeF widgetSize) {
         // Find the better remote view
         RemoteViews bestFit = null;
         float bestSqDist = Float.MAX_VALUE;
         for (RemoteViews layout : mSizedRemoteViews) {
-            PointF layoutSize = layout.getIdealSize();
+            SizeF layoutSize = layout.getIdealSize();
             if (fitsIn(layoutSize, widgetSize)) {
                 if (bestFit == null) {
                     bestFit = layout;
@@ -4682,6 +4673,46 @@
         return bestFit;
     }
 
+    /**
+     * Returns the most appropriate {@link RemoteViews} given the context and, if not null, the
+     * size of the widget.
+     *
+     * If {@link RemoteViews#hasSizedRemoteViews()} returns true, the most appropriate view is
+     * the one that fits in the widget (according to {@link RemoteViews#fitsIn}) and has the
+     * diagonal the most similar to the widget. If no layout fits or the size of the widget is
+     * not specified, the one with the smallest area will be chosen.
+     *
+     * @hide
+     */
+    public RemoteViews getRemoteViewsToApply(@NonNull Context context,
+            @Nullable SizeF widgetSize) {
+        if (!hasSizedRemoteViews()) {
+            // If there isn't multiple remote views, fall back on the previous methods.
+            return getRemoteViewsToApply(context);
+        }
+        return findBestFitLayout(widgetSize);
+    }
+
+    /**
+     * Checks whether the change of size will lead to using a different {@link RemoteViews}.
+     *
+     * @hide
+     */
+    @Nullable
+    public RemoteViews getRemoteViewsToApplyIfDifferent(@Nullable SizeF oldSize,
+            @NonNull SizeF newSize) {
+        if (!hasSizedRemoteViews()) {
+            return null;
+        }
+        RemoteViews oldBestFit = oldSize == null ? findSmallestRemoteView() : findBestFitLayout(
+                oldSize);
+        RemoteViews newBestFit = findBestFitLayout(newSize);
+        if (oldBestFit != newBestFit) {
+            return newBestFit;
+        }
+        return null;
+    }
+
 
     /**
      * Inflates the view hierarchy represented by this object and applies
@@ -4705,7 +4736,7 @@
 
     /** @hide */
     public View apply(@NonNull Context context, @NonNull ViewGroup parent,
-            @Nullable InteractionHandler handler, @Nullable PointF size) {
+            @Nullable InteractionHandler handler, @Nullable SizeF size) {
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
         View result = inflateView(context, rvToApply, parent);
@@ -4722,7 +4753,7 @@
     /** @hide */
     public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
             @Nullable InteractionHandler handler, @StyleRes int applyThemeResId,
-            @Nullable PointF size) {
+            @Nullable SizeF size) {
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
         View result = inflateView(context, rvToApply, parent, applyThemeResId, null);
@@ -4732,7 +4763,7 @@
 
     /** @hide */
     public View apply(Context context, ViewGroup parent, InteractionHandler handler,
-            @NonNull PointF size, @Nullable ColorResources colorResources) {
+            @NonNull SizeF size, @Nullable ColorResources colorResources) {
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
         View result = inflateView(context, rvToApply, parent, 0, colorResources);
@@ -4828,21 +4859,21 @@
     /** @hide */
     public CancellationSignal applyAsync(Context context, ViewGroup parent,
             Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
-            PointF size) {
+            SizeF size) {
         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,
+            OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
         return getAsyncApplyTask(context, parent, listener, handler, size, colorResources)
                 .startTaskOnExecutor(executor);
     }
 
     private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
-            OnViewAppliedListener listener, InteractionHandler handler, PointF size,
+            OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
                 handler, colorResources, null /* result */);
@@ -4968,7 +4999,7 @@
     }
 
     /** @hide */
-    public void reapply(Context context, View v, InteractionHandler handler, PointF size,
+    public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
@@ -5012,7 +5043,7 @@
 
     /** @hide */
     public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
-            OnViewAppliedListener listener, InteractionHandler handler, PointF size,
+            OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 65f3da7..3006729 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -281,6 +281,26 @@
     }
 
     /**
+     * API used for prototyping stretch effect parameters in framework sample apps
+     * @hide
+     */
+    public void setEdgeEffectIntensity(float intensity) {
+        mEdgeGlowTop.setMaxStretchIntensity(intensity);
+        mEdgeGlowBottom.setMaxStretchIntensity(intensity);
+        invalidate();
+    }
+
+    /**
+     * API used for prototyping stretch effect parameters in the framework sample apps
+     * @hide
+     */
+    public void setStretchDistance(float distance) {
+        mEdgeGlowTop.setStretchDistance(distance);
+        mEdgeGlowBottom.setStretchDistance(distance);
+        invalidate();
+    }
+
+    /**
      * Sets the bottom edge effect color.
      *
      * @param color The color for the bottom edge effect.
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 6f18920..eb6bce4 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -36,6 +36,7 @@
 import android.text.util.Linkify;
 import android.util.Log;
 import android.view.ActionMode;
+import android.view.ViewConfiguration;
 import android.view.textclassifier.ExtrasUtils;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.SelectionEvent.InvocationMethod;
@@ -1056,10 +1057,12 @@
      */
     private static final class TextClassificationHelper {
 
-        private static final int TRIM_DELTA = 120;  // characters
+        // The fixed upper bound of context size.
+        private static final int TRIM_DELTA_UPPER_BOUND = 240;
 
         private final Context mContext;
         private Supplier<TextClassifier> mTextClassifier;
+        private final ViewConfiguration mViewConfiguration;
 
         /** The original TextView text. **/
         private String mText;
@@ -1088,12 +1091,13 @@
         private SelectionResult mLastClassificationResult;
 
         /** Whether the TextClassifier has been initialized. */
-        private boolean mHot;
+        private boolean mInitialized;
 
         TextClassificationHelper(Context context, Supplier<TextClassifier> textClassifier,
                 CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
             init(textClassifier, text, selectionStart, selectionEnd, locales);
             mContext = Objects.requireNonNull(context);
+            mViewConfiguration = ViewConfiguration.get(mContext);
         }
 
         @UiThread
@@ -1110,13 +1114,13 @@
 
         @WorkerThread
         public SelectionResult classifyText() {
-            mHot = true;
+            mInitialized = true;
             return performClassification(null /* selection */);
         }
 
         @WorkerThread
         public SelectionResult suggestSelection() {
-            mHot = true;
+            mInitialized = true;
             trimText();
             final TextSelection selection;
             if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
@@ -1148,16 +1152,15 @@
         /**
          * Maximum time (in milliseconds) to wait for a textclassifier result before timing out.
          */
-        // TODO: Consider making this a ViewConfiguration.
         public int getTimeoutDuration() {
-            if (mHot) {
-                return 200;
+            if (mInitialized) {
+                return mViewConfiguration.getSmartSelectionInitializedTimeout();
             } else {
                 // Return a slightly larger number than usual when the TextClassifier is first
                 // initialized. Initialization would usually take longer than subsequent calls to
                 // the TextClassifier. The impact of this on the UI is that we do not show the
                 // selection handles or toolbar until after this timeout.
-                return 500;
+                return mViewConfiguration.getSmartSelectionInitializingTimeout();
             }
         }
 
@@ -1205,8 +1208,11 @@
         }
 
         private void trimText() {
-            mTrimStart = Math.max(0, mSelectionStart - TRIM_DELTA);
-            final int referenceEnd = Math.min(mText.length(), mSelectionEnd + TRIM_DELTA);
+            final int trimDelta = Math.min(
+                    TextClassificationManager.getSettings(mContext).getSmartSelectionTrimDelta(),
+                    TRIM_DELTA_UPPER_BOUND);
+            mTrimStart = Math.max(0, mSelectionStart - trimDelta);
+            final int referenceEnd = Math.min(mText.length(), mSelectionEnd + trimDelta);
             mTrimmedText = mText.subSequence(mTrimStart, referenceEnd);
             mRelativeStart = mSelectionStart - mTrimStart;
             mRelativeEnd = mSelectionEnd - mTrimStart;
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 4421f06..141f47b 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -139,8 +139,8 @@
         boolean matches(@NonNull TransitionInfo info) {
             for (int i = info.getChanges().size() - 1; i >= 0; --i) {
                 final TransitionInfo.Change change = info.getChanges().get(i);
-                if (change.getParent() != null) {
-                    // Only look at the top animating windows.
+                if (!TransitionInfo.isIndependent(change, info)) {
+                    // Only look at independent animating windows.
                     continue;
                 }
                 if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index d1d49b6..499ce25 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -246,6 +246,33 @@
         return sb.toString();
     }
 
+    /**
+     * Indication that `change` is independent of parents (ie. it has a different type of
+     * transition vs. "going along for the ride")
+     */
+    public static boolean isIndependent(TransitionInfo.Change change, TransitionInfo info) {
+        // If the change has no parent (it is root), then it is independent
+        if (change.getParent() == null) return true;
+
+        // non-visibility changes will just be folded into the parent change, so they aren't
+        // independent either.
+        if (change.getMode() == TRANSIT_CHANGE) return false;
+
+        TransitionInfo.Change parentChg = info.getChange(change.getParent());
+        while (parentChg != null) {
+            // If the parent is a visibility change, it will include the results of all child
+            // changes into itself, so none of its children can be independent.
+            if (parentChg.getMode() != TRANSIT_CHANGE) return false;
+
+            // If there are no more parents left, then all the parents, so far, have not been
+            // visibility changes which means this change is indpendent.
+            if (parentChg.getParent() == null) return true;
+
+            parentChg = info.getChange(parentChg.getParent());
+        }
+        return false;
+    }
+
     /** Represents the change a WindowContainer undergoes during a transition */
     public static final class Change implements Parcelable {
         private final WindowContainerToken mContainer;
diff --git a/core/java/com/android/internal/graphics/palette/WuQuantizer.java b/core/java/com/android/internal/graphics/palette/WuQuantizer.java
index 01e45f6..66206bf 100644
--- a/core/java/com/android/internal/graphics/palette/WuQuantizer.java
+++ b/core/java/com/android/internal/graphics/palette/WuQuantizer.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.graphics.palette;
 
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -120,7 +119,11 @@
             }
         }
 
-        for (k = 0; k < mMaxColors; ++k) {
+        // If extraction is run on a set of pixels whose count is less than the
+        // number of max colors, then colors.length < max colors, and accesses
+        // to colors[index] inside the for loop throw an ArrayOutOfBoundsException.
+        int numColorsToCreate = (int) Math.min(mMaxColors, colors.length);
+        for (k = 0; k < numColorsToCreate; ++k) {
             weight = getVolume(cube[k], mWt);
             if (weight > 0) {
                 red = (int) (getVolume(cube[k], mMr) / weight);
diff --git a/core/java/com/android/internal/infra/GlobalWhitelistState.java b/core/java/com/android/internal/infra/GlobalWhitelistState.java
index 3c081e2..7529536 100644
--- a/core/java/com/android/internal/infra/GlobalWhitelistState.java
+++ b/core/java/com/android/internal/infra/GlobalWhitelistState.java
@@ -99,6 +99,18 @@
     }
 
     /**
+     * Gets packages that are either entirely allowlisted or have components that are allowlisted
+     * for the given user.
+     */
+    public ArraySet<String> getWhitelistedPackages(@UserIdInt int userId) {
+        synchronized (mGlobalWhitelistStateLock) {
+            if (mWhitelisterHelpers == null) return null;
+            final WhitelistHelper helper = mWhitelisterHelpers.get(userId);
+            return helper == null ? null : helper.getWhitelistedPackages();
+        }
+    }
+
+    /**
      * Resets the allowlist for the given user.
      */
     public void resetWhitelist(@NonNull int userId) {
diff --git a/core/java/com/android/internal/infra/WhitelistHelper.java b/core/java/com/android/internal/infra/WhitelistHelper.java
index 1d76090..3e93106 100644
--- a/core/java/com/android/internal/infra/WhitelistHelper.java
+++ b/core/java/com/android/internal/infra/WhitelistHelper.java
@@ -140,6 +140,15 @@
         return mWhitelistedPackages == null ? null : mWhitelistedPackages.get(packageName);
     }
 
+    /**
+     * Returns a set of all packages that are either entirely allowlisted or have components that
+     * are allowlisted.
+     */
+    @Nullable
+    public ArraySet<String> getWhitelistedPackages() {
+        return mWhitelistedPackages == null ? null : new ArraySet<>(mWhitelistedPackages.keySet());
+    }
+
     @Override
     public String toString() {
         return "WhitelistHelper[" + mWhitelistedPackages + ']';
diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java
index 2113173..3958b9e 100644
--- a/core/java/com/android/internal/inputmethod/CallbackUtils.java
+++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java
@@ -225,4 +225,31 @@
             callback.onResult();
         } catch (RemoteException ignored) { }
     }
+
+    /**
+     * A utility method using given {@link IIInputContentUriTokenResultCallback} to callback the
+     * result.
+     *
+     * @param callback {@link IIInputContentUriTokenResultCallback} to be called back.
+     * @param resultSupplier the supplier from which the result is provided.
+     */
+    public static void onResult(@NonNull IIInputContentUriTokenResultCallback callback,
+            @NonNull Supplier<IInputContentUriToken> resultSupplier) {
+        IInputContentUriToken result = null;
+        Throwable exception = null;
+
+        try {
+            result = resultSupplier.get();
+        } catch (Throwable throwable) {
+            exception = throwable;
+        }
+
+        try {
+            if (exception != null) {
+                callback.onError(ThrowableHolder.of(exception));
+                return;
+            }
+            callback.onResult(result);
+        } catch (RemoteException ignored) { }
+    }
 }
diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java
index d6a4663..ba3a343 100644
--- a/core/java/com/android/internal/inputmethod/Completable.java
+++ b/core/java/com/android/internal/inputmethod/Completable.java
@@ -444,6 +444,13 @@
     }
 
     /**
+     * @return an instance of {@link Completable.IInputContentUriToken}.
+     */
+    public static Completable.IInputContentUriToken createIInputContentUriToken() {
+        return new Completable.IInputContentUriToken();
+    }
+
+    /**
      * @return an instance of {@link Completable.Void}.
      */
     public static Completable.Void createVoid() {
@@ -497,6 +504,12 @@
             extends Values<List<android.view.inputmethod.InputMethodInfo>> { }
 
     /**
+     * Completable object of {@link IInputContentUriToken>}.
+     */
+    public static final class IInputContentUriToken
+            extends Values<com.android.internal.inputmethod.IInputContentUriToken> { }
+
+    /**
      * Await the result by the {@link Completable.Values}.
      *
      * @return the result once {@link ValueBase#onComplete()}.
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl b/core/java/com/android/internal/inputmethod/IIInputContentUriTokenResultCallback.aidl
similarity index 65%
copy from core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
copy to core/java/com/android/internal/inputmethod/IIInputContentUriTokenResultCallback.aidl
index ddb5ef8..2e6d224 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
+++ b/core/java/com/android/internal/inputmethod/IIInputContentUriTokenResultCallback.aidl
@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
-package android.content.pm.verify.domain;
+package com.android.internal.inputmethod;
 
-parcelable DomainVerificationUserSelection;
+import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.internal.inputmethod.ThrowableHolder;
+
+oneway interface IIInputContentUriTokenResultCallback {
+    void onResult(in IInputContentUriToken result);
+    void onError(in ThrowableHolder exception);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index f0e26cf..e4dd7b0 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -19,25 +19,31 @@
 import android.net.Uri;
 import android.view.inputmethod.InputMethodSubtype;
 
+import com.android.internal.inputmethod.IBooleanResultCallback;
 import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.internal.inputmethod.IIInputContentUriTokenResultCallback;
+import com.android.internal.inputmethod.IVoidResultCallback;
 
 /**
  * Defines priviledged operations that only the current IME is allowed to call.
  * Actual operations are implemented and handled by InputMethodManagerService.
  */
-interface IInputMethodPrivilegedOperations {
-    void setImeWindowStatus(int vis, int backDisposition);
-    void reportStartInput(in IBinder startInputToken);
-    IInputContentUriToken createInputContentUriToken(in Uri contentUri, in String packageName);
-    void reportFullscreenMode(boolean fullscreen);
-    void setInputMethod(String id);
-    void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype);
-    void hideMySoftInput(int flags);
-    void showMySoftInput(int flags);
-    void updateStatusIcon(String packageName, int iconId);
-    boolean switchToPreviousInputMethod();
-    boolean switchToNextInputMethod(boolean onlyCurrentIme);
-    boolean shouldOfferSwitchingToNextInputMethod();
-    void notifyUserAction();
-    void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible);
+oneway interface IInputMethodPrivilegedOperations {
+    void setImeWindowStatus(int vis, int backDisposition, in IVoidResultCallback resultCallback);
+    void reportStartInput(in IBinder startInputToken, in IVoidResultCallback resultCallback);
+    void createInputContentUriToken(in Uri contentUri, in String packageName,
+            in IIInputContentUriTokenResultCallback resultCallback);
+    void reportFullscreenMode(boolean fullscreen, in IVoidResultCallback resultCallback);
+    void setInputMethod(String id, in IVoidResultCallback resultCallback);
+    void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype,
+            in IVoidResultCallback resultCallback);
+    void hideMySoftInput(int flags, in IVoidResultCallback resultCallback);
+    void showMySoftInput(int flags, in IVoidResultCallback resultCallback);
+    void updateStatusIcon(String packageName, int iconId, in IVoidResultCallback resultCallback);
+    void switchToPreviousInputMethod(in IBooleanResultCallback resultCallback);
+    void switchToNextInputMethod(boolean onlyCurrentIme, in IBooleanResultCallback resultCallback);
+    void shouldOfferSwitchingToNextInputMethod(in IBooleanResultCallback resultCallback);
+    void notifyUserAction(in IVoidResultCallback resultCallback);
+    void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible,
+            in IVoidResultCallback resultCallback);
 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index d6730e8..04cf3f3 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -95,7 +95,8 @@
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#setImeWindowStatus(int, int)}.
+     * Calls {@link IInputMethodPrivilegedOperations#setImeWindowStatus(int, int,
+     * IVoidResultCallback)}.
      *
      * @param vis visibility flags
      * @param backDisposition disposition flags
@@ -112,14 +113,17 @@
             return;
         }
         try {
-            ops.setImeWindowStatus(vis, backDisposition);
+            final Completable.Void value = Completable.createVoid();
+            ops.setImeWindowStatus(vis, backDisposition, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#reportStartInput(IBinder)}.
+     * Calls {@link IInputMethodPrivilegedOperations#reportStartInput(IBinder,
+     * IVoidResultCallback)}.
      *
      * @param startInputToken {@link IBinder} token to distinguish startInput session
      */
@@ -130,14 +134,17 @@
             return;
         }
         try {
-            ops.reportStartInput(startInputToken);
+            final Completable.Void value = Completable.createVoid();
+            ops.reportStartInput(startInputToken, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String)}.
+     * Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String,
+     * IIInputContentUriTokenResultCallback)}.
      *
      * @param contentUri Content URI to which a temporary read permission should be granted
      * @param packageName Indicates what package needs to have a temporary read permission
@@ -151,7 +158,10 @@
             return null;
         }
         try {
-            return ops.createInputContentUriToken(contentUri, packageName);
+            final Completable.IInputContentUriToken value =
+                    Completable.createIInputContentUriToken();
+            ops.createInputContentUriToken(contentUri, packageName, ResultCallbacks.of(value));
+            return Completable.getResult(value);
         } catch (RemoteException e) {
             // For historical reasons, this error was silently ignored.
             // Note that the caller already logs error so we do not need additional Log.e() here.
@@ -161,7 +171,8 @@
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#reportFullscreenMode(boolean)}.
+     * Calls {@link IInputMethodPrivilegedOperations#reportFullscreenMode(boolean,
+     * IVoidResultCallback)}.
      *
      * @param fullscreen {@code true} if the IME enters full screen mode
      */
@@ -172,14 +183,17 @@
             return;
         }
         try {
-            ops.reportFullscreenMode(fullscreen);
+            final Completable.Void value = Completable.createVoid();
+            ops.reportFullscreenMode(fullscreen, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#updateStatusIcon(String, int)}.
+     * Calls {@link IInputMethodPrivilegedOperations#updateStatusIcon(String, int,
+     * IVoidResultCallback)}.
      *
      * @param packageName package name from which the status icon should be loaded
      * @param iconResId resource ID of the icon to be loaded
@@ -191,14 +205,16 @@
             return;
         }
         try {
-            ops.updateStatusIcon(packageName, iconResId);
+            final Completable.Void value = Completable.createVoid();
+            ops.updateStatusIcon(packageName, iconResId, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#setInputMethod(String)}.
+     * Calls {@link IInputMethodPrivilegedOperations#setInputMethod(String, IVoidResultCallback)}.
      *
      * @param id IME ID of the IME to switch to
      * @see android.view.inputmethod.InputMethodInfo#getId()
@@ -210,7 +226,9 @@
             return;
         }
         try {
-            ops.setInputMethod(id);
+            final Completable.Void value = Completable.createVoid();
+            ops.setInputMethod(id, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -218,7 +236,7 @@
 
     /**
      * Calls {@link IInputMethodPrivilegedOperations#setInputMethodAndSubtype(String,
-     * InputMethodSubtype)}
+     * InputMethodSubtype, IVoidResultCallback)}
      *
      * @param id IME ID of the IME to switch to
      * @param subtype {@link InputMethodSubtype} to switch to
@@ -231,14 +249,16 @@
             return;
         }
         try {
-            ops.setInputMethodAndSubtype(id, subtype);
+            final Completable.Void value = Completable.createVoid();
+            ops.setInputMethodAndSubtype(id, subtype, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int)}
+     * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, IVoidResultCallback)}
      *
      * @param flags additional operating flags
      * @see android.view.inputmethod.InputMethodManager#HIDE_IMPLICIT_ONLY
@@ -251,14 +271,16 @@
             return;
         }
         try {
-            ops.hideMySoftInput(flags);
+            final Completable.Void value = Completable.createVoid();
+            ops.hideMySoftInput(flags, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int)}
+     * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int, IVoidResultCallback)}
      *
      * @param flags additional operating flags
      * @see android.view.inputmethod.InputMethodManager#SHOW_IMPLICIT
@@ -271,14 +293,17 @@
             return;
         }
         try {
-            ops.showMySoftInput(flags);
+            final Completable.Void value = Completable.createVoid();
+            ops.showMySoftInput(flags, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#switchToPreviousInputMethod()}
+     * Calls {@link IInputMethodPrivilegedOperations#switchToPreviousInputMethod(
+     * IBooleanResultCallback)}
      *
      * @return {@code true} if handled
      */
@@ -289,14 +314,17 @@
             return false;
         }
         try {
-            return ops.switchToPreviousInputMethod();
+            final Completable.Boolean value = Completable.createBoolean();
+            ops.switchToPreviousInputMethod(ResultCallbacks.of(value));
+            return Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#switchToNextInputMethod(boolean)}
+     * Calls {@link IInputMethodPrivilegedOperations#switchToNextInputMethod(boolean,
+     * IBooleanResultCallback)}
      *
      * @param onlyCurrentIme {@code true} to switch to a {@link InputMethodSubtype} within the same
      *                       IME
@@ -309,14 +337,17 @@
             return false;
         }
         try {
-            return ops.switchToNextInputMethod(onlyCurrentIme);
+            final Completable.Boolean value = Completable.createBoolean();
+            ops.switchToNextInputMethod(onlyCurrentIme, ResultCallbacks.of(value));
+            return Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#shouldOfferSwitchingToNextInputMethod()}
+     * Calls {@link IInputMethodPrivilegedOperations#shouldOfferSwitchingToNextInputMethod(
+     * IBooleanResultCallback)}
      *
      * @return {@code true} if the IEM should offer a way to globally switch IME
      */
@@ -327,14 +358,16 @@
             return false;
         }
         try {
-            return ops.shouldOfferSwitchingToNextInputMethod();
+            final Completable.Boolean value = Completable.createBoolean();
+            ops.shouldOfferSwitchingToNextInputMethod(ResultCallbacks.of(value));
+            return Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#notifyUserAction()}
+     * Calls {@link IInputMethodPrivilegedOperations#notifyUserAction(IVoidResultCallback)}
      */
     @AnyThread
     public void notifyUserAction() {
@@ -343,14 +376,17 @@
             return;
         }
         try {
-            ops.notifyUserAction();
+            final Completable.Void value = Completable.createVoid();
+            ops.notifyUserAction(ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean)}.
+     * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean,
+     * IVoidResultCallback)}.
      *
      * @param showOrHideInputToken placeholder token that maps to window requesting
      *        {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or
@@ -365,7 +401,9 @@
             return;
         }
         try {
-            ops.applyImeVisibility(showOrHideInputToken, setVisible);
+            final Completable.Void value = Completable.createVoid();
+            ops.applyImeVisibility(showOrHideInputToken, setVisible, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
index 2a48c1f..c56ed2d 100644
--- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java
+++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
@@ -387,4 +387,41 @@
             }
         };
     }
+
+    /**
+     * Creates {@link IIInputContentUriTokenResultCallback.Stub} that is to set
+     * {@link Completable.IInputContentUriToken} when receiving the result.
+     *
+     * @param value {@link Completable.IInputContentUriToken} to be set when receiving the result.
+     * @return {@link IIInputContentUriTokenResultCallback.Stub} that can be passed as a binder IPC
+     * parameter.
+     */
+    @AnyThread
+    public static IIInputContentUriTokenResultCallback.Stub of(
+            @NonNull Completable.IInputContentUriToken value) {
+        final AtomicReference<WeakReference<Completable.IInputContentUriToken>>
+                atomicRef = new AtomicReference<>(new WeakReference<>(value));
+
+        return new IIInputContentUriTokenResultCallback.Stub() {
+            @BinderThread
+            @Override
+            public void onResult(IInputContentUriToken result) {
+                final Completable.IInputContentUriToken value = unwrap(atomicRef);
+                if (value == null) {
+                    return;
+                }
+                value.onComplete(result);
+            }
+
+            @BinderThread
+            @Override
+            public void onError(ThrowableHolder throwableHolder) {
+                final Completable.IInputContentUriToken value = unwrap(atomicRef);
+                if (value == null) {
+                    return;
+                }
+                value.onError(throwableHolder);
+            }
+        };
+    }
 }
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 33ee8f0..db0b48e 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -327,11 +327,8 @@
             }
             if (info.surfaceControlCallbackFired) {
                 totalFramesCount++;
-
-                // Only count missed frames if it's not stuffed.
                 if ((info.jankType & PREDICTION_ERROR) != 0
-                        || ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0
-                                && (info.jankType & BUFFER_STUFFING) == 0)) {
+                        || ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0)) {
                     Log.w(TAG, "Missed App frame:" + info.jankType);
                     missedAppFramesCount++;
                 }
diff --git a/core/java/com/android/internal/os/DischargedPowerCalculator.java b/core/java/com/android/internal/os/BatteryChargeCalculator.java
similarity index 75%
rename from core/java/com/android/internal/os/DischargedPowerCalculator.java
rename to core/java/com/android/internal/os/BatteryChargeCalculator.java
index e94020c..dc72f32 100644
--- a/core/java/com/android/internal/os/DischargedPowerCalculator.java
+++ b/core/java/com/android/internal/os/BatteryChargeCalculator.java
@@ -27,10 +27,10 @@
 /**
  * Estimates the battery discharge amounts.
  */
-public class DischargedPowerCalculator extends PowerCalculator {
+public class BatteryChargeCalculator extends PowerCalculator {
     private final double mBatteryCapacity;
 
-    public DischargedPowerCalculator(PowerProfile powerProfile) {
+    public BatteryChargeCalculator(PowerProfile powerProfile) {
         mBatteryCapacity = powerProfile.getBatteryCapacity();
     }
 
@@ -42,6 +42,16 @@
                 .setDischargedPowerRange(
                         batteryStats.getLowDischargeAmountSinceCharge() * mBatteryCapacity / 100,
                         batteryStats.getHighDischargeAmountSinceCharge() * mBatteryCapacity / 100);
+
+        final long batteryTimeRemainingMs = batteryStats.computeBatteryTimeRemaining(rawRealtimeUs);
+        if (batteryTimeRemainingMs != -1) {
+            builder.setBatteryTimeRemainingMs(batteryTimeRemainingMs / 1000);
+        }
+
+        final long chargeTimeRemainingMs = batteryStats.computeChargeTimeRemaining(rawRealtimeUs);
+        if (chargeTimeRemainingMs != -1) {
+            builder.setChargeTimeRemainingMs(chargeTimeRemainingMs / 1000);
+        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 15b584d..619cd8e 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -55,7 +55,7 @@
                 mPowerCalculators = new ArrayList<>();
 
                 // Power calculators are applied in the order of registration
-                mPowerCalculators.add(new DischargedPowerCalculator(mPowerProfile));
+                mPowerCalculators.add(new BatteryChargeCalculator(mPowerProfile));
                 mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile));
                 mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
                 mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 0aa54f5..3f01ebb 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -6,6 +6,7 @@
 per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
 per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
 per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS
+per-file *ChargeCalculator* = file:/BATTERY_STATS_OWNERS
 per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS
 per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS
 
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 6b1d408..8b5a62a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -202,7 +202,7 @@
     public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map";
 
     /** List of allowlisted packages and its app data info: volume uuid and inode. */
-    public static final String WHITELISTED_DATA_INFO_MAP = "--whitelisted-data-info-map";
+    public static final String ALLOWLISTED_DATA_INFO_MAP = "--allowlisted-data-info-map";
 
     /** Bind mount app storage dirs to lower fs not via fuse */
     public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs";
@@ -324,7 +324,7 @@
      * @param isTopApp true if the process is for top (high priority) application.
      * @param pkgDataInfoList A list that stores related packages and its app data
      * info: volume uuid and inode.
-     * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
+     * @param allowlistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
      * @param bindMountAppDataDirs  True if the zygote needs to mount data dirs.
      * @param bindMountAppStorageDirs  True if the zygote needs to mount storage dirs.
      *
@@ -334,14 +334,14 @@
     static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
             int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
-            boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+            boolean isTopApp, String[] pkgDataInfoList, String[] allowlistedDataInfoList,
             boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
         ZygoteHooks.preFork();
 
         int pid = nativeForkAndSpecialize(
                 uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                 fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
-                pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs,
+                pkgDataInfoList, allowlistedDataInfoList, bindMountAppDataDirs,
                 bindMountAppStorageDirs);
         if (pid == 0) {
             // Note that this event ends at the end of handleChildProc,
@@ -364,7 +364,7 @@
             int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
             int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
             String appDataDir, boolean isTopApp, String[] pkgDataInfoList,
-            String[] whitelistedDataInfoList, boolean bindMountAppDataDirs,
+            String[] allowlistedDataInfoList, boolean bindMountAppDataDirs,
             boolean bindMountAppStorageDirs);
 
     /**
@@ -392,18 +392,18 @@
      * volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name,
      * app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid,
      * app_b_ce_inode, ...];
-     * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
+     * @param allowlistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
      * @param bindMountAppDataDirs  True if the zygote needs to mount data dirs.
      * @param bindMountAppStorageDirs  True if the zygote needs to mount storage dirs.
      */
     private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, int mountExternal, String seInfo, String niceName,
             boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
-            String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+            String[] pkgDataInfoList, String[] allowlistedDataInfoList,
             boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
         nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
                 niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
-                pkgDataInfoList, whitelistedDataInfoList,
+                pkgDataInfoList, allowlistedDataInfoList,
                 bindMountAppDataDirs, bindMountAppStorageDirs);
 
         // Note that this event ends at the end of handleChildProc.
@@ -428,7 +428,7 @@
     private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
             int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
             boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
-            String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+            String[] pkgDataInfoList, String[] allowlistedDataInfoList,
             boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs);
 
     /**
@@ -807,7 +807,7 @@
                                  args.mRuntimeFlags, rlimits, args.mMountExternal,
                                  args.mSeInfo, args.mNiceName, args.mStartChildZygote,
                                  args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
-                                 args.mPkgDataInfoList, args.mWhitelistedDataInfoList,
+                                 args.mPkgDataInfoList, args.mAllowlistedDataInfoList,
                                  args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);
 
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 65b454d..ef83982 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -230,7 +230,7 @@
      * A list that stores all allowlisted app data info: volume uuid and inode.
      * Null if it does need to do app data isolation.
      */
-    String[] mWhitelistedDataInfoList;
+    String[] mAllowlistedDataInfoList;
 
     /**
      * @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS
@@ -475,8 +475,8 @@
                 }
             } else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) {
                 mPkgDataInfoList = getAssignmentList(arg);
-            } else if (arg.startsWith(Zygote.WHITELISTED_DATA_INFO_MAP)) {
-                mWhitelistedDataInfoList = getAssignmentList(arg);
+            } else if (arg.startsWith(Zygote.ALLOWLISTED_DATA_INFO_MAP)) {
+                mAllowlistedDataInfoList = getAssignmentList(arg);
             } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) {
                 mBindMountAppStorageDirs = true;
             } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 37c7590..1673362 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -265,7 +265,7 @@
                             fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                             parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
                             parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
-                            parsedArgs.mWhitelistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
+                            parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
                             parsedArgs.mBindMountAppStorageDirs);
 
                     try {
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
index a5b757b..fc4cc57 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -220,7 +220,9 @@
      */
     @RemotableViewMethod
     public void setNumber(int number) {
-        mNumber = number;
-        updateNumber();
+        if (mNumber != number) {
+            mNumber = number;
+            updateNumber();
+        }
     }
 }
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 35d1d7b..f6629fd 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -43,6 +43,7 @@
 per-file EphemeralStorage* = file:platform/system/libhwbinder:/OWNERS
 
 per-file *Zygote* = file:/ZYGOTE_OWNERS
+per-file fd_utils.* = file:/ZYGOTE_OWNERS
 per-file Android.bp = file:platform/build/soong:/OWNERS
 per-file android_animation_* = file:/core/java/android/animation/OWNERS
 per-file android_app_admin_* = file:/core/java/android/app/admin/OWNERS
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 1c78750..5e142fd 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -45,6 +45,12 @@
     return value ? "true" : "false";
 }
 
+enum class HandleEventResponse : int {
+    // Allowed return values of 'handleEvent' function as documented in LooperCallback::handleEvent
+    REMOVE_CALLBACK = 0,
+    KEEP_CALLBACK = 1
+};
+
 static struct {
     jclass clazz;
 
@@ -70,6 +76,14 @@
     return str;
 }
 
+/**
+ * Convert an enumeration to its underlying type. Replace with std::to_underlying when available.
+ */
+template <class T>
+static std::underlying_type_t<T> toUnderlying(const T& t) {
+    return static_cast<std::underlying_type_t<T>>(t);
+}
+
 class NativeInputEventReceiver : public LooperCallback {
 public:
     NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak,
@@ -106,9 +120,16 @@
         return mInputConsumer.getChannel()->getName();
     }
 
-    virtual int handleEvent(int receiveFd, int events, void* data) override;
+    HandleEventResponse processOutboundEvents();
+    // From 'LooperCallback'
+    int handleEvent(int receiveFd, int events, void* data) override;
 };
 
+// Ensure HandleEventResponse underlying type matches the return type of LooperCallback::handleEvent
+static_assert(std::is_same<std::underlying_type_t<HandleEventResponse>,
+                           std::invoke_result_t<decltype(&LooperCallback::handleEvent),
+                                                NativeInputEventReceiver, int, int, void*>>::value);
+
 NativeInputEventReceiver::NativeInputEventReceiver(
         JNIEnv* env, jobject receiverWeak, const std::shared_ptr<InputChannel>& inputChannel,
         const sp<MessageQueue>& messageQueue)
@@ -179,10 +200,61 @@
     }
 }
 
+/**
+ * Receiver's primary role is to receive input events, but it has an additional duty of sending
+ * 'ack' for events (using the call 'finishInputEvent').
+ *
+ * If we are looking at the communication between InputPublisher and InputConsumer, we can say that
+ * from the InputConsumer's perspective, InputMessage's that are sent from publisher to consumer are
+ * called 'inbound / incoming' events, and the InputMessage's sent from InputConsumer to
+ * InputPublisher are 'outbound / outgoing' events.
+ *
+ * NativeInputEventReceiver owns (and acts like) an InputConsumer. So the finish events are outbound
+ * from InputEventReceiver (and will be sent to the InputPublisher).
+ *
+ * In this function, send as many events from 'mFinishQueue' as possible across the socket to the
+ * InputPublisher. If no events are remaining, let the looper know so that it doesn't wake up
+ * unnecessarily.
+ */
+HandleEventResponse NativeInputEventReceiver::processOutboundEvents() {
+    while (!mFinishQueue.empty()) {
+        const Finish& finish = *mFinishQueue.begin();
+        status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
+        if (status == OK) {
+            // Successful send. Erase the entry and keep trying to send more
+            mFinishQueue.erase(mFinishQueue.begin());
+            continue;
+        }
+
+        // Publisher is busy, try again later. Keep this entry (do not erase)
+        if (status == WOULD_BLOCK) {
+            if (kDebugDispatchCycle) {
+                ALOGD("channel '%s' ~ Remaining outbound events: %zu.",
+                      getInputChannelName().c_str(), mFinishQueue.size());
+            }
+            return HandleEventResponse::KEEP_CALLBACK; // try again later
+        }
+
+        // Some other error. Give up
+        ALOGW("Failed to send outbound event on channel '%s'.  status=%d",
+              getInputChannelName().c_str(), status);
+        if (status != DEAD_OBJECT) {
+            JNIEnv* env = AndroidRuntime::getJNIEnv();
+            std::string message =
+                    android::base::StringPrintf("Failed to send outbound event.  status=%d",
+                                                status);
+            jniThrowRuntimeException(env, message.c_str());
+            mMessageQueue->raiseAndClearException(env, "finishInputEvent");
+        }
+        return HandleEventResponse::REMOVE_CALLBACK;
+    }
+
+    // The queue is now empty. Tell looper there's no more output to expect.
+    setFdEvents(ALOOPER_EVENT_INPUT);
+    return HandleEventResponse::KEEP_CALLBACK;
+}
+
 int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
-    // Allowed return values of this function as documented in LooperCallback::handleEvent
-    constexpr int REMOVE_CALLBACK = 0;
-    constexpr int KEEP_CALLBACK = 1;
     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
         // This error typically occurs when the publisher has closed the input channel
         // as part of removing a window or finishing an IME session, in which case
@@ -191,56 +263,25 @@
             ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  "
                     "events=0x%x", getInputChannelName().c_str(), events);
         }
-        return REMOVE_CALLBACK;
+        return toUnderlying(HandleEventResponse::REMOVE_CALLBACK);
     }
 
     if (events & ALOOPER_EVENT_INPUT) {
         JNIEnv* env = AndroidRuntime::getJNIEnv();
         status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
         mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
-        return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
+        return status == OK || status == NO_MEMORY
+                ? toUnderlying(HandleEventResponse::KEEP_CALLBACK)
+                : toUnderlying(HandleEventResponse::REMOVE_CALLBACK);
     }
 
     if (events & ALOOPER_EVENT_OUTPUT) {
-        for (size_t i = 0; i < mFinishQueue.size(); i++) {
-            const Finish& finish = mFinishQueue[i];
-            status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
-            if (status != OK) {
-                mFinishQueue.erase(mFinishQueue.begin(), mFinishQueue.begin() + i);
-
-                if (status == WOULD_BLOCK) {
-                    if (kDebugDispatchCycle) {
-                        ALOGD("channel '%s' ~ Sent %zu queued finish events; %zu left.",
-                              getInputChannelName().c_str(), i, mFinishQueue.size());
-                    }
-                    return KEEP_CALLBACK; // try again later
-                }
-
-                ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
-                        getInputChannelName().c_str(), status);
-                if (status != DEAD_OBJECT) {
-                    JNIEnv* env = AndroidRuntime::getJNIEnv();
-                    std::string message =
-                            android::base::StringPrintf("Failed to finish input event.  status=%d",
-                                                        status);
-                    jniThrowRuntimeException(env, message.c_str());
-                    mMessageQueue->raiseAndClearException(env, "finishInputEvent");
-                }
-                return REMOVE_CALLBACK;
-            }
-        }
-        if (kDebugDispatchCycle) {
-            ALOGD("channel '%s' ~ Sent %zu queued finish events; none left.",
-                    getInputChannelName().c_str(), mFinishQueue.size());
-        }
-        mFinishQueue.clear();
-        setFdEvents(ALOOPER_EVENT_INPUT);
-        return KEEP_CALLBACK;
+        return toUnderlying(processOutboundEvents());
     }
 
     ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
             "events=0x%x", getInputChannelName().c_str(), events);
-    return KEEP_CALLBACK;
+    return toUnderlying(HandleEventResponse::KEEP_CALLBACK);
 }
 
 status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index bcfb06b..836074f 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1400,16 +1400,15 @@
 }
 
 static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
-    jobjectArray whitelisted_data_info_list, uid_t uid, const char* process_name,
-    jstring managed_nice_name, fail_fn_t fail_fn) {
+                           jobjectArray allowlisted_data_info_list, uid_t uid,
+                           const char* process_name, jstring managed_nice_name, fail_fn_t fail_fn) {
+    std::vector<std::string> merged_data_info_list;
+    insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list, process_name,
+                               managed_nice_name, fail_fn);
+    insertPackagesToMergedList(env, merged_data_info_list, allowlisted_data_info_list, process_name,
+                               managed_nice_name, fail_fn);
 
-  std::vector<std::string> merged_data_info_list;
-  insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list,
-          process_name, managed_nice_name, fail_fn);
-  insertPackagesToMergedList(env, merged_data_info_list, whitelisted_data_info_list,
-          process_name, managed_nice_name, fail_fn);
-
-  isolateAppData(env, merged_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+    isolateAppData(env, merged_data_info_list, uid, process_name, managed_nice_name, fail_fn);
 }
 
 /**
@@ -1510,240 +1509,242 @@
 }
 
 // Utility routine to specialize a zygote child process.
-static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
-                             jint runtime_flags, jobjectArray rlimits,
-                             jlong permitted_capabilities, jlong effective_capabilities,
-                             jint mount_external, jstring managed_se_info,
-                             jstring managed_nice_name, bool is_system_server,
-                             bool is_child_zygote, jstring managed_instruction_set,
-                             jstring managed_app_data_dir, bool is_top_app,
-                             jobjectArray pkg_data_info_list,
-                             jobjectArray whitelisted_data_info_list,
-                             bool mount_data_dirs, bool mount_storage_dirs) {
-  const char* process_name = is_system_server ? "system_server" : "zygote";
-  auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
-  auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
+static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, jint runtime_flags,
+                             jobjectArray rlimits, jlong permitted_capabilities,
+                             jlong effective_capabilities, jint mount_external,
+                             jstring managed_se_info, jstring managed_nice_name,
+                             bool is_system_server, bool is_child_zygote,
+                             jstring managed_instruction_set, jstring managed_app_data_dir,
+                             bool is_top_app, jobjectArray pkg_data_info_list,
+                             jobjectArray allowlisted_data_info_list, bool mount_data_dirs,
+                             bool mount_storage_dirs) {
+    const char* process_name = is_system_server ? "system_server" : "zygote";
+    auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
+    auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
 
-  auto se_info = extract_fn(managed_se_info);
-  auto nice_name = extract_fn(managed_nice_name);
-  auto instruction_set = extract_fn(managed_instruction_set);
-  auto app_data_dir = extract_fn(managed_app_data_dir);
+    auto se_info = extract_fn(managed_se_info);
+    auto nice_name = extract_fn(managed_nice_name);
+    auto instruction_set = extract_fn(managed_instruction_set);
+    auto app_data_dir = extract_fn(managed_app_data_dir);
 
-  // Keep capabilities across UID change, unless we're staying root.
-  if (uid != 0) {
-    EnableKeepCapabilities(fail_fn);
-  }
-
-  SetInheritable(permitted_capabilities, fail_fn);
-
-  DropCapabilitiesBoundingSet(fail_fn);
-
-  bool need_pre_initialize_native_bridge =
-      !is_system_server &&
-      instruction_set.has_value() &&
-      android::NativeBridgeAvailable() &&
-      // Native bridge may be already initialized if this
-      // is an app forked from app-zygote.
-      !android::NativeBridgeInitialized() &&
-      android::NeedsNativeBridge(instruction_set.value().c_str());
-
-  MountEmulatedStorage(uid, mount_external, need_pre_initialize_native_bridge, fail_fn);
-
-  // Make sure app is running in its own mount namespace before isolating its data directories.
-  ensureInAppMountNamespace(fail_fn);
-
-  // Sandbox data and jit profile directories by overlaying a tmpfs on those dirs and bind
-  // mount all related packages separately.
-  if (mount_data_dirs) {
-    isolateAppData(env, pkg_data_info_list, whitelisted_data_info_list,
-            uid, process_name, managed_nice_name, fail_fn);
-    isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
-  }
-  // MOUNT_EXTERNAL_INSTALLER, MOUNT_EXTERNAL_PASS_THROUGH, MOUNT_EXTERNAL_ANDROID_WRITABLE apps
-  // will have mount_storage_dirs == false here (set by ProcessList.needsStorageDataIsolation()),
-  // and hence they won't bind mount storage dirs.
-  if (mount_storage_dirs) {
-    BindMountStorageDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
-  }
-
-  // If this zygote isn't root, it won't be able to create a process group,
-  // since the directory is owned by root.
-  if (!is_system_server && getuid() == 0) {
-    const int rc = createProcessGroup(uid, getpid());
-    if (rc == -EROFS) {
-      ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
-    } else if (rc != 0) {
-      ALOGE("createProcessGroup(%d, %d) failed: %s", uid, /* pid= */ 0, strerror(-rc));
+    // Keep capabilities across UID change, unless we're staying root.
+    if (uid != 0) {
+        EnableKeepCapabilities(fail_fn);
     }
-  }
 
-  SetGids(env, gids, is_child_zygote, fail_fn);
-  SetRLimits(env, rlimits, fail_fn);
+    SetInheritable(permitted_capabilities, fail_fn);
 
-  if (need_pre_initialize_native_bridge) {
-    // Due to the logic behind need_pre_initialize_native_bridge we know that
-    // instruction_set contains a value.
-    android::PreInitializeNativeBridge(
-        app_data_dir.has_value() ? app_data_dir.value().c_str() : nullptr,
-        instruction_set.value().c_str());
-  }
+    DropCapabilitiesBoundingSet(fail_fn);
 
-  if (setresgid(gid, gid, gid) == -1) {
-    fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
-  }
+    bool need_pre_initialize_native_bridge = !is_system_server && instruction_set.has_value() &&
+            android::NativeBridgeAvailable() &&
+            // Native bridge may be already initialized if this
+            // is an app forked from app-zygote.
+            !android::NativeBridgeInitialized() &&
+            android::NeedsNativeBridge(instruction_set.value().c_str());
 
-  // Must be called when the new process still has CAP_SYS_ADMIN, in this case,
-  // before changing uid from 0, which clears capabilities.  The other
-  // alternative is to call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that
-  // breaks SELinux domain transition (see b/71859146).  As the result,
-  // privileged syscalls used below still need to be accessible in app process.
-  SetUpSeccompFilter(uid, is_child_zygote);
+    MountEmulatedStorage(uid, mount_external, need_pre_initialize_native_bridge, fail_fn);
 
-  // Must be called before losing the permission to set scheduler policy.
-  SetSchedulerPolicy(fail_fn, is_top_app);
+    // Make sure app is running in its own mount namespace before isolating its data directories.
+    ensureInAppMountNamespace(fail_fn);
 
-  if (setresuid(uid, uid, uid) == -1) {
-    fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
-  }
-
-  // The "dumpable" flag of a process, which controls core dump generation, is
-  // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
-  // user or group ID changes. See proc(5) for possible values. In most cases,
-  // the value is 0, so core dumps are disabled for zygote children. However,
-  // when running in a Chrome OS container, the value is already set to 2,
-  // which allows the external crash reporter to collect all core dumps. Since
-  // only system crashes are interested, core dump is disabled for app
-  // processes. This also ensures compliance with CTS.
-  int dumpable = prctl(PR_GET_DUMPABLE);
-  if (dumpable == -1) {
-    ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
-    RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
-  }
-
-  if (dumpable == 2 && uid >= AID_APP) {
-    if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
-      ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
-      RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
+    // Sandbox data and jit profile directories by overlaying a tmpfs on those dirs and bind
+    // mount all related packages separately.
+    if (mount_data_dirs) {
+        isolateAppData(env, pkg_data_info_list, allowlisted_data_info_list, uid, process_name,
+                       managed_nice_name, fail_fn);
+        isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
     }
-  }
-
-  // Set process properties to enable debugging if required.
-  if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) {
-    EnableDebugger();
-  }
-  if ((runtime_flags & RuntimeFlags::PROFILE_FROM_SHELL) != 0) {
-    // simpleperf needs the process to be dumpable to profile it.
-    if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
-      ALOGE("prctl(PR_SET_DUMPABLE) failed: %s", strerror(errno));
-      RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 1) failed");
+    // MOUNT_EXTERNAL_INSTALLER, MOUNT_EXTERNAL_PASS_THROUGH, MOUNT_EXTERNAL_ANDROID_WRITABLE apps
+    // will have mount_storage_dirs == false here (set by ProcessList.needsStorageDataIsolation()),
+    // and hence they won't bind mount storage dirs.
+    if (mount_storage_dirs) {
+        BindMountStorageDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name,
+                             fail_fn);
     }
-  }
 
-  HeapTaggingLevel heap_tagging_level;
-  switch (runtime_flags & RuntimeFlags::MEMORY_TAG_LEVEL_MASK) {
-    case RuntimeFlags::MEMORY_TAG_LEVEL_TBI:
-      heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
-      break;
-    case RuntimeFlags::MEMORY_TAG_LEVEL_ASYNC:
-      heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC;
-      break;
-    case RuntimeFlags::MEMORY_TAG_LEVEL_SYNC:
-      heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
-      break;
-    default:
-      heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
-      break;
-  }
-  mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level);
-
-  // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
-  runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
-
-  // Avoid heap zero initialization for applications without MTE. Zero init may
-  // cause app compat problems, use more memory, or reduce performance. While it
-  // would be nice to have them for apps, we will have to wait until they are
-  // proven out, have more efficient hardware, and/or apply them only to new
-  // applications.
-  if (!(runtime_flags & RuntimeFlags::NATIVE_HEAP_ZERO_INIT)) {
-    mallopt(M_BIONIC_ZERO_INIT, 0);
-  }
-
-  // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
-  runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT;
-
-  bool forceEnableGwpAsan = false;
-  switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
-      default:
-      case RuntimeFlags::GWP_ASAN_LEVEL_NEVER:
-          break;
-      case RuntimeFlags::GWP_ASAN_LEVEL_ALWAYS:
-          forceEnableGwpAsan = true;
-          [[fallthrough]];
-      case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY:
-          android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan));
-  }
-  // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
-  runtime_flags &= ~RuntimeFlags::GWP_ASAN_LEVEL_MASK;
-
-  if (NeedsNoRandomizeWorkaround()) {
-    // Work around ARM kernel ASLR lossage (http://b/5817320).
-    int old_personality = personality(0xffffffff);
-    int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
-    if (new_personality == -1) {
-      ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
+    // If this zygote isn't root, it won't be able to create a process group,
+    // since the directory is owned by root.
+    if (!is_system_server && getuid() == 0) {
+        const int rc = createProcessGroup(uid, getpid());
+        if (rc == -EROFS) {
+            ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
+        } else if (rc != 0) {
+            ALOGE("createProcessGroup(%d, %d) failed: %s", uid, /* pid= */ 0, strerror(-rc));
+        }
     }
-  }
 
-  SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities, fail_fn);
+    SetGids(env, gids, is_child_zygote, fail_fn);
+    SetRLimits(env, rlimits, fail_fn);
 
-  __android_log_close();
-  AStatsSocket_close();
+    if (need_pre_initialize_native_bridge) {
+        // Due to the logic behind need_pre_initialize_native_bridge we know that
+        // instruction_set contains a value.
+        android::PreInitializeNativeBridge(app_data_dir.has_value() ? app_data_dir.value().c_str()
+                                                                    : nullptr,
+                                           instruction_set.value().c_str());
+    }
 
-  const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
-  const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
+    if (setresgid(gid, gid, gid) == -1) {
+        fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
+    }
 
-  if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
-    fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed",
-                         uid, is_system_server, se_info_ptr, nice_name_ptr));
-  }
+    // Must be called when the new process still has CAP_SYS_ADMIN, in this case,
+    // before changing uid from 0, which clears capabilities.  The other
+    // alternative is to call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that
+    // breaks SELinux domain transition (see b/71859146).  As the result,
+    // privileged syscalls used below still need to be accessible in app process.
+    SetUpSeccompFilter(uid, is_child_zygote);
 
-  // Make it easier to debug audit logs by setting the main thread's name to the
-  // nice name rather than "app_process".
-  if (nice_name.has_value()) {
-    SetThreadName(nice_name.value());
-  } else if (is_system_server) {
-    SetThreadName("system_server");
-  }
+    // Must be called before losing the permission to set scheduler policy.
+    SetSchedulerPolicy(fail_fn, is_top_app);
 
-  // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
-  UnsetChldSignalHandler();
+    if (setresuid(uid, uid, uid) == -1) {
+        fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
+    }
 
-  if (is_system_server) {
-    env->CallStaticVoidMethod(gZygoteClass, gCallPostForkSystemServerHooks, runtime_flags);
+    // The "dumpable" flag of a process, which controls core dump generation, is
+    // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
+    // user or group ID changes. See proc(5) for possible values. In most cases,
+    // the value is 0, so core dumps are disabled for zygote children. However,
+    // when running in a Chrome OS container, the value is already set to 2,
+    // which allows the external crash reporter to collect all core dumps. Since
+    // only system crashes are interested, core dump is disabled for app
+    // processes. This also ensures compliance with CTS.
+    int dumpable = prctl(PR_GET_DUMPABLE);
+    if (dumpable == -1) {
+        ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
+        RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
+    }
+
+    if (dumpable == 2 && uid >= AID_APP) {
+        if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
+            ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
+            RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
+        }
+    }
+
+    // Set process properties to enable debugging if required.
+    if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) {
+        EnableDebugger();
+    }
+    if ((runtime_flags & RuntimeFlags::PROFILE_FROM_SHELL) != 0) {
+        // simpleperf needs the process to be dumpable to profile it.
+        if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
+            ALOGE("prctl(PR_SET_DUMPABLE) failed: %s", strerror(errno));
+            RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 1) failed");
+        }
+    }
+
+    HeapTaggingLevel heap_tagging_level;
+    switch (runtime_flags & RuntimeFlags::MEMORY_TAG_LEVEL_MASK) {
+        case RuntimeFlags::MEMORY_TAG_LEVEL_TBI:
+            heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
+            break;
+        case RuntimeFlags::MEMORY_TAG_LEVEL_ASYNC:
+            heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC;
+            break;
+        case RuntimeFlags::MEMORY_TAG_LEVEL_SYNC:
+            heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
+            break;
+        default:
+            heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
+            break;
+    }
+    mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level);
+
+    // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART
+    // runtime.
+    runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
+
+    // Avoid heap zero initialization for applications without MTE. Zero init may
+    // cause app compat problems, use more memory, or reduce performance. While it
+    // would be nice to have them for apps, we will have to wait until they are
+    // proven out, have more efficient hardware, and/or apply them only to new
+    // applications.
+    if (!(runtime_flags & RuntimeFlags::NATIVE_HEAP_ZERO_INIT)) {
+        mallopt(M_BIONIC_ZERO_INIT, 0);
+    }
+
+    // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART
+    // runtime.
+    runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT;
+
+    bool forceEnableGwpAsan = false;
+    switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
+        default:
+        case RuntimeFlags::GWP_ASAN_LEVEL_NEVER:
+            break;
+        case RuntimeFlags::GWP_ASAN_LEVEL_ALWAYS:
+            forceEnableGwpAsan = true;
+            [[fallthrough]];
+        case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY:
+            android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan));
+    }
+    // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART
+    // runtime.
+    runtime_flags &= ~RuntimeFlags::GWP_ASAN_LEVEL_MASK;
+
+    if (NeedsNoRandomizeWorkaround()) {
+        // Work around ARM kernel ASLR lossage (http://b/5817320).
+        int old_personality = personality(0xffffffff);
+        int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+        if (new_personality == -1) {
+            ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
+        }
+    }
+
+    SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities,
+                    fail_fn);
+
+    __android_log_close();
+    AStatsSocket_close();
+
+    const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
+    const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
+
+    if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
+        fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
+                             is_system_server, se_info_ptr, nice_name_ptr));
+    }
+
+    // Make it easier to debug audit logs by setting the main thread's name to the
+    // nice name rather than "app_process".
+    if (nice_name.has_value()) {
+        SetThreadName(nice_name.value());
+    } else if (is_system_server) {
+        SetThreadName("system_server");
+    }
+
+    // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
+    UnsetChldSignalHandler();
+
+    if (is_system_server) {
+        env->CallStaticVoidMethod(gZygoteClass, gCallPostForkSystemServerHooks, runtime_flags);
+        if (env->ExceptionCheck()) {
+            fail_fn("Error calling post fork system server hooks.");
+        }
+
+        // TODO(b/117874058): Remove hardcoded label here.
+        static const char* kSystemServerLabel = "u:r:system_server:s0";
+        if (selinux_android_setcon(kSystemServerLabel) != 0) {
+            fail_fn(CREATE_ERROR("selinux_android_setcon(%s)", kSystemServerLabel));
+        }
+    }
+
+    if (is_child_zygote) {
+        initUnsolSocketToSystemServer();
+    }
+
+    env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
+                              is_system_server, is_child_zygote, managed_instruction_set);
+
+    // Reset the process priority to the default value.
+    setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);
+
     if (env->ExceptionCheck()) {
-      fail_fn("Error calling post fork system server hooks.");
+        fail_fn("Error calling post fork hooks.");
     }
-
-    // TODO(oth): Remove hardcoded label here (b/117874058).
-    static const char* kSystemServerLabel = "u:r:system_server:s0";
-    if (selinux_android_setcon(kSystemServerLabel) != 0) {
-      fail_fn(CREATE_ERROR("selinux_android_setcon(%s)", kSystemServerLabel));
-    }
-  }
-
-  if (is_child_zygote) {
-      initUnsolSocketToSystemServer();
-  }
-
-  env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
-                            is_system_server, is_child_zygote, managed_instruction_set);
-
-  // Reset the process priority to the default value.
-  setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);
-
-  if (env->ExceptionCheck()) {
-    fail_fn("Error calling post fork hooks.");
-  }
 }
 
 static uint64_t GetEffectiveCapabilityMask(JNIEnv* env) {
@@ -2068,12 +2069,11 @@
 
 NO_PAC_FUNC
 static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
-        JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
-        jint runtime_flags, jobjectArray rlimits,
-        jint mount_external, jstring se_info, jstring nice_name,
+        JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, jint runtime_flags,
+        jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
         jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
         jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
-        jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
+        jobjectArray pkg_data_info_list, jobjectArray allowlisted_data_info_list,
         jboolean mount_data_dirs, jboolean mount_storage_dirs) {
     jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
@@ -2108,14 +2108,11 @@
     pid_t pid = zygote::ForkCommon(env, false, fds_to_close, fds_to_ignore, true);
 
     if (pid == 0) {
-      SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
-                       capabilities, capabilities,
-                       mount_external, se_info, nice_name, false,
-                       is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
-                       is_top_app == JNI_TRUE, pkg_data_info_list,
-                       whitelisted_data_info_list,
-                       mount_data_dirs == JNI_TRUE,
-                       mount_storage_dirs == JNI_TRUE);
+        SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
+                         mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
+                         instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
+                         allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
+                         mount_storage_dirs == JNI_TRUE);
     }
     return pid;
 }
@@ -2147,12 +2144,11 @@
   if (pid == 0) {
       // System server prcoess does not need data isolation so no need to
       // know pkg_data_info_list.
-      SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
-                       permitted_capabilities, effective_capabilities,
-                       MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
+      SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities,
+                       effective_capabilities, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
                        false, nullptr, nullptr, /* is_top_app= */ false,
                        /* pkg_data_info_list */ nullptr,
-                       /* whitelisted_data_info_list */ nullptr, false, false);
+                       /* allowlisted_data_info_list */ nullptr, false, false);
   } else if (pid > 0) {
       // The zygote process checks whether the child process has died or not.
       ALOGI("System server process %d has been created", pid);
@@ -2260,7 +2256,7 @@
     if (!path_cstr) {
         RuntimeAbort(env, __LINE__, "path_cstr == nullptr");
     }
-    FileDescriptorWhitelist::Get()->Allow(path_cstr);
+    FileDescriptorAllowlist::Get()->Allow(path_cstr);
 }
 
 static void com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter(
@@ -2295,20 +2291,19 @@
  * @param is_top_app  If the process is for top (high priority) application
  */
 static void com_android_internal_os_Zygote_nativeSpecializeAppProcess(
-    JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
-    jint runtime_flags, jobjectArray rlimits,
-    jint mount_external, jstring se_info, jstring nice_name,
-    jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
-    jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
-    jboolean mount_data_dirs, jboolean mount_storage_dirs) {
-  jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
+        JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, jint runtime_flags,
+        jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
+        jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir,
+        jboolean is_top_app, jobjectArray pkg_data_info_list,
+        jobjectArray allowlisted_data_info_list, jboolean mount_data_dirs,
+        jboolean mount_storage_dirs) {
+    jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
-  SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
-                   capabilities, capabilities,
-                   mount_external, se_info, nice_name, false,
-                   is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
-                   is_top_app == JNI_TRUE, pkg_data_info_list, whitelisted_data_info_list,
-                   mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE);
+    SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
+                     mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
+                     instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
+                     allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
+                     mount_storage_dirs == JNI_TRUE);
 }
 
 /**
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 06a71cb..7fa627b 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -31,8 +31,8 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-// Static whitelist of open paths that the zygote is allowed to keep open.
-static const char* kPathWhitelist[] = {
+// Static allowlist of open paths that the zygote is allowed to keep open.
+static const char* kPathAllowlist[] = {
         "/dev/null",
         "/dev/socket/zygote",
         "/dev/socket/zygote_secondary",
@@ -53,118 +53,114 @@
 static const char kFdPath[] = "/proc/self/fd";
 
 // static
-FileDescriptorWhitelist* FileDescriptorWhitelist::Get() {
-  if (instance_ == nullptr) {
-    instance_ = new FileDescriptorWhitelist();
-  }
-  return instance_;
+FileDescriptorAllowlist* FileDescriptorAllowlist::Get() {
+    if (instance_ == nullptr) {
+        instance_ = new FileDescriptorAllowlist();
+    }
+    return instance_;
 }
 
 static bool IsArtMemfd(const std::string& path) {
   return android::base::StartsWith(path, "/memfd:/boot-image-methods.art");
 }
 
-bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
-  // Check the static whitelist path.
-  for (const auto& whitelist_path : kPathWhitelist) {
-    if (path == whitelist_path)
-      return true;
-  }
-
-  // Check any paths added to the dynamic whitelist.
-  for (const auto& whitelist_path : whitelist_) {
-    if (path == whitelist_path)
-      return true;
-  }
-
-  // Framework jars are allowed.
-  static const char* kFrameworksPrefix[] = {
-          "/system/framework/",
-          "/system_ext/framework/",
-  };
-
-  static const char* kJarSuffix = ".jar";
-
-  for (const auto& frameworks_prefix : kFrameworksPrefix) {
-    if (android::base::StartsWith(path, frameworks_prefix)
-        && android::base::EndsWith(path, kJarSuffix)) {
-      return true;
+bool FileDescriptorAllowlist::IsAllowed(const std::string& path) const {
+    // Check the static allowlist path.
+    for (const auto& allowlist_path : kPathAllowlist) {
+        if (path == allowlist_path) return true;
     }
-  }
 
-  // Jars from APEXes are allowed. This matches /apex/**/javalib/*.jar.
-  static const char* kApexPrefix = "/apex/";
-  static const char* kApexJavalibPathSuffix = "/javalib";
-  if (android::base::StartsWith(path, kApexPrefix) && android::base::EndsWith(path, kJarSuffix) &&
-      android::base::EndsWith(android::base::Dirname(path), kApexJavalibPathSuffix)) {
-      return true;
-  }
+    // Check any paths added to the dynamic allowlist.
+    for (const auto& allowlist_path : allowlist_) {
+        if (path == allowlist_path) return true;
+    }
 
-  // the in-memory file created by ART through memfd_create is allowed.
-  if (IsArtMemfd(path)) {
-    return true;
-  }
+    // Framework jars are allowed.
+    static const char* kFrameworksPrefix[] = {
+            "/system/framework/",
+            "/system_ext/framework/",
+    };
 
-  // Whitelist files needed for Runtime Resource Overlay, like these:
-  // /system/vendor/overlay/framework-res.apk
-  // /system/vendor/overlay-subdir/pg/framework-res.apk
-  // /vendor/overlay/framework-res.apk
-  // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
-  // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
-  // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
-  // See AssetManager.cpp for more details on overlay-subdir.
-  static const char* kOverlayDir = "/system/vendor/overlay/";
-  static const char* kVendorOverlayDir = "/vendor/overlay";
-  static const char* kVendorOverlaySubdir = "/system/vendor/overlay-subdir/";
-  static const char* kSystemProductOverlayDir = "/system/product/overlay/";
-  static const char* kProductOverlayDir = "/product/overlay";
-  static const char* kSystemSystemExtOverlayDir = "/system/system_ext/overlay/";
-  static const char* kSystemExtOverlayDir = "/system_ext/overlay";
-  static const char* kSystemOdmOverlayDir = "/system/odm/overlay";
-  static const char* kOdmOverlayDir = "/odm/overlay";
-  static const char* kSystemOemOverlayDir = "/system/oem/overlay";
-  static const char* kOemOverlayDir = "/oem/overlay";
-  static const char* kApkSuffix = ".apk";
+    static const char* kJarSuffix = ".jar";
 
-  if ((android::base::StartsWith(path, kOverlayDir)
-       || android::base::StartsWith(path, kVendorOverlaySubdir)
-       || android::base::StartsWith(path, kVendorOverlayDir)
-       || android::base::StartsWith(path, kSystemProductOverlayDir)
-       || android::base::StartsWith(path, kProductOverlayDir)
-       || android::base::StartsWith(path, kSystemSystemExtOverlayDir)
-       || android::base::StartsWith(path, kSystemExtOverlayDir)
-       || android::base::StartsWith(path, kSystemOdmOverlayDir)
-       || android::base::StartsWith(path, kOdmOverlayDir)
-       || android::base::StartsWith(path, kSystemOemOverlayDir)
-       || android::base::StartsWith(path, kOemOverlayDir))
-      && android::base::EndsWith(path, kApkSuffix)
-      && path.find("/../") == std::string::npos) {
-    return true;
-  }
+    for (const auto& frameworks_prefix : kFrameworksPrefix) {
+        if (android::base::StartsWith(path, frameworks_prefix) &&
+            android::base::EndsWith(path, kJarSuffix)) {
+            return true;
+        }
+    }
 
-  static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
-  static const char* kOverlayIdmapSuffix = ".apk@idmap";
-  if (android::base::StartsWith(path, kOverlayIdmapPrefix)
-      && android::base::EndsWith(path, kOverlayIdmapSuffix)
-      && path.find("/../") == std::string::npos) {
-    return true;
-  }
+    // Jars from APEXes are allowed. This matches /apex/**/javalib/*.jar.
+    static const char* kApexPrefix = "/apex/";
+    static const char* kApexJavalibPathSuffix = "/javalib";
+    if (android::base::StartsWith(path, kApexPrefix) && android::base::EndsWith(path, kJarSuffix) &&
+        android::base::EndsWith(android::base::Dirname(path), kApexJavalibPathSuffix)) {
+        return true;
+    }
 
-  // All regular files that are placed under this path are whitelisted automatically.
-  static const char* kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
-  if (android::base::StartsWith(path, kZygoteWhitelistPath)
-      && path.find("/../") == std::string::npos) {
-    return true;
-  }
+    // the in-memory file created by ART through memfd_create is allowed.
+    if (IsArtMemfd(path)) {
+        return true;
+    }
 
-  return false;
+    // Allowlist files needed for Runtime Resource Overlay, like these:
+    // /system/vendor/overlay/framework-res.apk
+    // /system/vendor/overlay-subdir/pg/framework-res.apk
+    // /vendor/overlay/framework-res.apk
+    // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
+    // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
+    // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
+    // See AssetManager.cpp for more details on overlay-subdir.
+    static const char* kOverlayDir = "/system/vendor/overlay/";
+    static const char* kVendorOverlayDir = "/vendor/overlay";
+    static const char* kVendorOverlaySubdir = "/system/vendor/overlay-subdir/";
+    static const char* kSystemProductOverlayDir = "/system/product/overlay/";
+    static const char* kProductOverlayDir = "/product/overlay";
+    static const char* kSystemSystemExtOverlayDir = "/system/system_ext/overlay/";
+    static const char* kSystemExtOverlayDir = "/system_ext/overlay";
+    static const char* kSystemOdmOverlayDir = "/system/odm/overlay";
+    static const char* kOdmOverlayDir = "/odm/overlay";
+    static const char* kSystemOemOverlayDir = "/system/oem/overlay";
+    static const char* kOemOverlayDir = "/oem/overlay";
+    static const char* kApkSuffix = ".apk";
+
+    if ((android::base::StartsWith(path, kOverlayDir) ||
+         android::base::StartsWith(path, kVendorOverlaySubdir) ||
+         android::base::StartsWith(path, kVendorOverlayDir) ||
+         android::base::StartsWith(path, kSystemProductOverlayDir) ||
+         android::base::StartsWith(path, kProductOverlayDir) ||
+         android::base::StartsWith(path, kSystemSystemExtOverlayDir) ||
+         android::base::StartsWith(path, kSystemExtOverlayDir) ||
+         android::base::StartsWith(path, kSystemOdmOverlayDir) ||
+         android::base::StartsWith(path, kOdmOverlayDir) ||
+         android::base::StartsWith(path, kSystemOemOverlayDir) ||
+         android::base::StartsWith(path, kOemOverlayDir)) &&
+        android::base::EndsWith(path, kApkSuffix) && path.find("/../") == std::string::npos) {
+        return true;
+    }
+
+    static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
+    static const char* kOverlayIdmapSuffix = ".apk@idmap";
+    if (android::base::StartsWith(path, kOverlayIdmapPrefix) &&
+        android::base::EndsWith(path, kOverlayIdmapSuffix) &&
+        path.find("/../") == std::string::npos) {
+        return true;
+    }
+
+    // All regular files that are placed under this path are allowlisted
+    // automatically.  The directory name is maintained for compatibility.
+    static const char* kZygoteAllowlistPath = "/vendor/zygote_whitelist/";
+    if (android::base::StartsWith(path, kZygoteAllowlistPath) &&
+        path.find("/../") == std::string::npos) {
+        return true;
+    }
+
+    return false;
 }
 
-FileDescriptorWhitelist::FileDescriptorWhitelist()
-    : whitelist_() {
-}
+FileDescriptorAllowlist::FileDescriptorAllowlist() : allowlist_() {}
 
-FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
+FileDescriptorAllowlist* FileDescriptorAllowlist::instance_ = nullptr;
 
 // Keeps track of all relevant information (flags, offset etc.) of an
 // open zygote file descriptor.
@@ -217,7 +213,7 @@
     fail_fn(android::base::StringPrintf("Unable to stat %d", fd));
   }
 
-  const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
+  const FileDescriptorAllowlist* allowlist = FileDescriptorAllowlist::Get();
 
   if (S_ISSOCK(f_stat.st_mode)) {
     std::string socket_name;
@@ -225,16 +221,15 @@
       fail_fn("Unable to get socket name");
     }
 
-    if (!whitelist->IsAllowed(socket_name)) {
-      fail_fn(android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)",
-                                          socket_name.c_str(),
-                                          fd));
+    if (!allowlist->IsAllowed(socket_name)) {
+        fail_fn(android::base::StringPrintf("Socket name not allowlisted : %s (fd=%d)",
+                                            socket_name.c_str(), fd));
     }
 
     return new FileDescriptorInfo(fd);
   }
 
-  // We only handle whitelisted regular files and character devices. Whitelisted
+  // We only handle allowlisted regular files and character devices. Allowlisted
   // character devices must provide a guarantee of sensible behaviour when
   // reopened.
   //
@@ -268,8 +263,8 @@
                                         strerror(errno)));
   }
 
-  if (!whitelist->IsAllowed(file_path)) {
-    fail_fn(android::base::StringPrintf("Not whitelisted (%d): %s", fd, file_path.c_str()));
+  if (!allowlist->IsAllowed(file_path)) {
+      fail_fn(android::base::StringPrintf("Not allowlisted (%d): %s", fd, file_path.c_str()));
   }
 
   // File descriptor flags : currently on FD_CLOEXEC. We can set these
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index 2caf157..14c318e 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -33,42 +33,40 @@
 // This type is duplicated in com_android_internal_os_Zygote.cpp
 typedef const std::function<void(std::string)>& fail_fn_t;
 
-// Whitelist of open paths that the zygote is allowed to keep open.
+// Allowlist of open paths that the zygote is allowed to keep open.
 //
-// In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
+// In addition to the paths listed in kPathAllowlist in file_utils.cpp, and
 // paths dynamically added with Allow(), all files ending with ".jar"
-// under /system/framework" are whitelisted. See IsAllowed() for the canonical
+// under /system/framework" are allowlisted. See IsAllowed() for the canonical
 // definition.
 //
-// If the whitelisted path is associated with a regular file or a
+// If the allowlisted path is associated with a regular file or a
 // character device, the file is reopened after a fork with the same
-// offset and mode. If the whilelisted  path is associated with a
+// offset and mode. If the allowlisted path is associated with a
 // AF_UNIX socket, the socket will refer to /dev/null after each
 // fork, and all operations on it will fail.
-class FileDescriptorWhitelist {
- public:
-  // Lazily creates the global whitelist.
-  static FileDescriptorWhitelist* Get();
+class FileDescriptorAllowlist {
+public:
+    // Lazily creates the global allowlist.
+    static FileDescriptorAllowlist* Get();
 
-  // Adds a path to the whitelist.
-  void Allow(const std::string& path) {
-    whitelist_.push_back(path);
-  }
+    // Adds a path to the allowlist.
+    void Allow(const std::string& path) { allowlist_.push_back(path); }
 
-  // Returns true iff. a given path is whitelisted. A path is whitelisted
-  // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
-  // under /system/framework that ends with ".jar" or if it is a system
-  // framework overlay.
-  bool IsAllowed(const std::string& path) const;
+    // Returns true iff. a given path is allowlisted. A path is allowlisted
+    // if it belongs to the allowlist (see kPathAllowlist) or if it's a path
+    // under /system/framework that ends with ".jar" or if it is a system
+    // framework overlay.
+    bool IsAllowed(const std::string& path) const;
 
- private:
-  FileDescriptorWhitelist();
+private:
+    FileDescriptorAllowlist();
 
-  static FileDescriptorWhitelist* instance_;
+    static FileDescriptorAllowlist* instance_;
 
-  std::vector<std::string> whitelist_;
+    std::vector<std::string> allowlist_;
 
-  DISALLOW_COPY_AND_ASSIGN(FileDescriptorWhitelist);
+    DISALLOW_COPY_AND_ASSIGN(FileDescriptorAllowlist);
 };
 
 // A FileDescriptorTable is a collection of FileDescriptorInfo objects
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ec41a47..74a37ca 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -628,6 +628,7 @@
         optional string tag = 3;
         optional int32 type = 4;
         optional int32 reason_code = 5;
+        optional int32 calling_uid = 6;
     }
     repeated PendingTempWhitelist pending_temp_whitelist = 26;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 50d1e6b..072bb87 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1876,15 +1876,15 @@
     <permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @SystemApi @hide Allows system APK to update Wifi/Cellular coex channels to avoid.
+    <!-- @SystemApi @hide Allows applications to update Wifi/Cellular coex channels to avoid.
              <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- @SystemApi @hide Allows applications to access Wifi/Cellular coex channels being avoided.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS"
-                android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|role" />
 
     <!-- @SystemApi @hide Allows system APK to manage country code.
              <p>Not for use by third-party applications. -->
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 150734e..81a79c5 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -30,7 +30,8 @@
         android:id="@+id/left_icon"
         android:layout_width="@dimen/notification_left_icon_size"
         android:layout_height="@dimen/notification_left_icon_size"
-        android:layout_gravity="center_vertical|start"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
         android:layout_marginStart="@dimen/notification_left_icon_start"
         android:background="@drawable/notification_large_icon_outline"
         android:clipToOutline="true"
@@ -43,7 +44,8 @@
         android:id="@+id/icon"
         android:layout_width="@dimen/notification_icon_circle_size"
         android:layout_height="@dimen/notification_icon_circle_size"
-        android:layout_gravity="center_vertical|start"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
         android:layout_marginStart="@dimen/notification_icon_circle_start"
         android:background="@drawable/notification_icon_circle"
         android:padding="@dimen/notification_icon_circle_padding"
@@ -55,10 +57,12 @@
         android:id="@+id/notification_top_line"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:layout_gravity="center_vertical"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        android:layout_toStartOf="@id/expand_button"
+        android:layout_alignWithParentIfMissing="true"
         android:clipChildren="false"
         android:gravity="center_vertical"
-        android:paddingEnd="@dimen/notification_heading_margin_end"
         android:paddingStart="@dimen/notification_content_margin_start"
         android:theme="@style/Theme.DeviceDefault.Notification"
         >
@@ -71,14 +75,15 @@
         android:id="@+id/alternate_expand_target"
         android:layout_width="@dimen/notification_content_margin_start"
         android:layout_height="match_parent"
-        android:layout_gravity="start"
+        android:layout_alignParentStart="true"
         android:importantForAccessibility="no"
         />
 
     <include layout="@layout/notification_expand_button"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical|end"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
         />
 
 </NotificationHeaderView>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 100983b..efc8fe9 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8424,6 +8424,9 @@
         <!-- Fully qualified class name of an activity that allows the user to modify
              the settings for this service. -->
         <attr name="settingsActivity" />
+        <!-- Fully qualified class name of an activity that allows the user to view any passwords
+             saved by this service. -->
+        <attr name="passwordsActivity" format="string" />
 
         <!-- Specifies whether the AutofillService supports inline suggestions-->
         <attr name="supportsInlineSuggestions" format="boolean" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 45e11ba..bed5c31 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1773,6 +1773,8 @@
             <enum name="maps" value="6" />
             <!-- Apps which are primarily productivity apps, such as cloud storage or workplace apps. -->
             <enum name="productivity" value="7" />
+            <!-- Apps which are primarily accessibility apps, such as screen-readers. -->
+            <enum name="accessibility" value="8" />
         </attr>
 
         <!-- Declares the kind of classloader this application's classes must be loaded with -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a0cb3df..f38264c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1964,6 +1964,8 @@
     <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 hold the system Wi-Fi coex manager role. -->
+    <string name="config_systemWifiCoexManager" translateable="false"></string>
 
     <!-- The name of the package that will be allowed to change its components' label/icon. -->
     <string name="config_overrideComponentUiPackage" translatable="false"></string>
@@ -4666,11 +4668,11 @@
     <!-- WindowsManager JetPack display features -->
     <string name="config_display_features" translatable="false" />
 
-    <!-- Aspect ratio of task level letterboxing. Values <= 1.0 will be ignored.
-         Note: Activity min/max aspect ratio restrictions will still be respected by the
-         activity-level letterboxing (size-compat mode). Therefore this override can control the
-         maximum screen area that can be occupied by the app in the letterbox mode. -->
-    <item name="config_taskLetterboxAspectRatio" format="float" type="dimen">0.0</item>
+    <!-- Aspect ratio of letterboxing for fixed orientation. Values <= 1.0 will be ignored.
+         Note: Activity min/max aspect ratio restrictions will still be respected.
+         Therefore this override can control the maximum screen area that can be occupied by
+         the app in the letterbox mode. -->
+    <item name="config_fixedOrientationLetterboxAspectRatio" format="float" type="dimen">0.0</item>
 
     <!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and
          corners of the activity won't be rounded. -->
@@ -4696,6 +4698,14 @@
     <!-- If true, hide the display cutout with display area -->
     <bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
 
+    <!-- The timeout value in milliseconds used by SelectionActionModeHelper for each selections
+     when TextClassifier has been initialized. -->
+    <integer name="config_smartSelectionInitializedTimeoutMillis">200</integer>
+
+    <!-- The timeout value in milliseconds used by SelectionActionModeHelper for each selections
+         when TextClassifier has not been initialized. -->
+    <integer name="config_smartSelectionInitializingTimeoutMillis">500</integer>
+
     <!-- Indicates that default fitness tracker app needs to request sensor and location permissions. -->
     <bool name="config_trackerAppNeedsPermissions">false</bool>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index f390462..10aa7b3 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -224,7 +224,7 @@
     <dimen name="notification_content_margin_end">16dp</dimen>
 
     <!-- The margin on the end of the top-line content views (accommodates the expander) -->
-    <dimen name="notification_heading_margin_end">48dp</dimen>
+    <dimen name="notification_heading_margin_end">56dp</dimen>
 
     <!-- The margin for text at the end of the image view for media notifications -->
     <dimen name="notification_media_image_margin_end">72dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2004d0a..7702ee4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3085,6 +3085,7 @@
     <public name="hand_secondTint"/>
     <public name="hand_secondTintMode"/>
     <public name="dataExtractionRules"/>
+    <public name="passwordsActivity"/>
   </public-group>
 
   <public-group type="drawable" first-id="0x010800b5">
@@ -3168,6 +3169,8 @@
     <public name="config_customMediaSessionPolicyProvider" />
     <!-- @hide @SystemApi -->
     <public name="config_systemSpeechRecognizer" />
+    <!-- @hide @SystemApi -->
+    <public name="config_systemWifiCoexManager" />
   </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 71ba44b..2b1168f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1524,8 +1524,15 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_mediaLocation">Allows the app to read locations from your media collection.</string>
 
+    <!-- Name for an app setting that lets the user authenticate for that app using biometrics (e.g. fingerprint or face). [CHAR LIMIT=30] -->
+    <string name="biometric_app_setting_name">Use biometrics</string>
+    <!-- Name for an app setting that lets the user authenticate for that app using biometrics (e.g. fingerprint or face) or their screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=70] -->
+    <string name="biometric_or_screen_lock_app_setting_name">Use biometrics or screen lock</string>
     <!-- Title shown when the system-provided biometric dialog is shown, asking the user to authenticate. [CHAR LIMIT=40] -->
     <string name="biometric_dialog_default_title">Verify it\u2019s you</string>
+    <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with a biometric (e.g. fingerprint or face). [CHAR LIMIT=70] -->
+    <string name="biometric_dialog_default_subtitle">Use your biometric to continue</string>
+
     <!-- Message shown when biometric hardware is not available [CHAR LIMIT=50] -->
     <string name="biometric_error_hw_unavailable">Biometric hardware unavailable</string>
     <!-- Message shown when biometric authentication was canceled by the user [CHAR LIMIT=50] -->
@@ -1539,6 +1546,11 @@
     <!-- Message returned to applications when an unexpected/unknown error occurs. [CHAR LIMIT=50]-->
     <string name="biometric_error_generic">Error authenticating</string>
 
+    <!-- Name for an app setting that lets the user authenticate for that app with their screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=30] -->
+    <string name="screen_lock_app_setting_name">Use screen lock</string>
+    <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=70] -->
+    <string name="screen_lock_dialog_default_subtitle">Enter your device credential to continue</string>
+
     <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
     <string name="fingerprint_acquired_partial">Partial fingerprint detected. Please try again.</string>
     <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
@@ -1585,6 +1597,11 @@
 
     <!-- Template to be used to name enrolled fingerprints by default. -->
     <string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
+
+    <!-- Name for an app setting that lets the user authenticate for that app with their fingerprint. [CHAR LIMIT=30] -->
+    <string name="fingerprint_app_setting_name">Use fingerprint</string>
+    <!-- Name for an app setting that lets the user authenticate for that app with their fingerprint or screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=70] -->
+    <string name="fingerprint_or_screen_lock_app_setting_name">Use fingerprint or screen lock</string>
     <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their fingerprint. [CHAR LIMIT=70] -->
     <string name="fingerprint_dialog_default_subtitle">Use your fingerprint to continue</string>
 
@@ -1681,6 +1698,13 @@
     <!-- Template to be used to name enrolled faces by default. [CHAR LIMIT=10] -->
     <string name="face_name_template">Face <xliff:g id="faceId" example="1">%d</xliff:g></string>
 
+    <!-- Name for an app setting that lets the user authenticate for that app with their face. [CHAR LIMIT=30] -->
+    <string name="face_app_setting_name">Use face unlock</string>
+    <!-- Name for an app setting that lets the user authenticate for that app with their face or screen lock credential (i.e. PIN, pattern, or password). [CHAR LIMIT=70] -->
+    <string name="face_or_screen_lock_app_setting_name">Use face or screen lock</string>
+    <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their face. [CHAR LIMIT=70] -->
+    <string name="face_dialog_default_subtitle">Use face unlock to continue</string>
+
     <!-- Array containing custom error messages from vendor.  Vendor is expected to add and translate these strings -->
     <string-array name="face_error_vendor">
     </string-array>
@@ -5193,6 +5217,8 @@
     <string name="app_category_maps">Maps &amp; Navigation</string>
     <!-- Category title for apps which are primarily productivity apps, such as cloud storage or workplace apps. [CHAR LIMIT=32] -->
     <string name="app_category_productivity">Productivity</string>
+    <!-- Category title for apps which are primarily accessibility apps, such as screen-readers. [CHAR LIMIT=32] -->
+    <string name="app_category_accessibility">Accessibility</string>
 
     <!-- Channel name for DeviceStorageMonitor notifications -->
     <string name="device_storage_monitor_notification_channel">Device storage</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e380f40..2fff7b5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -482,6 +482,8 @@
   <java-symbol type="array" name="config_integrityRuleProviderPackages" />
   <java-symbol type="bool" name="config_useAssistantVolume" />
   <java-symbol type="string" name="config_bandwidthEstimateSource" />
+  <java-symbol type="integer" name="config_smartSelectionInitializedTimeoutMillis" />
+  <java-symbol type="integer" name="config_smartSelectionInitializingTimeoutMillis" />
 
   <java-symbol type="color" name="tab_indicator_text_v4" />
 
@@ -2470,7 +2472,10 @@
   <java-symbol type="string" name="config_keyguardComponent" />
 
   <!-- Biometric messages -->
+  <java-symbol type="string" name="biometric_app_setting_name" />
+  <java-symbol type="string" name="biometric_or_screen_lock_app_setting_name" />
   <java-symbol type="string" name="biometric_dialog_default_title" />
+  <java-symbol type="string" name="biometric_dialog_default_subtitle" />
   <java-symbol type="string" name="biometric_error_hw_unavailable" />
   <java-symbol type="string" name="biometric_error_user_canceled" />
   <java-symbol type="string" name="biometric_not_recognized" />
@@ -2478,6 +2483,10 @@
   <java-symbol type="string" name="biometric_error_device_not_secured" />
   <java-symbol type="string" name="biometric_error_generic" />
 
+  <!-- Device credential strings for BiometricManager -->
+  <java-symbol type="string" name="screen_lock_app_setting_name" />
+  <java-symbol type="string" name="screen_lock_dialog_default_subtitle" />
+
   <!-- Fingerprint messages -->
   <java-symbol type="string" name="fingerprint_error_unable_to_process" />
   <java-symbol type="string" name="fingerprint_error_hw_not_available" />
@@ -2495,6 +2504,8 @@
   <java-symbol type="string" name="fingerprint_error_lockout" />
   <java-symbol type="string" name="fingerprint_error_lockout_permanent" />
   <java-symbol type="string" name="fingerprint_name_template" />
+  <java-symbol type="string" name="fingerprint_app_setting_name" />
+  <java-symbol type="string" name="fingerprint_or_screen_lock_app_setting_name" />
   <java-symbol type="string" name="fingerprint_dialog_default_subtitle" />
   <java-symbol type="string" name="fingerprint_authenticated" />
   <java-symbol type="string" name="fingerprint_error_no_fingerprints" />
@@ -2542,6 +2553,9 @@
   <java-symbol type="string" name="face_acquired_sensor_dirty" />
   <java-symbol type="array" name="face_acquired_vendor" />
   <java-symbol type="string" name="face_name_template" />
+  <java-symbol type="string" name="face_app_setting_name" />
+  <java-symbol type="string" name="face_or_screen_lock_app_setting_name" />
+  <java-symbol type="string" name="face_dialog_default_subtitle" />
   <java-symbol type="string" name="face_authenticated_no_confirmation_required" />
   <java-symbol type="string" name="face_authenticated_confirmation_required" />
   <java-symbol type="string" name="face_error_security_update_required" />
@@ -3300,6 +3314,7 @@
   <java-symbol type="string" name="app_category_news" />
   <java-symbol type="string" name="app_category_maps" />
   <java-symbol type="string" name="app_category_productivity" />
+  <java-symbol type="string" name="app_category_accessibility" />
 
   <java-symbol type="raw" name="fallback_categories" />
 
@@ -4166,7 +4181,7 @@
   <java-symbol type="dimen" name="controls_thumbnail_image_max_height" />
   <java-symbol type="dimen" name="controls_thumbnail_image_max_width" />
 
-  <java-symbol type="dimen" name="config_taskLetterboxAspectRatio" />
+  <java-symbol type="dimen" name="config_fixedOrientationLetterboxAspectRatio" />
   <java-symbol type="integer" name="config_letterboxActivityCornersRadius" />
   <java-symbol type="integer" name="config_letterboxBackgroundType" />
   <java-symbol type="color" name="config_letterboxBackgroundColor" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
index 3706e4b..b0c1f25 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
@@ -24,6 +24,7 @@
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.State;
+import android.net.TetheringManager;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
@@ -141,7 +142,7 @@
         mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
         mIntentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
-        mIntentFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+        mIntentFilter.addAction(TetheringManager.ACTION_TETHER_STATE_CHANGED);
         mContext.registerReceiver(mWifiReceiver, mIntentFilter);
 
         logv("Clear Wifi before we start the test.");
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
index 0fe44630..9e88373 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
@@ -21,6 +21,8 @@
 
 import android.os.Bundle;
 
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Test;
 
 import java.util.List;
@@ -67,9 +69,9 @@
         SearchSpec searchSpec =
                 new SearchSpec.Builder()
                         .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
-                        .addProjection("TypeA", "field1", "field2.subfield2")
-                        .addProjection("TypeB", "field7")
-                        .addProjection("TypeC")
+                        .addProjection("TypeA", ImmutableList.of("field1", "field2.subfield2"))
+                        .addProjection("TypeB", ImmutableList.of("field7"))
+                        .addProjection("TypeC", ImmutableList.of())
                         .build();
 
         Map<String, List<String>> typePropertyPathMap = searchSpec.getProjections();
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
index bf6b07f..677f4bd 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
@@ -42,13 +42,13 @@
     }
 
     @Test
-    public void testInvalidSchemaReferences_fromSystemUiVisibility() {
+    public void testInvalidSchemaReferences_fromDisplayedBySystem() {
         IllegalArgumentException expected =
                 expectThrows(
                         IllegalArgumentException.class,
                         () ->
                                 new SetSchemaRequest.Builder()
-                                        .setSchemaTypeVisibilityForSystemUi("InvalidSchema", false)
+                                        .setSchemaTypeDisplayedBySystem("InvalidSchema", false)
                                         .build());
         assertThat(expected).hasMessageThat().contains("referenced, but were not added");
     }
@@ -71,30 +71,30 @@
     }
 
     @Test
-    public void testSchemaTypeVisibilityForSystemUi_visible() {
+    public void testSetSchemaTypeDisplayedBySystem_displayed() {
         AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
 
-        // By default, the schema is visible.
+        // By default, the schema is displayed.
         SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
-        assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
+        assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty();
 
         request =
                 new SetSchemaRequest.Builder()
                         .addSchemas(schema)
-                        .setSchemaTypeVisibilityForSystemUi("Schema", true)
+                        .setSchemaTypeDisplayedBySystem("Schema", true)
                         .build();
-        assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
+        assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty();
     }
 
     @Test
-    public void testSchemaTypeVisibilityForSystemUi_notVisible() {
+    public void testSetSchemaTypeDisplayedBySystem_notDisplayed() {
         AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
         SetSchemaRequest request =
                 new SetSchemaRequest.Builder()
                         .addSchemas(schema)
-                        .setSchemaTypeVisibilityForSystemUi("Schema", false)
+                        .setSchemaTypeDisplayedBySystem("Schema", false)
                         .build();
-        assertThat(request.getSchemasNotVisibleToSystemUi()).containsExactly("Schema");
+        assertThat(request.getSchemasNotDisplayedBySystem()).containsExactly("Schema");
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
index 4d04a7a..8de9454 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
@@ -78,15 +78,15 @@
             "VALID_FLAG_BITS", "UNASSIGNED_TOKEN", "MAX_EVENT_TYPE"};
     // All fields in this list are final constants defining event types and not persisted
     private static final String[] EVENT_TYPES = {"NONE", "ACTIVITY_DESTROYED", "ACTIVITY_PAUSED",
-            "ACTIVITY_RESUMED", "ACTIVITY_STOPPED", "CHOOSER_ACTION", "CONFIGURATION_CHANGE",
-            "CONTINUE_PREVIOUS_DAY", "CONTINUING_FOREGROUND_SERVICE", "DEVICE_SHUTDOWN",
-            "DEVICE_STARTUP", "END_OF_DAY", "FLUSH_TO_DISK", "FOREGROUND_SERVICE_START",
-            "FOREGROUND_SERVICE_STOP", "KEYGUARD_HIDDEN", "KEYGUARD_SHOWN", "LOCUS_ID_SET",
-            "MOVE_TO_BACKGROUND", "MOVE_TO_FOREGROUND", "NOTIFICATION_INTERRUPTION",
-            "NOTIFICATION_SEEN", "ROLLOVER_FOREGROUND_SERVICE", "SCREEN_INTERACTIVE",
-            "SCREEN_NON_INTERACTIVE", "SHORTCUT_INVOCATION", "SLICE_PINNED", "SLICE_PINNED_PRIV",
-            "STANDBY_BUCKET_CHANGED", "SYSTEM_INTERACTION", "USER_INTERACTION", "USER_STOPPED",
-            "USER_UNLOCKED"};
+            "ACTIVITY_RESUMED", "ACTIVITY_STOPPED", "APP_COMPONENT_USED", "CHOOSER_ACTION",
+            "CONFIGURATION_CHANGE", "CONTINUE_PREVIOUS_DAY", "CONTINUING_FOREGROUND_SERVICE",
+            "DEVICE_SHUTDOWN", "DEVICE_STARTUP", "END_OF_DAY", "FLUSH_TO_DISK",
+            "FOREGROUND_SERVICE_START", "FOREGROUND_SERVICE_STOP", "KEYGUARD_HIDDEN",
+            "KEYGUARD_SHOWN", "LOCUS_ID_SET", "MOVE_TO_BACKGROUND", "MOVE_TO_FOREGROUND",
+            "NOTIFICATION_INTERRUPTION", "NOTIFICATION_SEEN", "ROLLOVER_FOREGROUND_SERVICE",
+            "SCREEN_INTERACTIVE", "SCREEN_NON_INTERACTIVE", "SHORTCUT_INVOCATION", "SLICE_PINNED",
+            "SLICE_PINNED_PRIV", "STANDBY_BUCKET_CHANGED", "SYSTEM_INTERACTION", "USER_INTERACTION",
+            "USER_STOPPED", "USER_UNLOCKED"};
 
     @Test
     public void testUsageEventsFields() {
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
index eae41e3..7bc81cd 100644
--- a/core/tests/coretests/src/android/graphics/FontListParserTest.java
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -26,6 +26,8 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static junit.framework.Assert.fail;
+
 import android.graphics.fonts.FontStyle;
 import android.os.LocaleList;
 import android.text.FontConfig;
@@ -44,6 +46,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 
@@ -221,9 +224,113 @@
                 .that(readFamily(serialized)).isEqualTo(expected);
     }
 
+    @Test
+    public void invalidXml_unpaired_family() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'>"
+                + "    <font index='0'>test.ttc</font>"
+                + "</familyset>";
+
+        try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+            FontListParser.parse(is);
+            fail();
+        } catch (IOException | XmlPullParserException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void invalidXml_unpaired_font() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'>"
+                + "    <font index='0'>test.ttc"
+                + "  </family>"
+                + "</familyset>";
+
+        try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+            FontListParser.parse(is);
+            fail();
+        } catch (IOException | XmlPullParserException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void invalidXml_unpaired_axis() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'>"
+                + "    <font index='0'>test.ttc"
+                + "        <axis tag=\"wght\" styleValue=\"0\" >"
+                + "    </font>"
+                + "  </family>"
+                + "</familyset>";
+
+        try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+            FontListParser.parse(is);
+            fail();
+        } catch (IOException | XmlPullParserException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void invalidXml_unclosed_family() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'"
+                + "    <font index='0'>test.ttc</font>"
+                + "  </family>"
+                + "</familyset>";
+
+        try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+            FontListParser.parse(is);
+            fail();
+        } catch (IOException | XmlPullParserException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void invalidXml_unclosed_font() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'>"
+                + "    <font index='0'"
+                + "  </family>"
+                + "</familyset>";
+
+        try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+            FontListParser.parse(is);
+            fail();
+        } catch (IOException | XmlPullParserException e) {
+            // pass
+        }
+    }
+
+    @Test
+    public void invalidXml_unclosed_axis() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset>"
+                + "  <family name='sans-serif'>"
+                + "    <font index='0'>test.ttc"
+                + "        <axis tag=\"wght\" styleValue=\"0\""
+                + "    </font>"
+                + "  </family>"
+                + "</familyset>";
+
+        try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+            FontListParser.parse(is);
+            fail();
+        } catch (IOException | XmlPullParserException e) {
+            // pass
+        }
+    }
+
     private FontConfig.FontFamily readFamily(String xml)
             throws IOException, XmlPullParserException {
-        StandardCharsets.UTF_8.name();
         ByteArrayInputStream buffer = new ByteArrayInputStream(
                 xml.getBytes(StandardCharsets.UTF_8));
         XmlPullParser parser = Xml.newPullParser();
diff --git a/core/tests/coretests/src/android/os/BytesMatcherTest.java b/core/tests/coretests/src/android/os/BytesMatcherTest.java
new file mode 100644
index 0000000..67c1b3c9
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BytesMatcherTest.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 static com.android.internal.util.HexDump.hexStringToByteArray;
+
+import android.bluetooth.BluetoothUuid;
+import android.net.MacAddress;
+
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@SmallTest
+public class BytesMatcherTest extends TestCase {
+    @Test
+    public void testEmpty() throws Exception {
+        BytesMatcher matcher = BytesMatcher.decode("");
+        assertFalse(matcher.test(hexStringToByteArray("cafe")));
+        assertFalse(matcher.test(hexStringToByteArray("")));
+    }
+
+    @Test
+    public void testExact() throws Exception {
+        BytesMatcher matcher = BytesMatcher.decode("+cafe");
+        assertTrue(matcher.test(hexStringToByteArray("cafe")));
+        assertFalse(matcher.test(hexStringToByteArray("beef")));
+        assertFalse(matcher.test(hexStringToByteArray("ca")));
+        assertFalse(matcher.test(hexStringToByteArray("cafe00")));
+    }
+
+    @Test
+    public void testMask() throws Exception {
+        BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00");
+        assertTrue(matcher.test(hexStringToByteArray("cafe")));
+        assertTrue(matcher.test(hexStringToByteArray("ca88")));
+        assertFalse(matcher.test(hexStringToByteArray("beef")));
+        assertFalse(matcher.test(hexStringToByteArray("ca")));
+        assertFalse(matcher.test(hexStringToByteArray("cafe00")));
+    }
+
+    @Test
+    public void testMacAddress() throws Exception {
+        BytesMatcher matcher = BytesMatcher.decode("+cafe00112233/ffffff000000");
+        assertTrue(matcher.testMacAddress(
+                MacAddress.fromString("ca:fe:00:00:00:00")));
+        assertFalse(matcher.testMacAddress(
+                MacAddress.fromString("f0:0d:00:00:00:00")));
+    }
+
+    @Test
+    public void testBluetoothUuid() throws Exception {
+        BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00");
+        assertTrue(matcher.testBluetoothUuid(
+                BluetoothUuid.parseUuidFrom(hexStringToByteArray("cafe"))));
+        assertFalse(matcher.testBluetoothUuid(
+                BluetoothUuid.parseUuidFrom(hexStringToByteArray("beef"))));
+    }
+
+    /**
+     * Verify that single matcher can be configured to match Bluetooth UUIDs of
+     * varying lengths.
+     */
+    @Test
+    public void testBluetoothUuid_Mixed() throws Exception {
+        BytesMatcher matcher = BytesMatcher.decode("+aaaa/ff00,+bbbbbbbb/ffff0000");
+        assertTrue(matcher.testBluetoothUuid(
+                BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaa"))));
+        assertFalse(matcher.testBluetoothUuid(
+                BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbb"))));
+        assertTrue(matcher.testBluetoothUuid(
+                BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbbbbbb"))));
+        assertFalse(matcher.testBluetoothUuid(
+                BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaaaaaa"))));
+    }
+
+    @Test
+    public void testSerialize() throws Exception {
+        BytesMatcher matcher = new BytesMatcher();
+        matcher.addRejectRule(hexStringToByteArray("cafe00112233"),
+                hexStringToByteArray("ffffff000000"));
+        matcher.addRejectRule(hexStringToByteArray("beef00112233"),
+                null);
+        matcher.addAcceptRule(hexStringToByteArray("000000000000"),
+                hexStringToByteArray("000000000000"));
+
+        assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff")));
+        assertFalse(matcher.test(hexStringToByteArray("beef00112233")));
+        assertTrue(matcher.test(hexStringToByteArray("beef00ffffff")));
+
+        // Bounce through serialization pass and confirm it still works
+        matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
+
+        assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff")));
+        assertFalse(matcher.test(hexStringToByteArray("beef00112233")));
+        assertTrue(matcher.test(hexStringToByteArray("beef00ffffff")));
+    }
+
+    @Test
+    public void testOrdering_RejectFirst() throws Exception {
+        BytesMatcher matcher = BytesMatcher.decode("-ff/0f,+ff/f0");
+        assertFalse(matcher.test(hexStringToByteArray("ff")));
+        assertTrue(matcher.test(hexStringToByteArray("f0")));
+        assertFalse(matcher.test(hexStringToByteArray("0f")));
+    }
+
+    @Test
+    public void testOrdering_AcceptFirst() throws Exception {
+        BytesMatcher matcher = BytesMatcher.decode("+ff/f0,-ff/0f");
+        assertTrue(matcher.test(hexStringToByteArray("ff")));
+        assertTrue(matcher.test(hexStringToByteArray("f0")));
+        assertFalse(matcher.test(hexStringToByteArray("0f")));
+    }
+}
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index 8941190..c06405a 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -20,6 +20,7 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import android.hardware.vibrator.IVibrator;
 import android.platform.test.annotations.Presubmit;
 
 import org.junit.Test;
@@ -33,16 +34,16 @@
     @Test
     public void testHasAmplitudeControl() {
         assertFalse(createInfo(/* capabilities= */ 0).hasAmplitudeControl());
-        assertTrue(createInfo(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS
-                | VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL).hasAmplitudeControl());
+        assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS
+                | IVibrator.CAP_AMPLITUDE_CONTROL).hasAmplitudeControl());
     }
 
     @Test
     public void testHasCapabilities() {
-        assertTrue(createInfo(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS)
-                .hasCapability(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS));
-        assertFalse(createInfo(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS)
-                .hasCapability(VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL));
+        assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
+                .hasCapability(IVibrator.CAP_COMPOSE_EFFECTS));
+        assertFalse(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
+                .hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL));
     }
 
     @Test
@@ -59,7 +60,7 @@
 
     @Test
     public void testIsPrimitiveSupported() {
-        VibratorInfo info = new VibratorInfo(/* id= */ 0, VibratorInfo.CAPABILITY_COMPOSE_EFFECTS,
+        VibratorInfo info = new VibratorInfo(/* id= */ 0, IVibrator.CAP_COMPOSE_EFFECTS,
                 null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
         assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
         assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
@@ -73,30 +74,30 @@
     @Test
     public void testEquals() {
         VibratorInfo empty = new VibratorInfo(1, 0, null, null);
-        VibratorInfo complete = new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+        VibratorInfo complete = new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
                 new int[]{VibrationEffect.EFFECT_CLICK},
                 new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
 
         assertEquals(complete, complete);
-        assertEquals(complete, new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+        assertEquals(complete, new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
                 new int[]{VibrationEffect.EFFECT_CLICK},
                 new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}));
 
         assertFalse(empty.equals(new VibratorInfo(1, 0, new int[]{}, new int[]{})));
-        assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_COMPOSE_EFFECTS,
+        assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS,
                 new int[]{VibrationEffect.EFFECT_CLICK},
                 new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})));
-        assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+        assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
                 new int[]{}, new int[]{})));
-        assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+        assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
                 null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})));
-        assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+        assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
                 new int[]{VibrationEffect.EFFECT_CLICK}, null)));
     }
 
     @Test
     public void testSerialization() {
-        VibratorInfo original = new VibratorInfo(1, VibratorInfo.CAPABILITY_COMPOSE_EFFECTS,
+        VibratorInfo original = new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS,
                 new int[]{VibrationEffect.EFFECT_CLICK}, null);
 
         Parcel parcel = Parcel.obtain();
diff --git a/core/tests/coretests/src/android/security/CredentialManagementAppTest.java b/core/tests/coretests/src/android/security/CredentialManagementAppTest.java
index 366aabd..fa824b1 100644
--- a/core/tests/coretests/src/android/security/CredentialManagementAppTest.java
+++ b/core/tests/coretests/src/android/security/CredentialManagementAppTest.java
@@ -37,8 +37,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
-import java.util.Iterator;
-import java.util.Map;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -149,37 +147,6 @@
     private void assertCredentialManagementAppsEqual(CredentialManagementApp actual,
             CredentialManagementApp expected) {
         assertThat(actual.getPackageName(), is(expected.getPackageName()));
-        assertAuthenticationPoliciesEqual(actual.getAuthenticationPolicy(),
-                expected.getAuthenticationPolicy());
-    }
-
-    private void assertAuthenticationPoliciesEqual(AppUriAuthenticationPolicy actual,
-            AppUriAuthenticationPolicy expected) {
-        Iterator<Map.Entry<String, Map<Uri, String>>> actualIter =
-                actual.getAppAndUriMappings().entrySet().iterator();
-        Iterator<Map.Entry<String, Map<Uri, String>>> expectedIter =
-                expected.getAppAndUriMappings().entrySet().iterator();
-
-        assertThat(actual.getAppAndUriMappings().size(),
-                is(expected.getAppAndUriMappings().size()));
-        while (actualIter.hasNext()) {
-            Map.Entry<String, Map<Uri, String>> actualAppToUri = actualIter.next();
-            Map.Entry<String, Map<Uri, String>> expectedAppToUri = expectedIter.next();
-            assertThat(actualAppToUri.getKey(), is(expectedAppToUri.getKey()));
-            assertUrisToAliasesEqual(actualAppToUri.getValue(), expectedAppToUri.getValue());
-        }
-    }
-
-    private void assertUrisToAliasesEqual(Map<Uri, String> actual, Map<Uri, String> expected) {
-        Iterator<Map.Entry<Uri, String>> actualIter = actual.entrySet().iterator();
-        Iterator<Map.Entry<Uri, String>> expectedIter = expected.entrySet().iterator();
-
-        assertThat(actual.size(), is(expected.size()));
-        while (actualIter.hasNext()) {
-            Map.Entry<Uri, String> actualUriToAlias = actualIter.next();
-            Map.Entry<Uri, String> expectedUriToAlias = expectedIter.next();
-            assertThat(actualUriToAlias.getKey(), is(expectedUriToAlias.getKey()));
-            assertThat(actualUriToAlias.getValue(), is(expectedUriToAlias.getValue()));
-        }
+        assertThat(actual.getAuthenticationPolicy(), is(expected.getAuthenticationPolicy()));
     }
 }
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index ab24f89..7e1e7f4 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -33,9 +33,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 
-import java.util.Arrays;
-import java.util.List;
-
 /**
  * Tests for AccessibilityInteractionClient
  */
@@ -65,7 +62,7 @@
         final long accessibilityNodeId = 0x4321L;
         AccessibilityNodeInfo nodeFromConnection = AccessibilityNodeInfo.obtain();
         nodeFromConnection.setSourceNodeId(accessibilityNodeId, windowId);
-        mMockConnection.mInfosToReturn = Arrays.asList(nodeFromConnection);
+        mMockConnection.mInfoToReturn = nodeFromConnection;
         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
         AccessibilityNodeInfo node = client.findAccessibilityNodeInfoByAccessibilityId(
                 MOCK_CONNECTION_ID, windowId, accessibilityNodeId, true, 0, null);
@@ -75,7 +72,7 @@
     }
 
     private static class MockConnection extends AccessibilityServiceConnectionImpl {
-        List<AccessibilityNodeInfo> mInfosToReturn;
+        AccessibilityNodeInfo mInfoToReturn;
 
         @Override
         public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
@@ -83,7 +80,7 @@
                 IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
                 Bundle arguments) {
             try {
-                callback.setFindAccessibilityNodeInfosResult(mInfosToReturn, interactionId);
+                callback.setFindAccessibilityNodeInfoResult(mInfoToReturn, interactionId);
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
diff --git a/core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java
similarity index 65%
rename from core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java
rename to core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java
index bec3d16..cf126c6 100644
--- a/core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java
@@ -31,7 +31,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class DischargedPowerCalculatorTest {
+public class BatteryChargeCalculatorTest {
     private static final double PRECISION = 0.00001;
 
     @Rule
@@ -40,27 +40,41 @@
 
     @Test
     public void testDischargeTotals() {
+        BatteryChargeCalculator calculator =
+                new BatteryChargeCalculator(mStatsRule.getPowerProfile());
+
         final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
 
         mStatsRule.setTime(1000, 1000);
         batteryStats.resetAllStatsCmdLocked();
         batteryStats.setNoAutoReset(true);
         batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
-                /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000,
-                1_000_000, 1_000_000);
+                /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
+                1_000_000, 1_000_000, 1_000_000);
         batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
-                /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0, 2_000_000,
-                2_000_000, 2_000_000);
+                /* plugType */ 0, 85, 72, 3700, 3_000_000, 4_000_000, 0,
+                1_500_000, 1_500_000, 1_500_000);
+        batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+                /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0,
+                2_000_000, 2_000_000, 2_000_000);
 
-        DischargedPowerCalculator calculator =
-                new DischargedPowerCalculator(mStatsRule.getPowerProfile());
-
-        final BatteryUsageStats batteryUsageStats = mStatsRule.apply(calculator);
+        BatteryUsageStats batteryUsageStats = mStatsRule.apply(calculator);
 
         assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(10);
         assertThat(batteryUsageStats.getDischargedPowerRange().getLower())
                 .isWithin(PRECISION).of(360.0);
         assertThat(batteryUsageStats.getDischargedPowerRange().getUpper())
                 .isWithin(PRECISION).of(400.0);
+        assertThat(batteryUsageStats.getBatteryTimeRemainingMs()).isEqualTo(8_000_000);
+        assertThat(batteryUsageStats.getChargeTimeRemainingMs()).isEqualTo(-1);
+
+        // Plug in
+        batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_CHARGING, 100,
+                BatteryManager.BATTERY_PLUGGED_USB, 80, 72, 3700, 2_400_000, 4_000_000, 100,
+                4_000_000, 4_000_000, 4_000_000);
+
+        batteryUsageStats = mStatsRule.apply(calculator);
+
+        assertThat(batteryUsageStats.getChargeTimeRemainingMs()).isEqualTo(100_000);
     }
 }
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 74c37ada..ee472880 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -23,6 +23,7 @@
 @Suite.SuiteClasses({
         AmbientDisplayPowerCalculatorTest.class,
         AudioPowerCalculatorTest.class,
+        BatteryChargeCalculatorTest.class,
         BatteryStatsCpuTimesTest.class,
         BatteryStatsBackgroundStatsTest.class,
         BatteryStatsBinderCallStatsTest.class,
@@ -49,7 +50,6 @@
         CameraPowerCalculatorTest.class,
         CpuPowerCalculatorTest.class,
         CustomMeasuredPowerCalculatorTest.class,
-        DischargedPowerCalculatorTest.class,
         FlashlightPowerCalculatorTest.class,
         GnssPowerCalculatorTest.class,
         IdlePowerCalculatorTest.class,
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 2f41b90..89644e2 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -146,6 +146,13 @@
 }
 
 prebuilt_etc {
+    name: "allowed_privapp_com.android.car.rotary",
+    sub_dir: "permissions",
+    src: "com.android.car.rotary.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
     name: "allowed_privapp_com.android.car.ui.paintbooth",
     sub_dir: "permissions",
     src: "com.android.car.ui.paintbooth.xml",
diff --git a/data/etc/car/com.android.car.rotary.xml b/data/etc/car/com.android.car.rotary.xml
new file mode 100644
index 0000000..eddef1a
--- /dev/null
+++ b/data/etc/car/com.android.car.rotary.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.rotary">
+        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+</permissions>
+
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index ea42246..77a38a9 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -166,6 +166,7 @@
     <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" />
     <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="audioserver" />
     <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="audioserver" />
+    <assign-permission name="android.permission.OBSERVE_SENSOR_PRIVACY" uid="audioserver" />
 
     <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
@@ -176,6 +177,7 @@
     <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="cameraserver" />
     <assign-permission name="android.permission.WATCH_APPOPS" uid="cameraserver" />
     <assign-permission name="android.permission.MANAGE_APP_OPS_MODES" uid="cameraserver" />
+    <assign-permission name="android.permission.OBSERVE_SENSOR_PRIVACY" uid="cameraserver" />
 
     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
 
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index cabfad4..b7bf8ab 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -43,6 +43,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-2093859262": {
+      "message": "setClientVisible: %s clientVisible=%b Callers=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/WindowToken.java"
+    },
     "-2072089308": {
       "message": "Attempted to add window with token that is a sub-window: %s.  Aborting.",
       "level": "WARN",
@@ -811,6 +817,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/Task.java"
     },
+    "-1159577965": {
+      "message": "Focus requested for input consumer=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/InputMonitor.java"
+    },
     "-1156118957": {
       "message": "Updated config=%s",
       "level": "DEBUG",
@@ -823,12 +835,6 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java"
     },
-    "-1144293044": {
-      "message": "SURFACE SET FREEZE LAYER: %s",
-      "level": "INFO",
-      "group": "WM_SHOW_TRANSACTIONS",
-      "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
-    },
     "-1142279614": {
       "message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
       "level": "VERBOSE",
@@ -1831,6 +1837,12 @@
       "group": "WM_DEBUG_TASKS",
       "at": "com\/android\/server\/wm\/RootWindowContainer.java"
     },
+    "63329306": {
+      "message": "commitVisibility: %s: visible=%b mVisibleRequested=%b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/WallpaperWindowToken.java"
+    },
     "73987756": {
       "message": "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s",
       "level": "INFO",
@@ -2461,6 +2473,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "691515534": {
+      "message": "  Commit wallpaper becoming invisible: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "693423992": {
       "message": "setAnimationLocked: setting mFocusMayChange true",
       "level": "INFO",
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index d1e94df..a927f53 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -36,11 +36,12 @@
 
 java_test_host {
     name: "error_prone_android_framework_test",
-    test_suites: ["general-tests"],
     srcs: ["tests/java/**/*.java"],
     java_resource_dirs: ["tests/res"],
     java_resources: [":error_prone_android_framework_testdata"],
     static_libs: [
+        "truth-prebuilt",
+        "kxml2-2.3.0",
         "error_prone_android_framework_lib",
         "error_prone_test_helpers",
         "hamcrest-library",
@@ -48,6 +49,9 @@
         "platform-test-annotations",
         "junit",
     ],
+    test_options: {
+        unit_test: true,
+    },
 }
 
 filegroup {
diff --git a/errorprone/TEST_MAPPING b/errorprone/TEST_MAPPING
deleted file mode 100644
index ee4552f..0000000
--- a/errorprone/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-    "presubmit": [
-        {
-            "name": "error_prone_android_framework_test"
-        }
-    ]
-}
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 63df0db..95c7715 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -136,7 +136,7 @@
                 customization.getAdditionalNamedFamilies();
 
         parser.require(XmlPullParser.START_TAG, null, "familyset");
-        while (parser.next() != XmlPullParser.END_TAG) {
+        while (keepReading(parser)) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             String tag = parser.getName();
             if (tag.equals("family")) {
@@ -158,6 +158,12 @@
         return new FontConfig(families, aliases, lastModifiedDate, configVersion);
     }
 
+    private static boolean keepReading(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        int next = parser.next();
+        return next != XmlPullParser.END_TAG && next != XmlPullParser.END_DOCUMENT;
+    }
+
     /**
      * Read family tag in fonts.xml or oem_customization.xml
      */
@@ -168,7 +174,7 @@
         final String lang = parser.getAttributeValue("", "lang");
         final String variant = parser.getAttributeValue(null, "variant");
         final List<FontConfig.Font> fonts = new ArrayList<>();
-        while (parser.next() != XmlPullParser.END_TAG) {
+        while (keepReading(parser)) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             final String tag = parser.getName();
             if (tag.equals(TAG_FONT)) {
@@ -232,7 +238,7 @@
         boolean isItalic = STYLE_ITALIC.equals(parser.getAttributeValue(null, ATTR_STYLE));
         String fallbackFor = parser.getAttributeValue(null, ATTR_FALLBACK_FOR);
         StringBuilder filename = new StringBuilder();
-        while (parser.next() != XmlPullParser.END_TAG) {
+        while (keepReading(parser)) {
             if (parser.getEventType() == XmlPullParser.TEXT) {
                 filename.append(parser.getText());
             }
@@ -359,6 +365,8 @@
                 case XmlPullParser.END_TAG:
                     depth--;
                     break;
+                case XmlPullParser.END_DOCUMENT:
+                    return;
             }
         }
     }
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index f6f770b..da5162b 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -719,11 +719,11 @@
     /** @hide */
     public boolean stretch(float left, float top, float right, float bottom,
             float vecX, float vecY, float maxStretchAmount) {
-        if (1.0 < vecX || vecX < -1.0) {
-            throw new IllegalArgumentException("vecX must be in the range [-1, 1], was " + vecX);
+        if (Float.isInfinite(vecX) || Float.isNaN(vecX)) {
+            throw new IllegalArgumentException("vecX must be a finite, non-NaN value " + vecX);
         }
-        if (1.0 < vecY || vecY < -1.0) {
-            throw new IllegalArgumentException("vecY must be in the range [-1, 1], was " + vecY);
+        if (Float.isInfinite(vecY) || Float.isNaN(vecY)) {
+            throw new IllegalArgumentException("vecY must be a finite, non-NaN value " + vecY);
         }
         if (top >= bottom || left >= right) {
             throw new IllegalArgumentException(
@@ -734,7 +734,16 @@
             throw new IllegalArgumentException(
                     "The max stretch amount must be >0, got " + maxStretchAmount);
         }
-        return nStretch(mNativeRenderNode, left, top, right, bottom, vecX, vecY, maxStretchAmount);
+        return nStretch(
+                mNativeRenderNode,
+                left,
+                top,
+                right,
+                bottom,
+                vecX,
+                vecY,
+                maxStretchAmount
+        );
     }
 
     /**
diff --git a/keystore/java/android/security/AppUriAuthenticationPolicy.java b/keystore/java/android/security/AppUriAuthenticationPolicy.java
index 0244ce9..df79912 100644
--- a/keystore/java/android/security/AppUriAuthenticationPolicy.java
+++ b/keystore/java/android/security/AppUriAuthenticationPolicy.java
@@ -238,4 +238,21 @@
         return aliases;
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof AppUriAuthenticationPolicy)) {
+            return false;
+        }
+        AppUriAuthenticationPolicy other = (AppUriAuthenticationPolicy) obj;
+        return Objects.equals(mAppToUris, other.mAppToUris);
+    }
+
+    @Override
+    public int hashCode() {
+        return mAppToUris.hashCode();
+    }
+
 }
diff --git a/keystore/java/android/security/UrisToAliases.java b/keystore/java/android/security/UrisToAliases.java
index 65d433a..9a8b659 100644
--- a/keystore/java/android/security/UrisToAliases.java
+++ b/keystore/java/android/security/UrisToAliases.java
@@ -30,6 +30,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * The mapping from URI to alias, which determines the alias to use when the user visits a URI.
@@ -135,4 +136,21 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeMap(mUrisToAliases);
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof UrisToAliases)) {
+            return false;
+        }
+        UrisToAliases other = (UrisToAliases) obj;
+        return Objects.equals(mUrisToAliases, other.mUrisToAliases);
+    }
+
+    @Override
+    public int hashCode() {
+        return mUrisToAliases.hashCode();
+    }
 }
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index c79c12c..72735a7 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -279,8 +279,10 @@
  * }
  */
 public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
-    private static final X500Principal DEFAULT_CERT_SUBJECT =
+    private static final X500Principal DEFAULT_ATTESTATION_CERT_SUBJECT =
             new X500Principal("CN=Android Keystore Key");
+    private static final X500Principal DEFAULT_SELF_SIGNED_CERT_SUBJECT =
+            new X500Principal("CN=Fake");
     private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
     private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
     private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
@@ -366,7 +368,11 @@
         }
 
         if (certificateSubject == null) {
-            certificateSubject = DEFAULT_CERT_SUBJECT;
+            if (attestationChallenge == null) {
+                certificateSubject = DEFAULT_SELF_SIGNED_CERT_SUBJECT;
+            } else {
+                certificateSubject = DEFAULT_ATTESTATION_CERT_SUBJECT;
+            }
         }
         if (certificateNotBefore == null) {
             certificateNotBefore = DEFAULT_CERT_NOT_BEFORE;
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 1320780..d31e637b 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
@@ -327,6 +327,10 @@
         return mImpl;
     }
 
+    public ShellExecutor getMainExecutor() {
+        return mMainExecutor;
+    }
+
     /**
      * Hides the current input method, wherever it may be focused, via InputMethodManagerInternal.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 2f31acd..9ef3fb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -340,7 +340,7 @@
             mSettingsIcon.setVisibility(GONE);
         } else {
             mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
-            mTaskView.setListener(mContext.getMainExecutor(), mTaskViewListener);
+            mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
             mExpandedViewContainer.addView(mTaskView);
             bringChildToFront(mTaskView);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 6f5f2eb..aab2334 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -234,8 +234,8 @@
             final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
             mStarter.startShortcut(packageName, id, stage, position, opts, user);
         } else {
-            mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position,
-                    opts);
+            mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT),
+                    mContext, null, stage, position, opts);
         }
     }
 
@@ -295,7 +295,8 @@
                 @Nullable Bundle options);
         void startShortcut(String packageName, String shortcutId, @StageType int stage,
                 @StagePosition int position, @Nullable Bundle options, UserHandle user);
-        void startIntent(PendingIntent intent, @StageType int stage, @StagePosition int position,
+        void startIntent(PendingIntent intent, Context context, Intent fillInIntent,
+                @StageType int stage, @StagePosition int position,
                 @Nullable Bundle options);
         void enterSplitScreen(int taskId, boolean leftOrTop);
         void exitSplitScreen();
@@ -336,10 +337,11 @@
         }
 
         @Override
-        public void startIntent(PendingIntent intent, int stage, int position,
+        public void startIntent(PendingIntent intent, Context context,
+                @Nullable Intent fillInIntent, int stage, int position,
                 @Nullable Bundle options) {
             try {
-                intent.send(null, 0, null, null, null, null, options);
+                intent.send(mContext, 0, fillInIntent, null, null, null, options);
             } catch (PendingIntent.CanceledException e) {
                 Slog.e(TAG, "Failed to launch activity", e);
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
index 0552601..f4c0f93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
@@ -52,6 +52,9 @@
     private final SyncTransactionQueue mSyncQueue;
     private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>();
 
+    // TODO(shell-transitions): Remove when switched to shell-transitions.
+    private final SparseArray<Point> mPositionByTaskId = new SparseArray<>();
+
     RunningTaskInfo mPrimary;
     RunningTaskInfo mSecondary;
     SurfaceControl mPrimarySurface;
@@ -167,6 +170,7 @@
     @Override
     public void onTaskVanished(RunningTaskInfo taskInfo) {
         synchronized (this) {
+            mPositionByTaskId.remove(taskInfo.taskId);
             if (taskInfo.hasParentTask()) {
                 mLeashByTaskId.remove(taskInfo.taskId);
                 return;
@@ -200,16 +204,24 @@
         }
         synchronized (this) {
             if (taskInfo.hasParentTask()) {
+                // changed messages are noisy since it reports on every ensureVisibility. This
+                // conflicts with legacy app-transitions which "swaps" the position to a
+                // leash. For now, only update when position actually changes to avoid
+                // poorly-timed duplicate calls.
+                if (taskInfo.positionInParent.equals(mPositionByTaskId.get(taskInfo.taskId))) {
+                    return;
+                }
                 handleChildTaskChanged(taskInfo);
-                return;
+            } else {
+                handleTaskInfoChanged(taskInfo);
             }
-
-            handleTaskInfoChanged(taskInfo);
+            mPositionByTaskId.put(taskInfo.taskId, new Point(taskInfo.positionInParent));
         }
     }
 
     private void handleChildTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
         mLeashByTaskId.put(taskInfo.taskId, leash);
+        mPositionByTaskId.put(taskInfo.taskId, new Point(taskInfo.positionInParent));
         if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
         updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 843e27f..7ed7fd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -172,6 +172,10 @@
     };
 
     private ActivityManager.RunningTaskInfo mTaskInfo;
+    // To handle the edge case that onTaskInfoChanged callback is received during the entering
+    // PiP transition, where we do not want to intercept the transition but still want to apply the
+    // changed RunningTaskInfo when it finishes.
+    private ActivityManager.RunningTaskInfo mDeferredTaskInfo;
     private WindowContainerToken mToken;
     private SurfaceControl mLeash;
     private State mState = State.UNDEFINED;
@@ -520,12 +524,18 @@
         mPipTransitionController.sendOnPipTransitionStarted(direction);
     }
 
-    private void sendOnPipTransitionFinished(
+    @VisibleForTesting
+    void sendOnPipTransitionFinished(
             @PipAnimationController.TransitionDirection int direction) {
         if (direction == TRANSITION_DIRECTION_TO_PIP) {
             mState = State.ENTERED_PIP;
         }
         mPipTransitionController.sendOnPipTransitionFinished(direction);
+        // Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent.
+        if (direction == TRANSITION_DIRECTION_TO_PIP && mDeferredTaskInfo != null) {
+            onTaskInfoChanged(mDeferredTaskInfo);
+            mDeferredTaskInfo = null;
+        }
     }
 
     private void sendOnPipTransitionCancelled(
@@ -567,6 +577,11 @@
     @Override
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
         Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
+        if (mState != State.ENTERED_PIP) {
+            Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState);
+            mDeferredTaskInfo = info;
+            return;
+        }
         mPipBoundsState.setLastPipComponentName(info.topActivity);
         mPipBoundsState.setOverrideMinSize(
                 mPipBoundsAlgorithm.getMinimalSize(info.topActivityInfo));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 31057f8..c726c01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -65,6 +65,8 @@
     private static final int PINCH_RESIZE_SNAP_DURATION = 250;
     private static final int PINCH_RESIZE_MAX_ANGLE_ROTATION = 45;
     private static final float PINCH_RESIZE_AUTO_MAX_RATIO = 0.9f;
+    private static final float OVERROTATE_DAMP_FACTOR = 0.4f;
+    private static final float ANGLE_THRESHOLD = 5f;
 
     private final Context mContext;
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
@@ -423,26 +425,28 @@
                 float down1X = mDownSecondaryPoint.x;
                 float down1Y = mDownSecondaryPoint.y;
 
+                float angle = 0;
                 if (down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY) {
                     // Top right + Bottom left pinch to zoom.
-                    mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+                    angle = calculateRotationAngle(mLastResizeBounds.centerX(),
                             mLastResizeBounds.centerY(), x0, y0, x1, y1, true);
                 } else if (down1X > focusX && down1Y < focusY
                         && down0X < focusX && down0Y > focusY) {
                     // Top right + Bottom left pinch to zoom.
-                    mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+                    angle = calculateRotationAngle(mLastResizeBounds.centerX(),
                             mLastResizeBounds.centerY(), x1, y1, x0, y0, true);
                 } else if (down0X < focusX && down0Y < focusY
                         && down1X > focusX && down1Y > focusY) {
                     // Top left + bottom right pinch to zoom.
-                    mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+                    angle = calculateRotationAngle(mLastResizeBounds.centerX(),
                             mLastResizeBounds.centerY(), x0, y0, x1, y1, false);
                 } else if (down1X < focusX && down1Y < focusY
                         && down0X > focusX && down0Y > focusY) {
                     // Top left + bottom right pinch to zoom.
-                    mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+                    angle = calculateRotationAngle(mLastResizeBounds.centerX(),
                             mLastResizeBounds.centerY(), x1, y1, x0, y0, false);
                 }
+                mAngle = angle;
 
                 mLastResizeBounds.set(PipPinchResizingAlgorithm.pinchResize(x0, y0, x1, y1,
                         mDownPoint.x, mDownPoint.y, mDownSecondaryPoint.x, mDownSecondaryPoint.y,
@@ -477,10 +481,40 @@
         }
 
         // Calculate the percentage difference of [0, 90] compare to the base angle.
-        double diff0 = (Math.max(0, Math.min(angle0, 90)) - baseAngle) / 90;
-        double diff1 = (Math.max(0, Math.min(angle1, 90)) - baseAngle) / 90;
+        double diff0 = (Math.max(-90, Math.min(angle0, 90)) - baseAngle) / 90;
+        double diff1 = (Math.max(-90, Math.min(angle1, 90)) - baseAngle) / 90;
 
-        return (float) (diff0 + diff1) / 2 * PINCH_RESIZE_MAX_ANGLE_ROTATION * (positive ? 1 : -1);
+        final float angle =
+                (float) (diff0 + diff1) / 2 * PINCH_RESIZE_MAX_ANGLE_ROTATION * (positive ? 1 : -1);
+
+        // Remove some degrees so that user doesn't immediately start rotating until a threshold
+        return angle / Math.abs(angle)
+                * Math.max(0, (Math.abs(dampedRotate(angle)) - ANGLE_THRESHOLD));
+    }
+
+    /**
+     * Given the current rotation angle, dampen it so that as it approaches the maximum angle,
+     * dampen it.
+     */
+    private float dampedRotate(float amount) {
+        if (Float.compare(amount, 0) == 0) return 0;
+
+        float f = amount / PINCH_RESIZE_MAX_ANGLE_ROTATION;
+        f = f / (Math.abs(f)) * (overRotateInfluenceCurve(Math.abs(f)));
+
+        // Clamp this factor, f, to -1 < f < 1
+        if (Math.abs(f) >= 1) {
+            f /= Math.abs(f);
+        }
+        return OVERROTATE_DAMP_FACTOR * f * PINCH_RESIZE_MAX_ANGLE_ROTATION;
+    }
+
+    /**
+     * Returns a value that corresponds to y = (f - 1)^3 + 1.
+     */
+    private float overRotateInfluenceCurve(float f) {
+        f -= 1.0f;
+        return f * f * f + 1.0f;
     }
 
     private void onDragCornerResize(MotionEvent ev) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 7ca5693..25a84bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -19,18 +19,17 @@
 import android.annotation.IntDef;
 import android.app.ActivityManager;
 import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserHandle;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.wm.shell.common.annotations.ExternalThread;
 import com.android.wm.shell.draganddrop.DragAndDropPolicy;
 
-import java.io.PrintWriter;
-
 /**
  * Interface to engage split-screen feature.
  * TODO: Figure out which of these are actually needed outside of the Shell
@@ -87,7 +86,7 @@
     /** Callback interface for listening to changes in a split-screen stage. */
     interface SplitScreenListener {
         void onStagePositionChanged(@StageType int stage, @StagePosition int position);
-        void onTaskStageChanged(int taskId, @StageType int stage);
+        void onTaskStageChanged(int taskId, @StageType int stage, boolean visible);
     }
 
     /** @return {@code true} if split-screen is currently visible. */
@@ -118,6 +117,7 @@
             @StageType int stage, @StagePosition int position, @Nullable Bundle options);
     void startShortcut(String packageName, String shortcutId, @StageType int stage,
             @StagePosition int position, @Nullable Bundle options, UserHandle user);
-    void startIntent(PendingIntent intent,
-            @StageType int stage, @StagePosition int position, @Nullable Bundle options);
+    void startIntent(PendingIntent intent, Context context,
+            @Nullable Intent fillInIntent, @StageType int stage,
+            @StagePosition int position, @Nullable Bundle options);
 }
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 11548ad..bb6f6f2 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
@@ -30,6 +30,7 @@
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -171,12 +172,13 @@
         }
     }
 
-    public void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
+    public void startIntent(PendingIntent intent, Context context,
+            Intent fillInIntent, @SplitScreen.StageType int stage,
             @SplitScreen.StagePosition int position, @Nullable Bundle options) {
         options = resolveStartStage(stage, position, options);
 
         try {
-            intent.send(null, 0, null, null, null, null, options);
+            intent.send(context, 0, fillInIntent, null, null, null, options);
         } catch (PendingIntent.CanceledException e) {
             Slog.e(TAG, "Failed to launch activity", e);
         }
@@ -348,10 +350,11 @@
         }
 
         @Override
-        public void startIntent(PendingIntent intent, int stage, int position,
-                @Nullable Bundle options) {
+        public void startIntent(PendingIntent intent, Context context, Intent fillInIntent,
+                int stage, int position, @Nullable Bundle options) {
             mMainExecutor.execute(() -> {
-                SplitScreenController.this.startIntent(intent, stage, position, options);
+                SplitScreenController.this.startIntent(intent, context, fillInIntent, stage,
+                        position, options);
             });
         }
     }
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 bbfbc40..b180bb5 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
@@ -225,7 +225,7 @@
     }
 
     private void onStageChildTaskStatusChanged(
-            StageListenerImpl stageListener, int taskId, boolean present) {
+            StageListenerImpl stageListener, int taskId, boolean present, boolean visible) {
 
         int stage;
         if (present) {
@@ -236,7 +236,7 @@
         }
 
         for (int i = mListeners.size() - 1; i >= 0; --i) {
-            mListeners.get(i).onTaskStageChanged(taskId, stage);
+            mListeners.get(i).onTaskStageChanged(taskId, stage, visible);
         }
     }
 
@@ -495,8 +495,8 @@
         }
 
         @Override
-        public void onChildTaskStatusChanged(int taskId, boolean present) {
-            StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present);
+        public void onChildTaskStatusChanged(int taskId, boolean present, boolean visible) {
+            StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present, visible);
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 10c742b..b8cdc4ab4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -58,7 +58,7 @@
     public interface StageListenerCallbacks {
         void onRootTaskAppeared();
         void onStatusChanged(boolean visible, boolean hasChildren);
-        void onChildTaskStatusChanged(int taskId, boolean present);
+        void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
         void onRootTaskVanished();
     }
     private final StageListenerCallbacks mCallbacks;
@@ -88,7 +88,7 @@
             mChildrenLeashes.put(taskId, leash);
             mChildrenTaskInfo.put(taskId, taskInfo);
             updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
-            mCallbacks.onChildTaskStatusChanged(taskId, true /* present */);
+            mCallbacks.onChildTaskStatusChanged(taskId, true /* present */, taskInfo.isVisible);
         } else {
             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
                     + "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -105,6 +105,8 @@
             mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
             updateChildTaskSurface(
                     taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */);
+            mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */,
+                    taskInfo.isVisible);
         } else {
             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
                     + "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -123,7 +125,7 @@
             mChildrenTaskInfo.remove(taskId);
             mChildrenLeashes.remove(taskId);
             sendStatusChanged();
-            mCallbacks.onChildTaskStatusChanged(taskId, false /* present */);
+            mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
         } else {
             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
                     + "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -152,7 +154,9 @@
     void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
             @SplitScreen.StageType int stage) {
         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
-            listener.onTaskStageChanged(mChildrenTaskInfo.keyAt(i), stage);
+            int taskId = mChildrenTaskInfo.keyAt(i);
+            listener.onTaskStageChanged(taskId, stage,
+                    mChildrenTaskInfo.get(taskId).isVisible);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 2182ee5..629ff0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -97,8 +97,8 @@
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
 
-            // Don't animate anything with an animating parent
-            if (change.getParent() != null) continue;
+            // Don't animate anything that isn't independent.
+            if (!TransitionInfo.isIndependent(change, info)) continue;
 
             Animation a = loadAnimation(info.getType(), change);
             if (a != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 89eee67..677db10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -214,8 +214,8 @@
             final SurfaceControl leash = change.getLeash();
             final int mode = info.getChanges().get(i).getMode();
 
-            // Don't move anything with an animating parent
-            if (change.getParent() != null) {
+            // Don't move anything that isn't independent within its parents
+            if (!TransitionInfo.isIndependent(change, info)) {
                 if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
                     t.show(leash);
                     t.setMatrix(leash, 1, 0, 0, 1);
@@ -225,9 +225,13 @@
                 continue;
             }
 
-            t.reparent(leash, info.getRootLeash());
-            t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
-                    change.getStartAbsBounds().top - info.getRootOffset().y);
+            boolean hasParent = change.getParent() != null;
+
+            if (!hasParent) {
+                t.reparent(leash, info.getRootLeash());
+                t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
+                        change.getStartAbsBounds().top - info.getRootOffset().y);
+            }
             // Put all the OPEN/SHOW on top
             if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
                 t.show(leash);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 19ecc49..c1c4c6d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -205,7 +205,7 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(),
+        verify(mSplitScreenStarter).startIntent(any(), any(), any(),
                 eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
     }
 
@@ -217,12 +217,12 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(),
+        verify(mSplitScreenStarter).startIntent(any(), any(), any(),
                 eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(),
+        verify(mSplitScreenStarter).startIntent(any(), any(), any(),
                 eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
@@ -234,12 +234,12 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(),
+        verify(mSplitScreenStarter).startIntent(any(), any(), any(),
                 eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(),
+        verify(mSplitScreenStarter).startIntent(any(), any(), any(),
                 eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
@@ -251,7 +251,7 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(),
+        verify(mSplitScreenStarter).startIntent(any(), any(), any(),
                 eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
     }
 
@@ -263,7 +263,7 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(),
+        verify(mSplitScreenStarter).startIntent(any(), any(), any(),
                 eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
     }
 
@@ -276,13 +276,13 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(),
+        verify(mSplitScreenStarter).startIntent(any(), any(), any(),
                 eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(),
+        verify(mSplitScreenStarter).startIntent(any(), any(), any(),
                 eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
@@ -295,13 +295,13 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(),
+        verify(mSplitScreenStarter).startIntent(any(), any(), any(),
                 eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
-        verify(mSplitScreenStarter).startIntent(any(),
+        verify(mSplitScreenStarter).startIntent(any(), any(), any(),
                 eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 79ec624..195b701 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.pip;
 
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -162,12 +164,30 @@
     }
 
     @Test
-    public void onTaskInfoChanged_updatesAspectRatioIfChanged() {
+    public void onTaskInfoChanged_notInPip_deferUpdatesAspectRatio() {
         final Rational startAspectRatio = new Rational(2, 1);
         final Rational newAspectRatio = new Rational(1, 2);
         mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
                 createPipParams(startAspectRatio)), null /* leash */);
 
+        // It is in entering transition, should defer onTaskInfoChanged callback in this case.
+        mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
+                createPipParams(newAspectRatio)));
+        assertEquals(startAspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
+
+        // Once the entering transition finishes, the new aspect ratio applies in a deferred manner
+        mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+        assertEquals(newAspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
+    }
+
+    @Test
+    public void onTaskInfoChanged_inPip_updatesAspectRatioIfChanged() {
+        final Rational startAspectRatio = new Rational(2, 1);
+        final Rational newAspectRatio = new Rational(1, 2);
+        mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+                createPipParams(startAspectRatio)), null /* leash */);
+        mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+
         mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
                 createPipParams(newAspectRatio)));
 
@@ -175,9 +195,10 @@
     }
 
     @Test
-    public void onTaskInfoChanged_updatesLastPipComponentName() {
+    public void onTaskInfoChanged_inPip_updatesLastPipComponentName() {
         mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
                 createPipParams(null)), null /* leash */);
+        mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
 
         mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
                 createPipParams(null)));
@@ -186,9 +207,10 @@
     }
 
     @Test
-    public void onTaskInfoChanged_updatesOverrideMinSize() {
+    public void onTaskInfoChanged_inPip_updatesOverrideMinSize() {
         mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
                 createPipParams(null)), null /* leash */);
+        mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
 
         final Size minSize = new Size(400, 320);
         mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 77ceda9..d663c52 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -421,7 +421,6 @@
                 "libstatspull",
                 "libstatssocket",
                 "libpdfium",
-                "libbinder_ndk",
             ],
             static_libs: [
                 "libgif",
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 912d04c5..e9b2f4a 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -80,6 +80,10 @@
     explicit UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) {
         memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
         set(FrameInfoIndex::FrameTimelineVsyncId) = INVALID_VSYNC_ID;
+        // The struct is zeroed by memset above. That also sets FrameInfoIndex::InputEventId to
+        // equal android::os::IInputConstants::INVALID_INPUT_EVENT_ID == 0.
+        // Therefore, we can skip setting the value for InputEventId here. If the value for
+        // INVALID_INPUT_EVENT_ID changes, this code would have to be updated, as well.
         set(FrameInfoIndex::FrameDeadline) = std::numeric_limits<int64_t>::max();
     }
 
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 609706e..5540e2d 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -552,8 +552,8 @@
 
     bool promotedToLayer() const {
         return mLayerProperties.mType == LayerType::None && fitsOnLayer() &&
-               (mComputedFields.mNeedLayerForFunctors ||
-                mLayerProperties.mImageFilter != nullptr ||
+               (mComputedFields.mNeedLayerForFunctors || mLayerProperties.mImageFilter != nullptr ||
+                !mLayerProperties.getStretchEffect().isEmpty() ||
                 (!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 &&
                  mPrimitiveFields.mHasOverlappingRendering));
     }
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index 51cbc75..d4fd105 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -15,13 +15,195 @@
  */
 
 #include "StretchEffect.h"
+#include <SkImageFilter.h>
+#include <SkRefCnt.h>
+#include <SkRuntimeEffect.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <include/effects/SkImageFilters.h>
+
+#include <memory>
 
 namespace android::uirenderer {
 
-sk_sp<SkImageFilter> StretchEffect::getImageFilter() const {
-    // TODO: Implement & Cache
-    // Probably need to use mutable to achieve caching
-    return nullptr;
+static const SkString stretchShader = SkString(R"(
+    uniform shader uContentTexture;
+
+    // multiplier to apply to scale effect
+    uniform float uMaxStretchIntensity;
+
+    // Maximum percentage to stretch beyond bounds  of target
+    uniform float uStretchAffectedDist;
+
+    // Distance stretched as a function of the normalized overscroll times
+    // scale intensity
+    uniform float uDistanceStretchedX;
+    uniform float uDistanceStretchedY;
+    uniform float uDistDiffX;
+
+    // Difference between the peak stretch amount and overscroll amount normalized
+    uniform float uDistDiffY;
+
+    // Horizontal offset represented as a ratio of pixels divided by the target width
+    uniform float uScrollX;
+    // Vertical offset represented as a ratio of pixels divided by the target height
+    uniform float uScrollY;
+
+    // Normalized overscroll amount in the horizontal direction
+    uniform float uOverscrollX;
+
+    // Normalized overscroll amount in the vertical direction
+    uniform float uOverscrollY;
+    uniform float viewportWidth; // target height in pixels
+    uniform float viewportHeight; // target width in pixels
+
+    void computeOverscrollStart(
+        out float outPos,
+        float inPos,
+        float overscroll,
+        float uStretchAffectedDist,
+        float distanceStretched
+    ) {
+        float offsetPos = uStretchAffectedDist - inPos;
+        float posBasedVariation = smoothstep(0., uStretchAffectedDist, offsetPos);
+        float stretchIntensity = overscroll * posBasedVariation;
+        outPos = distanceStretched - (offsetPos / (1. + stretchIntensity));
+    }
+
+    void computeOverscrollEnd(
+        out float outPos,
+        float inPos,
+        float overscroll,
+        float reverseStretchDist,
+        float uStretchAffectedDist,
+        float distanceStretched
+    ) {
+        float offsetPos = inPos - reverseStretchDist;
+        float posBasedVariation = (smoothstep(0., uStretchAffectedDist, offsetPos));
+        float stretchIntensity = (-overscroll) * posBasedVariation;
+        outPos = 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
+    }
+
+    void computeOverscroll(
+        out float outPos,
+        float inPos,
+        float overscroll,
+        float uStretchAffectedDist,
+        float distanceStretched,
+        float distanceDiff
+    ) {
+        if (overscroll > 0) {
+            if (inPos <= uStretchAffectedDist) {
+                computeOverscrollStart(
+                  outPos,
+                  inPos,
+                  overscroll,
+                  uStretchAffectedDist,
+                  distanceStretched
+                );
+            } else if (inPos >= distanceStretched) {
+                outPos = distanceDiff + inPos;
+            }
+        }
+        if (overscroll < 0) {
+            float stretchAffectedDist = 1. - uStretchAffectedDist;
+            if (inPos >= stretchAffectedDist) {
+                computeOverscrollEnd(
+                  outPos,
+                  inPos,
+                  overscroll,
+                  stretchAffectedDist,
+                  uStretchAffectedDist,
+                  distanceStretched
+                );
+            } else if (inPos < stretchAffectedDist) {
+                outPos = -distanceDiff + inPos;
+            }
+        }
+    }
+
+    vec4 main(vec2 coord) {
+        // Normalize SKSL pixel coordinate into a unit vector
+        float inU = coord.x / viewportWidth;
+        float inV = coord.y / viewportHeight;
+        float outU;
+        float outV;
+        float stretchIntensity;
+        // Add the normalized scroll position within scrolling list
+        inU += uScrollX;
+        inV += uScrollY;
+        outU = inU;
+        outV = inV;
+        computeOverscroll(
+            outU,
+            inU,
+            uOverscrollX,
+            uStretchAffectedDist,
+            uDistanceStretchedX,
+            uDistDiffX
+        );
+        computeOverscroll(
+            outV,
+            inV,
+            uOverscrollY,
+            uStretchAffectedDist,
+            uDistanceStretchedY,
+            uDistDiffY
+        );
+        coord.x = outU * viewportWidth;
+        coord.y = outV * viewportHeight;
+        return sample(uContentTexture, coord);
+    })");
+
+static const float ZERO = 0.f;
+
+sk_sp<SkImageFilter> StretchEffect::getImageFilter(const sk_sp<SkImage>& snapshotImage) const {
+    if (isEmpty()) {
+        return nullptr;
+    }
+
+    if (mStretchFilter != nullptr) {
+        return mStretchFilter;
+    }
+
+    float distanceNotStretchedX = maxStretchAmount / stretchArea.width();
+    float distanceNotStretchedY = maxStretchAmount / stretchArea.height();
+    float normOverScrollDistX = mStretchDirection.x();
+    float normOverScrollDistY = mStretchDirection.y();
+    float distanceStretchedX = maxStretchAmount / (1 + abs(normOverScrollDistX));
+    float distanceStretchedY = maxStretchAmount / (1 + abs(normOverScrollDistY));
+    float diffX = distanceStretchedX - distanceNotStretchedX;
+    float diffY = distanceStretchedY - distanceNotStretchedY;
+    float viewportWidth = stretchArea.width();
+    float viewportHeight = stretchArea.height();
+
+    if (mBuilder == nullptr) {
+        mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
+    }
+
+    mBuilder->child("uContentTexture") = snapshotImage->makeShader(
+            SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear));
+    mBuilder->uniform("uStretchAffectedDist").set(&maxStretchAmount, 1);
+    mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
+    mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
+    mBuilder->uniform("uDistDiffX").set(&diffX, 1);
+    mBuilder->uniform("uDistDiffY").set(&diffY, 1);
+    mBuilder->uniform("uOverscrollX").set(&normOverScrollDistX, 1);
+    mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1);
+    mBuilder->uniform("uScrollX").set(&ZERO, 1);
+    mBuilder->uniform("uScrollY").set(&ZERO, 1);
+    mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
+    mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
+
+    mStretchFilter = SkImageFilters::Shader(mBuilder->makeShader(nullptr, false),
+                                            SkRect{0, 0, viewportWidth, viewportHeight});
+
+    return mStretchFilter;
+}
+
+sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
+    const static SkRuntimeEffect::Result instance = SkRuntimeEffect::Make(stretchShader);
+    return instance.effect;
 }
 
 } // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 7dfd639..d2da06b 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -18,9 +18,11 @@
 
 #include "utils/MathUtils.h"
 
+#include <SkImage.h>
+#include <SkImageFilter.h>
 #include <SkPoint.h>
 #include <SkRect.h>
-#include <SkImageFilter.h>
+#include <SkRuntimeEffect.h>
 
 namespace android::uirenderer {
 
@@ -31,15 +33,27 @@
         SmoothStep,
     };
 
+    StretchEffect(const SkRect& area, const SkVector& direction, float maxStretchAmount)
+            : stretchArea(area), maxStretchAmount(maxStretchAmount), mStretchDirection(direction) {}
+
+    StretchEffect() {}
+
     bool isEmpty() const {
-        return MathUtils::isZero(stretchDirection.x())
-                && MathUtils::isZero(stretchDirection.y());
+        return MathUtils::isZero(mStretchDirection.x()) && MathUtils::isZero(mStretchDirection.y());
     }
 
     void setEmpty() {
         *this = StretchEffect{};
     }
 
+    StretchEffect& operator=(const StretchEffect& other) {
+        this->stretchArea = other.stretchArea;
+        this->mStretchDirection = other.mStretchDirection;
+        this->mStretchFilter = nullptr;
+        this->maxStretchAmount = other.maxStretchAmount;
+        return *this;
+    }
+
     void mergeWith(const StretchEffect& other) {
         if (other.isEmpty()) {
             return;
@@ -48,7 +62,7 @@
             *this = other;
             return;
         }
-        stretchDirection += other.stretchDirection;
+        setStretchDirection(mStretchDirection + other.mStretchDirection);
         if (isEmpty()) {
             return setEmpty();
         }
@@ -56,11 +70,23 @@
         maxStretchAmount = std::max(maxStretchAmount, other.maxStretchAmount);
     }
 
-    sk_sp<SkImageFilter> getImageFilter() const;
+    sk_sp<SkImageFilter> getImageFilter(const sk_sp<SkImage>& snapshotImage) const;
 
     SkRect stretchArea {0, 0, 0, 0};
-    SkVector stretchDirection {0, 0};
     float maxStretchAmount = 0;
+
+    void setStretchDirection(const SkVector& direction) {
+        mStretchFilter = nullptr;
+        mStretchDirection = direction;
+    }
+
+    const SkVector getStretchDirection() const { return mStretchDirection; }
+
+private:
+    static sk_sp<SkRuntimeEffect> getStretchEffect();
+    mutable SkVector mStretchDirection{0, 0};
+    mutable std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+    mutable sk_sp<SkImageFilter> mStretchFilter;
 };
 
 } // namespace android::uirenderer
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 5f60437..fc7d0d1 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -180,14 +180,13 @@
 }
 
 static jboolean android_view_RenderNode_stretch(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
-        jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat vX, jfloat vY, jfloat max) {
+                                                jfloat left, jfloat top, jfloat right,
+                                                jfloat bottom, jfloat vX, jfloat vY, jfloat max) {
+    StretchEffect effect =
+            StretchEffect(SkRect::MakeLTRB(left, top, right, bottom), {.fX = vX, .fY = vY}, max);
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->mutateStagingProperties().mutateLayerProperties().mutableStretchEffect().mergeWith(
-            StretchEffect{
-        .stretchArea = SkRect::MakeLTRB(left, top, right, bottom),
-        .stretchDirection = {.fX = vX, .fY = vY},
-        .maxStretchAmount = max
-    });
+            effect);
     renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
     return true;
 }
@@ -659,10 +658,11 @@
                 return;
             }
 #ifdef __ANDROID__  // Layoutlib does not support CanvasContext
+            SkVector stretchDirection = effect->getStretchDirection();
             env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod,
                                 info.canvasContext.getFrameNumber(), area.left, area.top,
-                                area.right, area.bottom, effect->stretchDirection.fX,
-                                effect->stretchDirection.fY, effect->maxStretchAmount);
+                                area.right, area.bottom, stretchDirection.fX, stretchDirection.fY,
+                                effect->maxStretchAmount);
 #endif
             env->DeleteLocalRef(localref);
         }
@@ -702,106 +702,110 @@
 const char* const kClassPathName = "android/graphics/RenderNode";
 
 static const JNINativeMethod gMethods[] = {
-// ----------------------------------------------------------------------------
-// Regular JNI
-// ----------------------------------------------------------------------------
-    { "nCreate",               "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
-    { "nGetNativeFinalizer",   "()J",    (void*) android_view_RenderNode_getNativeFinalizer },
-    { "nOutput",               "(J)V",    (void*) android_view_RenderNode_output },
-    { "nGetUsageSize",         "(J)I",    (void*) android_view_RenderNode_getUsageSize },
-    { "nGetAllocatedSize",         "(J)I",    (void*) android_view_RenderNode_getAllocatedSize },
-    { "nAddAnimator",              "(JJ)V", (void*) android_view_RenderNode_addAnimator },
-    { "nEndAllAnimators",          "(J)V", (void*) android_view_RenderNode_endAllAnimators },
-    { "nRequestPositionUpdates",   "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates },
+        // ----------------------------------------------------------------------------
+        // Regular JNI
+        // ----------------------------------------------------------------------------
+        {"nCreate", "(Ljava/lang/String;)J", (void*)android_view_RenderNode_create},
+        {"nGetNativeFinalizer", "()J", (void*)android_view_RenderNode_getNativeFinalizer},
+        {"nOutput", "(J)V", (void*)android_view_RenderNode_output},
+        {"nGetUsageSize", "(J)I", (void*)android_view_RenderNode_getUsageSize},
+        {"nGetAllocatedSize", "(J)I", (void*)android_view_RenderNode_getAllocatedSize},
+        {"nAddAnimator", "(JJ)V", (void*)android_view_RenderNode_addAnimator},
+        {"nEndAllAnimators", "(J)V", (void*)android_view_RenderNode_endAllAnimators},
+        {"nRequestPositionUpdates", "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V",
+         (void*)android_view_RenderNode_requestPositionUpdates},
 
-// ----------------------------------------------------------------------------
-// Critical JNI via @CriticalNative annotation in RenderNode.java
-// ----------------------------------------------------------------------------
-    { "nDiscardDisplayList",   "(J)V",   (void*) android_view_RenderNode_discardDisplayList },
-    { "nIsValid",              "(J)Z",   (void*) android_view_RenderNode_isValid },
-    { "nSetLayerType",         "(JI)Z",  (void*) android_view_RenderNode_setLayerType },
-    { "nGetLayerType",         "(J)I",   (void*) android_view_RenderNode_getLayerType },
-    { "nSetLayerPaint",        "(JJ)Z",  (void*) android_view_RenderNode_setLayerPaint },
-    { "nSetStaticMatrix",      "(JJ)Z",  (void*) android_view_RenderNode_setStaticMatrix },
-    { "nSetAnimationMatrix",   "(JJ)Z",  (void*) android_view_RenderNode_setAnimationMatrix },
-    { "nGetAnimationMatrix",   "(JJ)Z",  (void*) android_view_RenderNode_getAnimationMatrix },
-    { "nSetClipToBounds",      "(JZ)Z",  (void*) android_view_RenderNode_setClipToBounds },
-    { "nGetClipToBounds",      "(J)Z",   (void*) android_view_RenderNode_getClipToBounds },
-    { "nSetClipBounds",        "(JIIII)Z", (void*) android_view_RenderNode_setClipBounds },
-    { "nSetClipBoundsEmpty",   "(J)Z",   (void*) android_view_RenderNode_setClipBoundsEmpty },
-    { "nSetProjectBackwards",  "(JZ)Z",  (void*) android_view_RenderNode_setProjectBackwards },
-    { "nSetProjectionReceiver","(JZ)Z",  (void*) android_view_RenderNode_setProjectionReceiver },
+        // ----------------------------------------------------------------------------
+        // Critical JNI via @CriticalNative annotation in RenderNode.java
+        // ----------------------------------------------------------------------------
+        {"nDiscardDisplayList", "(J)V", (void*)android_view_RenderNode_discardDisplayList},
+        {"nIsValid", "(J)Z", (void*)android_view_RenderNode_isValid},
+        {"nSetLayerType", "(JI)Z", (void*)android_view_RenderNode_setLayerType},
+        {"nGetLayerType", "(J)I", (void*)android_view_RenderNode_getLayerType},
+        {"nSetLayerPaint", "(JJ)Z", (void*)android_view_RenderNode_setLayerPaint},
+        {"nSetStaticMatrix", "(JJ)Z", (void*)android_view_RenderNode_setStaticMatrix},
+        {"nSetAnimationMatrix", "(JJ)Z", (void*)android_view_RenderNode_setAnimationMatrix},
+        {"nGetAnimationMatrix", "(JJ)Z", (void*)android_view_RenderNode_getAnimationMatrix},
+        {"nSetClipToBounds", "(JZ)Z", (void*)android_view_RenderNode_setClipToBounds},
+        {"nGetClipToBounds", "(J)Z", (void*)android_view_RenderNode_getClipToBounds},
+        {"nSetClipBounds", "(JIIII)Z", (void*)android_view_RenderNode_setClipBounds},
+        {"nSetClipBoundsEmpty", "(J)Z", (void*)android_view_RenderNode_setClipBoundsEmpty},
+        {"nSetProjectBackwards", "(JZ)Z", (void*)android_view_RenderNode_setProjectBackwards},
+        {"nSetProjectionReceiver", "(JZ)Z", (void*)android_view_RenderNode_setProjectionReceiver},
 
-    { "nSetOutlineRoundRect",  "(JIIIIFF)Z", (void*) android_view_RenderNode_setOutlineRoundRect },
-    { "nSetOutlinePath",       "(JJF)Z", (void*) android_view_RenderNode_setOutlinePath },
-    { "nSetOutlineEmpty",      "(J)Z",   (void*) android_view_RenderNode_setOutlineEmpty },
-    { "nSetOutlineNone",       "(J)Z",   (void*) android_view_RenderNode_setOutlineNone },
-    { "nClearStretch",         "(J)Z",   (void*) android_view_RenderNode_clearStretch },
-    { "nStretch",              "(JFFFFFFF)Z",   (void*) android_view_RenderNode_stretch },
-    { "nHasShadow",            "(J)Z",   (void*) android_view_RenderNode_hasShadow },
-    { "nSetSpotShadowColor",   "(JI)Z",  (void*) android_view_RenderNode_setSpotShadowColor },
-    { "nGetSpotShadowColor",   "(J)I",   (void*) android_view_RenderNode_getSpotShadowColor },
-    { "nSetAmbientShadowColor","(JI)Z",  (void*) android_view_RenderNode_setAmbientShadowColor },
-    { "nGetAmbientShadowColor","(J)I",   (void*) android_view_RenderNode_getAmbientShadowColor },
-    { "nSetClipToOutline",     "(JZ)Z",  (void*) android_view_RenderNode_setClipToOutline },
-    { "nSetRevealClip",        "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
+        {"nSetOutlineRoundRect", "(JIIIIFF)Z", (void*)android_view_RenderNode_setOutlineRoundRect},
+        {"nSetOutlinePath", "(JJF)Z", (void*)android_view_RenderNode_setOutlinePath},
+        {"nSetOutlineEmpty", "(J)Z", (void*)android_view_RenderNode_setOutlineEmpty},
+        {"nSetOutlineNone", "(J)Z", (void*)android_view_RenderNode_setOutlineNone},
+        {"nClearStretch", "(J)Z", (void*)android_view_RenderNode_clearStretch},
+        {"nStretch", "(JFFFFFFF)Z", (void*)android_view_RenderNode_stretch},
+        {"nHasShadow", "(J)Z", (void*)android_view_RenderNode_hasShadow},
+        {"nSetSpotShadowColor", "(JI)Z", (void*)android_view_RenderNode_setSpotShadowColor},
+        {"nGetSpotShadowColor", "(J)I", (void*)android_view_RenderNode_getSpotShadowColor},
+        {"nSetAmbientShadowColor", "(JI)Z", (void*)android_view_RenderNode_setAmbientShadowColor},
+        {"nGetAmbientShadowColor", "(J)I", (void*)android_view_RenderNode_getAmbientShadowColor},
+        {"nSetClipToOutline", "(JZ)Z", (void*)android_view_RenderNode_setClipToOutline},
+        {"nSetRevealClip", "(JZFFF)Z", (void*)android_view_RenderNode_setRevealClip},
 
-    { "nSetAlpha",             "(JF)Z",  (void*) android_view_RenderNode_setAlpha },
-    { "nSetRenderEffect",      "(JJ)Z",  (void*) android_view_RenderNode_setRenderEffect },
-    { "nSetHasOverlappingRendering", "(JZ)Z",
-            (void*) android_view_RenderNode_setHasOverlappingRendering },
-    { "nSetUsageHint",    "(JI)V", (void*) android_view_RenderNode_setUsageHint },
-    { "nSetElevation",         "(JF)Z",  (void*) android_view_RenderNode_setElevation },
-    { "nSetTranslationX",      "(JF)Z",  (void*) android_view_RenderNode_setTranslationX },
-    { "nSetTranslationY",      "(JF)Z",  (void*) android_view_RenderNode_setTranslationY },
-    { "nSetTranslationZ",      "(JF)Z",  (void*) android_view_RenderNode_setTranslationZ },
-    { "nSetRotation",          "(JF)Z",  (void*) android_view_RenderNode_setRotation },
-    { "nSetRotationX",         "(JF)Z",  (void*) android_view_RenderNode_setRotationX },
-    { "nSetRotationY",         "(JF)Z",  (void*) android_view_RenderNode_setRotationY },
-    { "nSetScaleX",            "(JF)Z",  (void*) android_view_RenderNode_setScaleX },
-    { "nSetScaleY",            "(JF)Z",  (void*) android_view_RenderNode_setScaleY },
-    { "nSetPivotX",            "(JF)Z",  (void*) android_view_RenderNode_setPivotX },
-    { "nSetPivotY",            "(JF)Z",  (void*) android_view_RenderNode_setPivotY },
-    { "nResetPivot",           "(J)Z",   (void*) android_view_RenderNode_resetPivot },
-    { "nSetCameraDistance",    "(JF)Z",  (void*) android_view_RenderNode_setCameraDistance },
-    { "nSetLeft",              "(JI)Z",  (void*) android_view_RenderNode_setLeft },
-    { "nSetTop",               "(JI)Z",  (void*) android_view_RenderNode_setTop },
-    { "nSetRight",             "(JI)Z",  (void*) android_view_RenderNode_setRight },
-    { "nSetBottom",            "(JI)Z",  (void*) android_view_RenderNode_setBottom },
-    { "nGetLeft",              "(J)I",  (void*) android_view_RenderNode_getLeft },
-    { "nGetTop",               "(J)I",  (void*) android_view_RenderNode_getTop },
-    { "nGetRight",             "(J)I",  (void*) android_view_RenderNode_getRight },
-    { "nGetBottom",            "(J)I",  (void*) android_view_RenderNode_getBottom },
-    { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom },
-    { "nOffsetLeftAndRight",   "(JI)Z",  (void*) android_view_RenderNode_offsetLeftAndRight },
-    { "nOffsetTopAndBottom",   "(JI)Z",  (void*) android_view_RenderNode_offsetTopAndBottom },
+        {"nSetAlpha", "(JF)Z", (void*)android_view_RenderNode_setAlpha},
+        {"nSetRenderEffect", "(JJ)Z", (void*)android_view_RenderNode_setRenderEffect},
+        {"nSetHasOverlappingRendering", "(JZ)Z",
+         (void*)android_view_RenderNode_setHasOverlappingRendering},
+        {"nSetUsageHint", "(JI)V", (void*)android_view_RenderNode_setUsageHint},
+        {"nSetElevation", "(JF)Z", (void*)android_view_RenderNode_setElevation},
+        {"nSetTranslationX", "(JF)Z", (void*)android_view_RenderNode_setTranslationX},
+        {"nSetTranslationY", "(JF)Z", (void*)android_view_RenderNode_setTranslationY},
+        {"nSetTranslationZ", "(JF)Z", (void*)android_view_RenderNode_setTranslationZ},
+        {"nSetRotation", "(JF)Z", (void*)android_view_RenderNode_setRotation},
+        {"nSetRotationX", "(JF)Z", (void*)android_view_RenderNode_setRotationX},
+        {"nSetRotationY", "(JF)Z", (void*)android_view_RenderNode_setRotationY},
+        {"nSetScaleX", "(JF)Z", (void*)android_view_RenderNode_setScaleX},
+        {"nSetScaleY", "(JF)Z", (void*)android_view_RenderNode_setScaleY},
+        {"nSetPivotX", "(JF)Z", (void*)android_view_RenderNode_setPivotX},
+        {"nSetPivotY", "(JF)Z", (void*)android_view_RenderNode_setPivotY},
+        {"nResetPivot", "(J)Z", (void*)android_view_RenderNode_resetPivot},
+        {"nSetCameraDistance", "(JF)Z", (void*)android_view_RenderNode_setCameraDistance},
+        {"nSetLeft", "(JI)Z", (void*)android_view_RenderNode_setLeft},
+        {"nSetTop", "(JI)Z", (void*)android_view_RenderNode_setTop},
+        {"nSetRight", "(JI)Z", (void*)android_view_RenderNode_setRight},
+        {"nSetBottom", "(JI)Z", (void*)android_view_RenderNode_setBottom},
+        {"nGetLeft", "(J)I", (void*)android_view_RenderNode_getLeft},
+        {"nGetTop", "(J)I", (void*)android_view_RenderNode_getTop},
+        {"nGetRight", "(J)I", (void*)android_view_RenderNode_getRight},
+        {"nGetBottom", "(J)I", (void*)android_view_RenderNode_getBottom},
+        {"nSetLeftTopRightBottom", "(JIIII)Z",
+         (void*)android_view_RenderNode_setLeftTopRightBottom},
+        {"nOffsetLeftAndRight", "(JI)Z", (void*)android_view_RenderNode_offsetLeftAndRight},
+        {"nOffsetTopAndBottom", "(JI)Z", (void*)android_view_RenderNode_offsetTopAndBottom},
 
-    { "nHasOverlappingRendering", "(J)Z",  (void*) android_view_RenderNode_hasOverlappingRendering },
-    { "nGetClipToOutline",        "(J)Z",  (void*) android_view_RenderNode_getClipToOutline },
-    { "nGetAlpha",                "(J)F",  (void*) android_view_RenderNode_getAlpha },
-    { "nGetCameraDistance",       "(J)F",  (void*) android_view_RenderNode_getCameraDistance },
-    { "nGetScaleX",               "(J)F",  (void*) android_view_RenderNode_getScaleX },
-    { "nGetScaleY",               "(J)F",  (void*) android_view_RenderNode_getScaleY },
-    { "nGetElevation",            "(J)F",  (void*) android_view_RenderNode_getElevation },
-    { "nGetTranslationX",         "(J)F",  (void*) android_view_RenderNode_getTranslationX },
-    { "nGetTranslationY",         "(J)F",  (void*) android_view_RenderNode_getTranslationY },
-    { "nGetTranslationZ",         "(J)F",  (void*) android_view_RenderNode_getTranslationZ },
-    { "nGetRotation",             "(J)F",  (void*) android_view_RenderNode_getRotation },
-    { "nGetRotationX",            "(J)F",  (void*) android_view_RenderNode_getRotationX },
-    { "nGetRotationY",            "(J)F",  (void*) android_view_RenderNode_getRotationY },
-    { "nIsPivotExplicitlySet",    "(J)Z",  (void*) android_view_RenderNode_isPivotExplicitlySet },
-    { "nHasIdentityMatrix",       "(J)Z",  (void*) android_view_RenderNode_hasIdentityMatrix },
+        {"nHasOverlappingRendering", "(J)Z",
+         (void*)android_view_RenderNode_hasOverlappingRendering},
+        {"nGetClipToOutline", "(J)Z", (void*)android_view_RenderNode_getClipToOutline},
+        {"nGetAlpha", "(J)F", (void*)android_view_RenderNode_getAlpha},
+        {"nGetCameraDistance", "(J)F", (void*)android_view_RenderNode_getCameraDistance},
+        {"nGetScaleX", "(J)F", (void*)android_view_RenderNode_getScaleX},
+        {"nGetScaleY", "(J)F", (void*)android_view_RenderNode_getScaleY},
+        {"nGetElevation", "(J)F", (void*)android_view_RenderNode_getElevation},
+        {"nGetTranslationX", "(J)F", (void*)android_view_RenderNode_getTranslationX},
+        {"nGetTranslationY", "(J)F", (void*)android_view_RenderNode_getTranslationY},
+        {"nGetTranslationZ", "(J)F", (void*)android_view_RenderNode_getTranslationZ},
+        {"nGetRotation", "(J)F", (void*)android_view_RenderNode_getRotation},
+        {"nGetRotationX", "(J)F", (void*)android_view_RenderNode_getRotationX},
+        {"nGetRotationY", "(J)F", (void*)android_view_RenderNode_getRotationY},
+        {"nIsPivotExplicitlySet", "(J)Z", (void*)android_view_RenderNode_isPivotExplicitlySet},
+        {"nHasIdentityMatrix", "(J)Z", (void*)android_view_RenderNode_hasIdentityMatrix},
 
-    { "nGetTransformMatrix",       "(JJ)V", (void*) android_view_RenderNode_getTransformMatrix },
-    { "nGetInverseTransformMatrix","(JJ)V", (void*) android_view_RenderNode_getInverseTransformMatrix },
+        {"nGetTransformMatrix", "(JJ)V", (void*)android_view_RenderNode_getTransformMatrix},
+        {"nGetInverseTransformMatrix", "(JJ)V",
+         (void*)android_view_RenderNode_getInverseTransformMatrix},
 
-    { "nGetPivotX",                "(J)F",  (void*) android_view_RenderNode_getPivotX },
-    { "nGetPivotY",                "(J)F",  (void*) android_view_RenderNode_getPivotY },
-    { "nGetWidth",                 "(J)I",  (void*) android_view_RenderNode_getWidth },
-    { "nGetHeight",                "(J)I",  (void*) android_view_RenderNode_getHeight },
-    { "nSetAllowForceDark",        "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark },
-    { "nGetAllowForceDark",        "(J)Z",  (void*) android_view_RenderNode_getAllowForceDark },
-    { "nGetUniqueId",              "(J)J",  (void*) android_view_RenderNode_getUniqueId },
+        {"nGetPivotX", "(J)F", (void*)android_view_RenderNode_getPivotX},
+        {"nGetPivotY", "(J)F", (void*)android_view_RenderNode_getPivotY},
+        {"nGetWidth", "(J)I", (void*)android_view_RenderNode_getWidth},
+        {"nGetHeight", "(J)I", (void*)android_view_RenderNode_getHeight},
+        {"nSetAllowForceDark", "(JZ)Z", (void*)android_view_RenderNode_setAllowForceDark},
+        {"nGetAllowForceDark", "(J)Z", (void*)android_view_RenderNode_getAllowForceDark},
+        {"nGetUniqueId", "(J)J", (void*)android_view_RenderNode_getUniqueId},
 };
 
 int register_android_view_RenderNode(JNIEnv* env) {
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index c010212..cb0ff8d 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -169,8 +169,8 @@
     displayList->mProjectedOutline = nullptr;
 }
 
-static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier,
-                            SkPaint* paint) {
+static bool layerNeedsPaint(const sk_sp<SkImage>& snapshotImage, const LayerProperties& properties,
+                            float alphaMultiplier, SkPaint* paint) {
     if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
         properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
         properties.getImageFilter() != nullptr || !properties.getStretchEffect().isEmpty()) {
@@ -179,7 +179,8 @@
         paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
 
         sk_sp<SkImageFilter> imageFilter = sk_ref_sp(properties.getImageFilter());
-        sk_sp<SkImageFilter> stretchFilter = properties.getStretchEffect().getImageFilter();
+        sk_sp<SkImageFilter> stretchFilter =
+                properties.getStretchEffect().getImageFilter(snapshotImage);
         sk_sp<SkImageFilter> filter;
         if (imageFilter && stretchFilter) {
             filter = SkImageFilters::Compose(
@@ -240,7 +241,8 @@
         if (renderNode->getLayerSurface() && mComposeLayer) {
             SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
             SkPaint paint;
-            layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
+            sk_sp<SkImage> snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot();
+            layerNeedsPaint(snapshotImage, layerProperties, alphaMultiplier, &paint);
             SkSamplingOptions sampling(SkFilterMode::kLinear);
 
             // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
@@ -254,8 +256,8 @@
                 canvas->drawAnnotation(bounds, String8::format(
                     "SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr);
             }
-            canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot(), bounds,
-                                  bounds, sampling, &paint, SkCanvas::kStrict_SrcRectConstraint);
+            canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
+                                  SkCanvas::kStrict_SrcRectConstraint);
 
             if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
                 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index c9146b2..3408ffd 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -17,7 +17,7 @@
 #include "DrawFrameTask.h"
 
 #include <utils/Log.h>
-#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
 
 #include "../DeferredLayerUpdater.h"
 #include "../DisplayList.h"
@@ -82,7 +82,8 @@
 }
 
 void DrawFrameTask::run() {
-    ATRACE_NAME("DrawFrame");
+    const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
+    ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
 
     bool canUnblockUiThread;
     bool canDrawThisFrame;
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index f5b204a..5656dff 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.graphics.GraphicBuffer;
@@ -24,6 +25,7 @@
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 import android.hardware.HardwareBuffer.Usage;
+import android.hardware.camera2.MultiResolutionImageReader;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -36,7 +38,9 @@
 import java.nio.ByteOrder;
 import java.nio.NioUtils;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -134,7 +138,8 @@
         // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
         // work, and is inscrutable anyway
         return new ImageReader(width, height, format, maxImages,
-                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN);
+                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN,
+                /*parent*/ null);
     }
 
     /**
@@ -250,18 +255,37 @@
 //            throw new IllegalArgumentException("The given format=" + Integer.toHexString(format)
 //                + " & usage=" + Long.toHexString(usage) + " is not supported");
 //        }
-        return new ImageReader(width, height, format, maxImages, usage);
+        return new ImageReader(width, height, format, maxImages, usage, /*parent*/ null);
     }
 
+     /**
+      * @hide
+      */
+     public static @NonNull ImageReader newInstance(
+            @IntRange(from = 1) int width,
+            @IntRange(from = 1) int height,
+            @Format             int format,
+            @IntRange(from = 1) int maxImages,
+            @NonNull            MultiResolutionImageReader parent) {
+        // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
+        // work, and is inscrutable anyway
+        return new ImageReader(width, height, format, maxImages,
+                format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN,
+                parent);
+    }
+
+
     /**
      * @hide
      */
-    protected ImageReader(int width, int height, int format, int maxImages, long usage) {
+    protected ImageReader(int width, int height, int format, int maxImages, long usage,
+            MultiResolutionImageReader parent) {
         mWidth = width;
         mHeight = height;
         mFormat = format;
         mUsage = usage;
         mMaxImages = maxImages;
+        mParent = parent;
 
         if (width < 1 || height < 1) {
             throw new IllegalArgumentException(
@@ -433,6 +457,9 @@
             if (image != null) {
                 image.close();
             }
+            if (mParent != null) {
+                mParent.flushOther(this);
+            }
         }
     }
 
@@ -584,12 +611,38 @@
                 }
                 if (mListenerHandler == null || mListenerHandler.getLooper() != looper) {
                     mListenerHandler = new ListenerHandler(looper);
+                    mListenerExecutor = new HandlerExecutor(mListenerHandler);
                 }
-                mListener = listener;
             } else {
-                mListener = null;
                 mListenerHandler = null;
+                mListenerExecutor = null;
             }
+            mListener = listener;
+        }
+    }
+
+    /**
+     * Register a listener to be invoked when a new image becomes available
+     * from the ImageReader.
+     *
+     * @param listener
+     *            The listener that will be run.
+     * @param executor
+     *            The executor which will be used to invoke the listener.
+     * @throws IllegalArgumentException
+     *            If no handler specified and the calling thread has no looper.
+     *
+     * @hide
+     */
+    public void setOnImageAvailableListenerWithExecutor(@NonNull OnImageAvailableListener listener,
+            @NonNull Executor executor) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        }
+
+        synchronized (mListenerLock) {
+            mListenerExecutor = executor;
+            mListener = listener;
         }
     }
 
@@ -763,12 +816,27 @@
             return;
         }
 
-        final Handler handler;
+        final Executor executor;
+        final OnImageAvailableListener listener;
         synchronized (ir.mListenerLock) {
-            handler = ir.mListenerHandler;
+            executor = ir.mListenerExecutor;
+            listener = ir.mListener;
         }
-        if (handler != null) {
-            handler.sendEmptyMessage(0);
+        final boolean isReaderValid;
+        synchronized (ir.mCloseLock) {
+            isReaderValid = ir.mIsReaderValid;
+        }
+
+        // It's dangerous to fire onImageAvailable() callback when the ImageReader
+        // is being closed, as application could acquire next image in the
+        // onImageAvailable() callback.
+        if (executor != null && listener != null && isReaderValid) {
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    listener.onImageAvailable(ir);
+                }
+            });
         }
     }
 
@@ -785,11 +853,16 @@
     private final Object mCloseLock = new Object();
     private boolean mIsReaderValid = false;
     private OnImageAvailableListener mListener;
+    private Executor mListenerExecutor;
     private ListenerHandler mListenerHandler;
     // Keep track of the successfully acquired Images. This need to be thread safe as the images
     // could be closed by different threads (e.g., application thread and GC thread).
     private List<Image> mAcquiredImages = new CopyOnWriteArrayList<>();
 
+    // Applicable if this isn't a standalone ImageReader, but belongs to a
+    // MultiResolutionImageReader.
+    private final MultiResolutionImageReader mParent;
+
     /**
      * This field is used by native code, do not access or modify.
      */
@@ -802,23 +875,22 @@
         public ListenerHandler(Looper looper) {
             super(looper, null, true /*async*/);
         }
+    }
+
+    /**
+     * An adapter {@link Executor} that posts all executed tasks onto the
+     * given {@link Handler}.
+     **/
+    private final class HandlerExecutor implements Executor {
+        private final Handler mHandler;
+
+        public HandlerExecutor(@NonNull Handler handler) {
+            mHandler = Objects.requireNonNull(handler);
+        }
 
         @Override
-        public void handleMessage(Message msg) {
-            OnImageAvailableListener listener;
-            synchronized (mListenerLock) {
-                listener = mListener;
-            }
-
-            // It's dangerous to fire onImageAvailable() callback when the ImageReader is being
-            // closed, as application could acquire next image in the onImageAvailable() callback.
-            boolean isReaderValid = false;
-            synchronized (mCloseLock) {
-                isReaderValid = mIsReaderValid;
-            }
-            if (listener != null && isReaderValid) {
-                listener.onImageAvailable(ImageReader.this);
-            }
+        public void execute(Runnable command) {
+            mHandler.post(command);
         }
     }
 
diff --git a/media/java/android/media/metrics/Event.java b/media/java/android/media/metrics/Event.java
index 5646dcd..96b61d2 100644
--- a/media/java/android/media/metrics/Event.java
+++ b/media/java/android/media/metrics/Event.java
@@ -17,22 +17,30 @@
 package android.media.metrics;
 
 import android.annotation.IntRange;
+import android.os.Bundle;
 
 /**
  * Abstract class for metrics events.
  */
 public abstract class Event {
-    private final long mTimeSinceCreatedMillis;
+    final long mTimeSinceCreatedMillis;
+    Bundle mExtras;
 
     // hide default constructor
     /* package */ Event() {
         mTimeSinceCreatedMillis = MediaMetricsManager.INVALID_TIMESTAMP;
     }
 
+    // TODO: remove
     protected Event(long timeSinceCreatedMillis) {
         mTimeSinceCreatedMillis = timeSinceCreatedMillis;
     }
 
+    /* package */ Event(long timeSinceCreatedMillis, Bundle extras) {
+        mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+        mExtras = extras;
+    }
+
     /**
      * Gets time since the corresponding instance is created in millisecond.
      * @return the timestamp since the instance is created, or -1 if unknown.
@@ -41,4 +49,9 @@
     public long getTimeSinceCreatedMillis() {
         return mTimeSinceCreatedMillis;
     }
+
+    /** @hide */
+    public Bundle getExtras() {
+        return mExtras;
+    }
 }
diff --git a/media/java/android/media/metrics/IMediaMetricsManager.aidl b/media/java/android/media/metrics/IMediaMetricsManager.aidl
index 2cb2ab5..f2c0d44 100644
--- a/media/java/android/media/metrics/IMediaMetricsManager.aidl
+++ b/media/java/android/media/metrics/IMediaMetricsManager.aidl
@@ -28,7 +28,8 @@
  */
 interface IMediaMetricsManager {
     void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId);
-    String getSessionId(int userId);
+    String getPlaybackSessionId(int userId);
+    String getRecordingSessionId(int userId);
     void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId);
     void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId);
     void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId);
diff --git a/media/java/android/media/metrics/LogSessionId.java b/media/java/android/media/metrics/LogSessionId.java
new file mode 100644
index 0000000..7ddb259
--- /dev/null
+++ b/media/java/android/media/metrics/LogSessionId.java
@@ -0,0 +1,34 @@
+/*
+ * 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.media.metrics;
+
+/**
+ * An instances of this class represents the ID of a log session.
+ * @hide
+ */
+public class LogSessionId {
+    private final String mSessionId;
+
+    /* package */ LogSessionId(String id) {
+        mSessionId = id;
+    }
+
+    /** @hide */
+    public String getStringId() {
+        return mSessionId;
+    }
+}
diff --git a/media/java/android/media/metrics/MediaMetricsManager.java b/media/java/android/media/metrics/MediaMetricsManager.java
index de780f6..9710e88 100644
--- a/media/java/android/media/metrics/MediaMetricsManager.java
+++ b/media/java/android/media/metrics/MediaMetricsManager.java
@@ -94,7 +94,7 @@
     @NonNull
     public PlaybackSession createPlaybackSession() {
         try {
-            String id = mService.getSessionId(mUserId);
+            String id = mService.getPlaybackSessionId(mUserId);
             PlaybackSession session = new PlaybackSession(id, this);
             return session;
         } catch (RemoteException e) {
@@ -103,6 +103,21 @@
     }
 
     /**
+     * Creates a recording session.
+     * @hide
+     */
+    @NonNull
+    public RecordingSession createRecordingSession() {
+        try {
+            String id = mService.getRecordingSessionId(mUserId);
+            RecordingSession session = new RecordingSession(id, this);
+            return session;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Reports error event.
      * @hide
      */
diff --git a/media/java/android/media/metrics/NetworkEvent.java b/media/java/android/media/metrics/NetworkEvent.java
index 029edeb..098885c 100644
--- a/media/java/android/media/metrics/NetworkEvent.java
+++ b/media/java/android/media/metrics/NetworkEvent.java
@@ -20,6 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -33,6 +34,9 @@
 public final class NetworkEvent extends Event implements Parcelable {
     /** Network type is not specified. Default type. */
     public static final int NETWORK_TYPE_NONE = 0;
+    // TODO: replace NONE with UNKNOWN
+    /** @hide */
+    public static final int NETWORK_TYPE_UNKNOWN = 0;
     /** Other network type */
     public static final int NETWORK_TYPE_OTHER = 1;
     /** Wi-Fi network */
@@ -49,6 +53,9 @@
     public static final int NETWORK_TYPE_5G_NSA = 7;
     /** 5G SA network */
     public static final int NETWORK_TYPE_5G_SA = 8;
+    /** Not network connected */
+    /** @hide */
+    public static final int NETWORK_TYPE_OFFLINE = 9;
 
     private final int mNetworkType;
     private final long mTimeSinceCreatedMillis;
@@ -56,6 +63,7 @@
     /** @hide */
     @IntDef(prefix = "NETWORK_TYPE_", value = {
         NETWORK_TYPE_NONE,
+        NETWORK_TYPE_UNKNOWN,
         NETWORK_TYPE_OTHER,
         NETWORK_TYPE_WIFI,
         NETWORK_TYPE_ETHERNET,
@@ -63,7 +71,8 @@
         NETWORK_TYPE_3G,
         NETWORK_TYPE_4G,
         NETWORK_TYPE_5G_NSA,
-        NETWORK_TYPE_5G_SA
+        NETWORK_TYPE_5G_SA,
+        NETWORK_TYPE_OFFLINE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface NetworkType {}
@@ -92,6 +101,8 @@
                 return "NETWORK_TYPE_5G_NSA";
             case NETWORK_TYPE_5G_SA:
                 return "NETWORK_TYPE_5G_SA";
+            case NETWORK_TYPE_OFFLINE:
+                return "NETWORK_TYPE_OFFLINE";
             default:
                 return Integer.toHexString(value);
         }
@@ -102,9 +113,10 @@
      *
      * @hide
      */
-    public NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis) {
+    public NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis, Bundle extras) {
         this.mNetworkType = type;
         this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+        this.mExtras = extras.deepCopy();
     }
 
     /**
@@ -149,8 +161,12 @@
 
     @Override
     public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        byte flg = 0;
+        if (mExtras != null) flg |= 0x1;
+        dest.writeByte(flg);
         dest.writeInt(mNetworkType);
         dest.writeLong(mTimeSinceCreatedMillis);
+        if (mExtras != null) dest.writeBundle(mExtras);
     }
 
     @Override
@@ -160,11 +176,14 @@
 
     /** @hide */
     /* package-private */ NetworkEvent(@NonNull android.os.Parcel in) {
+        byte flg = in.readByte();
         int type = in.readInt();
         long timeSinceCreatedMillis = in.readLong();
+        Bundle extras = (flg & 0x2) == 0 ? null : in.readBundle();
 
         this.mNetworkType = type;
         this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+        this.mExtras = extras;
     }
 
     /**
@@ -189,6 +208,7 @@
     public static final class Builder {
         private int mNetworkType = NETWORK_TYPE_NONE;
         private long mTimeSinceCreatedMillis = -1;
+        private Bundle mExtras;
 
         /**
          * Creates a new Builder.
@@ -214,9 +234,19 @@
             return this;
         }
 
+        /**
+         * Set extras for compatibility.
+         * <p>Should be used by support library only.
+         * @hide
+         */
+        public @NonNull Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
         /** Builds the instance. */
         public @NonNull NetworkEvent build() {
-            NetworkEvent o = new NetworkEvent(mNetworkType, mTimeSinceCreatedMillis);
+            NetworkEvent o = new NetworkEvent(mNetworkType, mTimeSinceCreatedMillis, mExtras);
             return o;
         }
     }
diff --git a/media/java/android/media/metrics/PlaybackErrorEvent.java b/media/java/android/media/metrics/PlaybackErrorEvent.java
index 5a0820d1..b23b4d2 100644
--- a/media/java/android/media/metrics/PlaybackErrorEvent.java
+++ b/media/java/android/media/metrics/PlaybackErrorEvent.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -63,11 +64,13 @@
             @Nullable String exceptionStack,
             int errorCode,
             int subErrorCode,
-            long timeSinceCreatedMillis) {
+            long timeSinceCreatedMillis,
+            Bundle extras) {
         this.mExceptionStack = exceptionStack;
         this.mErrorCode = errorCode;
         this.mSubErrorCode = subErrorCode;
         this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+        this.mExtras = extras.deepCopy();
     }
 
     /** @hide */
@@ -135,11 +138,13 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         byte flg = 0;
         if (mExceptionStack != null) flg |= 0x1;
+        if (mExtras != null) flg |= 0x2;
         dest.writeByte(flg);
         if (mExceptionStack != null) dest.writeString(mExceptionStack);
         dest.writeInt(mErrorCode);
         dest.writeInt(mSubErrorCode);
         dest.writeLong(mTimeSinceCreatedMillis);
+        if (mExtras != null) dest.writeBundle(mExtras);
     }
 
     @Override
@@ -154,11 +159,13 @@
         int errorCode = in.readInt();
         int subErrorCode = in.readInt();
         long timeSinceCreatedMillis = in.readLong();
+        Bundle extras = (flg & 0x2) == 0 ? null : in.readBundle();
 
         this.mExceptionStack = exceptionStack;
         this.mErrorCode = errorCode;
         this.mSubErrorCode = subErrorCode;
         this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+        this.mExtras = extras;
     }
 
 
@@ -183,6 +190,7 @@
         private int mErrorCode;
         private int mSubErrorCode;
         private long mTimeSinceCreatedMillis = -1;
+        private Bundle mExtras;
 
         /**
          * Creates a new Builder.
@@ -226,6 +234,16 @@
             return this;
         }
 
+        /**
+         * Set extras for compatibility.
+         * <p>Should be used by support library only.
+         * @hide
+         */
+        public @NonNull Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
         /** Builds the instance. */
         public @NonNull PlaybackErrorEvent build() {
 
@@ -241,7 +259,8 @@
                     stack,
                     mErrorCode,
                     mSubErrorCode,
-                    mTimeSinceCreatedMillis);
+                    mTimeSinceCreatedMillis,
+                    mExtras);
             return o;
         }
     }
diff --git a/media/java/android/media/metrics/PlaybackMetrics.java b/media/java/android/media/metrics/PlaybackMetrics.java
index 4aa61662..7e7f44a 100644
--- a/media/java/android/media/metrics/PlaybackMetrics.java
+++ b/media/java/android/media/metrics/PlaybackMetrics.java
@@ -20,6 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -58,6 +59,10 @@
     /** SS (HTTP Smooth Streaming) stream type. */
     public static final int STREAM_TYPE_SS = 5;
 
+    /** Unknown playback type. */
+    // TODO: change the PLAYBACK_TYPE_ values
+    /** @hide */
+    public static final int PLAYBACK_TYPE_UNKNOWN = 0;
     /** VOD (Video on Demand) playback type. */
     public static final int PLAYBACK_TYPE_VOD = 0;
     /** Live playback type. */
@@ -80,6 +85,10 @@
     /** Clear key DRM type. */
     public static final int DRM_TYPE_CLEARKEY = 6;
 
+    /** Unknown content type. */
+    // TODO: change the CONTENT_TYPE_ values
+    /** @hide */
+    public static final int CONTENT_TYPE_UNKNOWN = 0;
     /** Main contents. */
     public static final int CONTENT_TYPE_MAIN = 0;
     /** Advertisement contents. */
@@ -112,6 +121,7 @@
 
     /** @hide */
     @IntDef(prefix = "PLAYBACK_TYPE_", value = {
+        PLAYBACK_TYPE_UNKNOWN,
         PLAYBACK_TYPE_VOD,
         PLAYBACK_TYPE_LIVE,
         PLAYBACK_TYPE_OTHER
@@ -134,6 +144,7 @@
 
     /** @hide */
     @IntDef(prefix = "CONTENT_TYPE_", value = {
+        CONTENT_TYPE_UNKNOWN,
         CONTENT_TYPE_MAIN,
         CONTENT_TYPE_AD,
         CONTENT_TYPE_OTHER
@@ -158,6 +169,8 @@
     private final long mNetworkBytesRead;
     private final long mLocalBytesRead;
     private final long mNetworkTransferDurationMillis;
+    private final byte[] mDrmSessionId;
+    private final Bundle mExtras;
 
     /**
      * Creates a new PlaybackMetrics.
@@ -179,7 +192,9 @@
             int audioUnderrunCount,
             long networkBytesRead,
             long localBytesRead,
-            long networkTransferDurationMillis) {
+            long networkTransferDurationMillis,
+            byte[] drmSessionId,
+            Bundle extras) {
         this.mMediaDurationMillis = mediaDurationMillis;
         this.mStreamSource = streamSource;
         this.mStreamType = streamType;
@@ -196,6 +211,8 @@
         this.mNetworkBytesRead = networkBytesRead;
         this.mLocalBytesRead = localBytesRead;
         this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
+        this.mDrmSessionId = drmSessionId;
+        this.mExtras = extras.deepCopy();
     }
 
     /**
@@ -321,6 +338,12 @@
         return mNetworkTransferDurationMillis;
     }
 
+    /** @hide */
+    @NonNull
+    public byte[] getDrmSessionId() {
+        return mDrmSessionId;
+    }
+
     @Override
     public String toString() {
         return "PlaybackMetrics { "
@@ -339,6 +362,7 @@
                 + "networkBytesRead = " + mNetworkBytesRead + ", "
                 + "localBytesRead = " + mLocalBytesRead + ", "
                 + "networkTransferDurationMillis = " + mNetworkTransferDurationMillis
+                + "drmSessionId = " + Arrays.toString(mDrmSessionId)
                 + " }";
     }
 
@@ -361,7 +385,8 @@
                 && mAudioUnderrunCount == that.mAudioUnderrunCount
                 && mNetworkBytesRead == that.mNetworkBytesRead
                 && mLocalBytesRead == that.mLocalBytesRead
-                && mNetworkTransferDurationMillis == that.mNetworkTransferDurationMillis;
+                && mNetworkTransferDurationMillis == that.mNetworkTransferDurationMillis
+                && Arrays.equals(mDrmSessionId, that.mDrmSessionId);
     }
 
     @Override
@@ -369,7 +394,7 @@
         return Objects.hash(mMediaDurationMillis, mStreamSource, mStreamType, mPlaybackType,
                 mDrmType, mContentType, mPlayerName, mPlayerVersion, mExperimentIds,
                 mVideoFramesPlayed, mVideoFramesDropped, mAudioUnderrunCount, mNetworkBytesRead,
-                mLocalBytesRead, mNetworkTransferDurationMillis);
+                mLocalBytesRead, mNetworkTransferDurationMillis, mDrmSessionId);
     }
 
     @Override
@@ -377,6 +402,7 @@
         long flg = 0;
         if (mPlayerName != null) flg |= 0x80;
         if (mPlayerVersion != null) flg |= 0x100;
+        if (mExtras != null) flg |= 0x200;
         dest.writeLong(flg);
         dest.writeLong(mMediaDurationMillis);
         dest.writeInt(mStreamSource);
@@ -386,6 +412,7 @@
         dest.writeInt(mContentType);
         if (mPlayerName != null) dest.writeString(mPlayerName);
         if (mPlayerVersion != null) dest.writeString(mPlayerVersion);
+        if (mExtras != null) dest.writeBundle(mExtras);
         dest.writeLongArray(mExperimentIds);
         dest.writeInt(mVideoFramesPlayed);
         dest.writeInt(mVideoFramesDropped);
@@ -393,6 +420,8 @@
         dest.writeLong(mNetworkBytesRead);
         dest.writeLong(mLocalBytesRead);
         dest.writeLong(mNetworkTransferDurationMillis);
+        dest.writeInt(mDrmSessionId.length);
+        dest.writeByteArray(mDrmSessionId);
     }
 
     @Override
@@ -411,6 +440,7 @@
         int contentType = in.readInt();
         String playerName = (flg & 0x80) == 0 ? null : in.readString();
         String playerVersion = (flg & 0x100) == 0 ? null : in.readString();
+        Bundle extras = (flg & 0x200) == 0 ? null : in.readBundle();
         long[] experimentIds = in.createLongArray();
         int videoFramesPlayed = in.readInt();
         int videoFramesDropped = in.readInt();
@@ -418,6 +448,9 @@
         long networkBytesRead = in.readLong();
         long localBytesRead = in.readLong();
         long networkTransferDurationMillis = in.readLong();
+        int drmSessionIdLen = in.readInt();
+        byte[] drmSessionId = new byte[drmSessionIdLen];
+        in.readByteArray(drmSessionId);
 
         this.mMediaDurationMillis = mediaDurationMillis;
         this.mStreamSource = streamSource;
@@ -435,6 +468,8 @@
         this.mNetworkBytesRead = networkBytesRead;
         this.mLocalBytesRead = localBytesRead;
         this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
+        this.mDrmSessionId = drmSessionId;
+        this.mExtras = extras;
     }
 
     public static final @NonNull Parcelable.Creator<PlaybackMetrics> CREATOR =
@@ -470,6 +505,8 @@
         private long mNetworkBytesRead = -1;
         private long mLocalBytesRead = -1;
         private long mNetworkTransferDurationMillis = -1;
+        private byte[] mDrmSessionId = new byte[0];
+        private Bundle mExtras;
 
         /**
          * Creates a new Builder.
@@ -608,6 +645,24 @@
             return this;
         }
 
+        /**
+         * @hide
+         */
+        public @NonNull Builder setDrmSessionId(@NonNull byte[] drmSessionId) {
+            mDrmSessionId = drmSessionId;
+            return this;
+        }
+
+        /**
+         * Set extras for compatibility.
+         * <p>Should be used by support library only.
+         * @hide
+         */
+        public @NonNull Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull PlaybackMetrics build() {
 
@@ -626,7 +681,9 @@
                     mAudioUnderrunCount,
                     mNetworkBytesRead,
                     mLocalBytesRead,
-                    mNetworkTransferDurationMillis);
+                    mNetworkTransferDurationMillis,
+                    mDrmSessionId,
+                    mExtras);
             return o;
         }
 
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
index 4ee8a45..272fd9b 100644
--- a/media/java/android/media/metrics/PlaybackSession.java
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -29,6 +29,7 @@
 public final class PlaybackSession implements AutoCloseable {
     private final @NonNull String mId;
     private final @NonNull MediaMetricsManager mManager;
+    private final @NonNull LogSessionId mLogSessionId;
     private boolean mClosed = false;
 
     /**
@@ -41,6 +42,7 @@
         mManager = manager;
         AnnotationValidations.validate(NonNull.class, null, mId);
         AnnotationValidations.validate(NonNull.class, null, mManager);
+        mLogSessionId = new LogSessionId(mId);
     }
 
     /**
@@ -79,9 +81,16 @@
     }
 
     public @NonNull String getId() {
+        // TODO: remove this method and use getSessionId();
         return mId;
     }
 
+    /** @hide */
+    public @NonNull LogSessionId getSessionId() {
+        // TODO: remove getId() and use this method;
+        return mLogSessionId;
+    }
+
     @Override
     public boolean equals(@Nullable Object o) {
         if (this == o) return true;
diff --git a/media/java/android/media/metrics/PlaybackStateEvent.java b/media/java/android/media/metrics/PlaybackStateEvent.java
index 8ca5b75..dea8c1d 100644
--- a/media/java/android/media/metrics/PlaybackStateEvent.java
+++ b/media/java/android/media/metrics/PlaybackStateEvent.java
@@ -20,6 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -136,9 +137,11 @@
      */
     public PlaybackStateEvent(
             int state,
-            long timeSinceCreatedMillis) {
+            long timeSinceCreatedMillis,
+            Bundle extras) {
         this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
         this.mState = state;
+        this.mExtras = extras.deepCopy();
     }
 
     /**
@@ -174,8 +177,12 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
+        byte flg = 0;
+        if (mExtras != null) flg |= 0x1;
+        dest.writeByte(flg);
         dest.writeInt(mState);
         dest.writeLong(mTimeSinceCreatedMillis);
+        if (mExtras != null) dest.writeBundle(mExtras);
     }
 
     @Override
@@ -185,11 +192,14 @@
 
     /** @hide */
     /* package-private */ PlaybackStateEvent(@NonNull Parcel in) {
+        byte flg = in.readByte();
         int state = in.readInt();
         long timeSinceCreatedMillis = in.readLong();
+        Bundle extras = (flg & 0x1) == 0 ? null : in.readBundle();
 
         this.mState = state;
         this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+        this.mExtras = extras;
     }
 
     public static final @NonNull Parcelable.Creator<PlaybackStateEvent> CREATOR =
@@ -211,6 +221,7 @@
     public static final class Builder {
         private int mState = STATE_NOT_STARTED;
         private long mTimeSinceCreatedMillis = -1;
+        private Bundle mExtras;
 
         /**
          * Creates a new Builder.
@@ -236,11 +247,22 @@
             return this;
         }
 
+        /**
+         * Set extras for compatibility.
+         * <p>Should be used by support library only.
+         * @hide
+         */
+        public @NonNull Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
         /** Builds the instance. */
         public @NonNull PlaybackStateEvent build() {
             PlaybackStateEvent o = new PlaybackStateEvent(
                     mState,
-                    mTimeSinceCreatedMillis);
+                    mTimeSinceCreatedMillis,
+                    mExtras);
             return o;
         }
     }
diff --git a/media/java/android/media/metrics/RecordingSession.java b/media/java/android/media/metrics/RecordingSession.java
new file mode 100644
index 0000000..541d129
--- /dev/null
+++ b/media/java/android/media/metrics/RecordingSession.java
@@ -0,0 +1,62 @@
+/*
+ * 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.media.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.Objects;
+
+/**
+ * An instances of this class represents a session of media recording.
+ * @hide
+ */
+public final class RecordingSession implements AutoCloseable {
+    private final @NonNull String mId;
+    private final @NonNull MediaMetricsManager mManager;
+    private final @NonNull LogSessionId mLogSessionId;
+    private boolean mClosed = false;
+
+    /** @hide */
+    public RecordingSession(@NonNull String id, @NonNull MediaMetricsManager manager) {
+        mId = id;
+        mManager = manager;
+        AnnotationValidations.validate(NonNull.class, null, mId);
+        AnnotationValidations.validate(NonNull.class, null, mManager);
+        mLogSessionId = new LogSessionId(mId);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        RecordingSession that = (RecordingSession) o;
+        return Objects.equals(mId, that.mId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mId);
+    }
+
+    @Override
+    public void close() {
+        mClosed = true;
+    }
+}
diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java
index ef25357..aa51978 100644
--- a/media/java/android/media/metrics/TrackChangeEvent.java
+++ b/media/java/android/media/metrics/TrackChangeEvent.java
@@ -16,10 +16,12 @@
 
 package android.media.metrics;
 
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -65,9 +67,10 @@
     private final @Nullable String mLanguage;
     private final @Nullable String mLanguageRegion;
     private final int mChannelCount;
-    private final int mSampleRate;
+    private final int mAudioSampleRate;
     private final int mWidth;
     private final int mHeight;
+    private final float mVideoFrameRate;
 
 
 
@@ -99,6 +102,7 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface TrackType {}
 
+    // TODO: remove this constructor. Use the private one below.
     public TrackChangeEvent(
             int state,
             int reason,
@@ -125,9 +129,45 @@
         this.mLanguage = language;
         this.mLanguageRegion = languageRegion;
         this.mChannelCount = channelCount;
-        this.mSampleRate = sampleRate;
+        this.mAudioSampleRate = sampleRate;
         this.mWidth = width;
         this.mHeight = height;
+        this.mVideoFrameRate = -1;
+    }
+
+    private TrackChangeEvent(
+            int state,
+            int reason,
+            @Nullable String containerMimeType,
+            @Nullable String sampleMimeType,
+            @Nullable String codecName,
+            int bitrate,
+            long timeSinceCreatedMillis,
+            int type,
+            @Nullable String language,
+            @Nullable String languageRegion,
+            int channelCount,
+            int sampleRate,
+            int width,
+            int height,
+            float videoFrameRate,
+            @Nullable Bundle extras) {
+        this.mState = state;
+        this.mReason = reason;
+        this.mContainerMimeType = containerMimeType;
+        this.mSampleMimeType = sampleMimeType;
+        this.mCodecName = codecName;
+        this.mBitrate = bitrate;
+        this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+        this.mType = type;
+        this.mLanguage = language;
+        this.mLanguageRegion = languageRegion;
+        this.mChannelCount = channelCount;
+        this.mAudioSampleRate = sampleRate;
+        this.mWidth = width;
+        this.mHeight = height;
+        this.mVideoFrameRate = videoFrameRate;
+        this.mExtras = extras.deepCopy();
     }
 
     /**
@@ -223,7 +263,7 @@
      */
     @IntRange(from = -1, to = Integer.MAX_VALUE)
     public int getSampleRate() {
-        return mSampleRate;
+        return mAudioSampleRate;
     }
 
     /**
@@ -244,6 +284,16 @@
         return mHeight;
     }
 
+    /**
+     * Gets video frame rate.
+     * @return the video frame rate, or -1 if unknown.
+     * @hide
+     */
+    @FloatRange(from = -1, to = Float.MAX_VALUE)
+    public float getVideoFrameRate() {
+        return mVideoFrameRate;
+    }
+
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         int flg = 0;
@@ -252,6 +302,7 @@
         if (mCodecName != null) flg |= 0x10;
         if (mLanguage != null) flg |= 0x100;
         if (mLanguageRegion != null) flg |= 0x200;
+        if (mExtras != null) flg |= 0x400;
         dest.writeInt(flg);
         dest.writeInt(mState);
         dest.writeInt(mReason);
@@ -264,9 +315,11 @@
         if (mLanguage != null) dest.writeString(mLanguage);
         if (mLanguageRegion != null) dest.writeString(mLanguageRegion);
         dest.writeInt(mChannelCount);
-        dest.writeInt(mSampleRate);
+        dest.writeInt(mAudioSampleRate);
         dest.writeInt(mWidth);
         dest.writeInt(mHeight);
+        dest.writeFloat(mVideoFrameRate);
+        if (mExtras != null) dest.writeBundle(mExtras);
     }
 
     @Override
@@ -291,6 +344,8 @@
         int sampleRate = in.readInt();
         int width = in.readInt();
         int height = in.readInt();
+        float videoFrameRate = in.readFloat();
+        Bundle extras = (flg & 0x400) == 0 ? null : in.readBundle();
 
         this.mState = state;
         this.mReason = reason;
@@ -303,9 +358,11 @@
         this.mLanguage = language;
         this.mLanguageRegion = languageRegion;
         this.mChannelCount = channelCount;
-        this.mSampleRate = sampleRate;
+        this.mAudioSampleRate = sampleRate;
         this.mWidth = width;
         this.mHeight = height;
+        this.mVideoFrameRate = videoFrameRate;
+        this.mExtras = extras;
     }
 
     public static final @NonNull Parcelable.Creator<TrackChangeEvent> CREATOR =
@@ -335,9 +392,10 @@
                 + "language = " + mLanguage + ", "
                 + "languageRegion = " + mLanguageRegion + ", "
                 + "channelCount = " + mChannelCount + ", "
-                + "sampleRate = " + mSampleRate + ", "
+                + "sampleRate = " + mAudioSampleRate + ", "
                 + "width = " + mWidth + ", "
-                + "height = " + mHeight
+                + "height = " + mHeight + ", "
+                + "videoFrameRate = " + mVideoFrameRate
                 + " }";
     }
 
@@ -357,16 +415,17 @@
                 && Objects.equals(mLanguage, that.mLanguage)
                 && Objects.equals(mLanguageRegion, that.mLanguageRegion)
                 && mChannelCount == that.mChannelCount
-                && mSampleRate == that.mSampleRate
+                && mAudioSampleRate == that.mAudioSampleRate
                 && mWidth == that.mWidth
-                && mHeight == that.mHeight;
+                && mHeight == that.mHeight
+                && mVideoFrameRate == that.mVideoFrameRate;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mState, mReason, mContainerMimeType, mSampleMimeType, mCodecName,
                 mBitrate, mTimeSinceCreatedMillis, mType, mLanguage, mLanguageRegion,
-                mChannelCount, mSampleRate, mWidth, mHeight);
+                mChannelCount, mAudioSampleRate, mWidth, mHeight, mVideoFrameRate);
     }
 
     /**
@@ -385,9 +444,11 @@
         private @Nullable String mLanguage;
         private @Nullable String mLanguageRegion;
         private int mChannelCount = -1;
-        private int mSampleRate = -1;
+        private int mAudioSampleRate = -1;
         private int mWidth = -1;
         private int mHeight = -1;
+        private float mVideoFrameRate = -1;
+        private Bundle mExtras;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -512,9 +573,10 @@
          */
         public @NonNull Builder setSampleRate(
                 @IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
+            // TODO: rename it to setAudioSampleRate
             checkNotUsed();
             mBuilderFieldsSet |= 0x800;
-            mSampleRate = value;
+            mAudioSampleRate = value;
             return this;
         }
 
@@ -540,6 +602,28 @@
             return this;
         }
 
+        /**
+         * Sets video frame rate.
+         * @param value the video frame rate. -1 indicates the value is unknown.
+         * @hide
+         */
+        public @NonNull Builder setVideoFrameRate(
+                @FloatRange(from = -1, to = Float.MAX_VALUE) float value) {
+            checkNotUsed();
+            mVideoFrameRate = value;
+            return this;
+        }
+
+        /**
+         * Set extras for compatibility.
+         * <p>Should be used by support library only.
+         * @hide
+         */
+        public @NonNull Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull TrackChangeEvent build() {
             checkNotUsed();
@@ -557,9 +641,11 @@
                     mLanguage,
                     mLanguageRegion,
                     mChannelCount,
-                    mSampleRate,
+                    mAudioSampleRate,
                     mWidth,
-                    mHeight);
+                    mHeight,
+                    mVideoFrameRate,
+                    mExtras);
             return o;
         }
 
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
index 307d80d..07866ac 100644
--- a/media/jni/android_media_MediaCodecList.cpp
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -105,6 +105,7 @@
             // This should never happen unless something is really wrong
             jniThrowException(
                         env, "java/lang/RuntimeException", "cannot get MediaCodecList");
+            return NULL;
         }
 
         sListWrapper.reset(new JavaMediaCodecListWrapper(mcl));
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 56f6c45..53f6fe2 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -445,8 +445,7 @@
         jniThrowException(env, "android/media/DeniedByServerException", msg);
         return true;
     } else if (err == DEAD_OBJECT) {
-        jniThrowException(env, "android/media/MediaDrmResetException",
-                "mediaserver died");
+        jniThrowException(env, "android/media/MediaDrmResetException", msg);
         return true;
     } else if (isSessionException(err)) {
         throwSessionException(env, msg, err);
@@ -967,10 +966,12 @@
     status_t err = drm->initCheck();
 
     if (err != OK) {
+        auto logs(DrmUtils::gLogBuf.getLogs());
+        auto msg(DrmUtils::GetExceptionMessage(err, "Failed to instantiate drm object", logs));
         jniThrowException(
                 env,
                 "android/media/UnsupportedSchemeException",
-                "Failed to instantiate drm object.");
+                msg.c_str());
         return;
     }
 
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index a9fd6f2..d2ed73e 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -6,6 +6,7 @@
   }
 
   public class ConnectivityManager {
+    method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot();
     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);
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 373fa3c..f5972fa 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -2,7 +2,7 @@
 package android.net {
 
   public class CaptivePortal implements android.os.Parcelable {
-    method public void logEvent(int, @NonNull String);
+    method @Deprecated 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
diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortal.java b/packages/Connectivity/framework/src/android/net/CaptivePortal.java
index 269bbf2..4a7b601 100644
--- a/packages/Connectivity/framework/src/android/net/CaptivePortal.java
+++ b/packages/Connectivity/framework/src/android/net/CaptivePortal.java
@@ -160,12 +160,11 @@
      * @param eventId one of the CAPTIVE_PORTAL_LOGIN_* constants in metrics_constants.proto.
      * @param packageName captive portal application package name.
      * @hide
+     * @deprecated The event will not be logged in Android S and above. The
+     * caller is migrating to statsd.
      */
+    @Deprecated
     @SystemApi
     public void logEvent(int eventId, @NonNull String packageName) {
-        try {
-            ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName);
-        } catch (RemoteException e) {
-        }
     }
 }
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 39ec2edc..d7c6854 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -1259,6 +1259,25 @@
     }
 
     /**
+     * Return a list of {@link NetworkStateSnapshot}s, one for each network that is currently
+     * connected.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK,
+            android.Manifest.permission.NETWORK_SETTINGS})
+    @NonNull
+    public List<NetworkStateSnapshot> getAllNetworkStateSnapshot() {
+        try {
+            return mService.getAllNetworkStateSnapshot();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the {@link Network} object currently serving a given type, or
      * null if the given type is not connected.
      *
diff --git a/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl b/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl
index fe21905..e35f8d4 100644
--- a/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl
+++ b/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl
@@ -23,5 +23,4 @@
 oneway interface ICaptivePortal {
     void appRequest(int request);
     void appResponse(int response);
-    void logEvent(int eventId, String packageName);
 }
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 160338d..cd49258 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -31,6 +31,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
+import android.net.NetworkStateSnapshot;
 import android.net.OemNetworkPreferences;
 import android.net.ProxyInfo;
 import android.net.UidRange;
@@ -79,6 +80,8 @@
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     NetworkState[] getAllNetworkState();
 
+    List<NetworkStateSnapshot> getAllNetworkStateSnapshot();
+
     boolean isActiveNetworkMetered();
 
     boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress,
diff --git a/core/java/android/net/NetworkState.java b/packages/Connectivity/framework/src/android/net/NetworkState.java
similarity index 97%
rename from core/java/android/net/NetworkState.java
rename to packages/Connectivity/framework/src/android/net/NetworkState.java
index 813fde1..d010265 100644
--- a/core/java/android/net/NetworkState.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkState.java
@@ -115,7 +115,8 @@
     }
 
     @UnsupportedAppUsage
-    public static final @android.annotation.NonNull Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
+    @NonNull
+    public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
         @Override
         public NetworkState createFromParcel(Parcel in) {
             return new NetworkState(in);
diff --git a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 43fffd7..739ddad 100644
--- a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -30,8 +30,8 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.provider.Settings;
-import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -92,8 +92,8 @@
     }
 
     @VisibleForTesting
-    protected class ActiveDataSubscriptionIdChangedListener extends PhoneStateListener
-            implements PhoneStateListener.ActiveDataSubscriptionIdChangedListener {
+    protected class ActiveDataSubscriptionIdListener extends TelephonyCallback
+            implements TelephonyCallback.ActiveDataSubscriptionIdListener {
         @Override
         public void onActiveDataSubscriptionIdChanged(int subId) {
             mActiveSubId = subId;
@@ -121,8 +121,8 @@
             }
         };
 
-        ctx.getSystemService(TelephonyManager.class).registerPhoneStateListener(
-                new HandlerExecutor(handler), new ActiveDataSubscriptionIdChangedListener());
+        ctx.getSystemService(TelephonyManager.class).registerTelephonyCallback(
+                new HandlerExecutor(handler), new ActiveDataSubscriptionIdListener());
 
         updateAvoidBadWifi();
         updateMeteredMultipathPreference();
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 139c8e5..63edc77 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -165,6 +165,9 @@
         if (mParameters.isDeviceTransfer()) {
             flags |= BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER;
         }
+        if (mParameters.isEncrypted()) {
+            flags |= BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
+        }
         return flags;
     }
 
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
index 2946db3..1ba1bc6 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
@@ -28,10 +28,12 @@
     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 static final String KEY_IS_ENCRYPTED = "is_encrypted";
 
     private boolean mFakeEncryptionFlag;
     private boolean mIsNonIncrementalOnly;
     private boolean mIsDeviceTransfer;
+    private boolean mIsEncrypted;
 
     public LocalTransportParameters(Handler handler, ContentResolver resolver) {
         super(handler, resolver, Settings.Secure.getUriFor(SETTING));
@@ -49,6 +51,10 @@
         return mIsDeviceTransfer;
     }
 
+    boolean isEncrypted() {
+        return mIsEncrypted;
+    }
+
     public String getSettingValue(ContentResolver resolver) {
         return Settings.Secure.getString(resolver, SETTING);
     }
@@ -57,5 +63,6 @@
         mFakeEncryptionFlag = parser.getBoolean(KEY_FAKE_ENCRYPTION_FLAG, false);
         mIsNonIncrementalOnly = parser.getBoolean(KEY_NON_INCREMENTAL_ONLY, false);
         mIsDeviceTransfer = parser.getBoolean(KEY_IS_DEVICE_TRANSFER, false);
+        mIsEncrypted = parser.getBoolean(KEY_IS_ENCRYPTED, false);
     }
 }
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/toolbar_base_layout.xml
new file mode 100644
index 0000000..c799b99
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/toolbar_base_layout.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+<!-- The main content view -->
+<LinearLayout
+    android:id="@+id/content_parent"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true"
+    android:transitionGroup="true"
+    android:orientation="vertical">
+    <Toolbar
+        android:id="@+id/action_bar"
+        style="?android:attr/actionBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:theme="?android:attr/actionBarTheme" />
+    <FrameLayout
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index 637805f..ad94cd03 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -24,6 +24,7 @@
 import android.widget.Toolbar;
 
 import androidx.annotation.Nullable;
+import androidx.core.os.BuildCompat;
 import androidx.fragment.app.FragmentActivity;
 
 import com.google.android.material.appbar.CollapsingToolbarLayout;
@@ -40,8 +41,15 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        super.setContentView(R.layout.collapsing_toolbar_base_layout);
-        mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
+        // TODO(b/181723278): Update the version check after SDK for S is finalized
+        // The collapsing toolbar is only supported if the android platform version is S or higher.
+        // Otherwise the regular action bar will be shown.
+        if (BuildCompat.isAtLeastS()) {
+            super.setContentView(R.layout.collapsing_toolbar_base_layout);
+            mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
+        } else {
+            super.setContentView(R.layout.toolbar_base_layout);
+        }
 
         final Toolbar toolbar = findViewById(R.id.action_bar);
         setActionBar(toolbar);
@@ -90,6 +98,14 @@
         super.setTitle(titleId);
     }
 
+    @Override
+    public boolean onNavigateUp() {
+        if (!super.onNavigateUp()) {
+            finish();
+        }
+        return true;
+    }
+
     /**
      * Returns an instance of collapsing toolbar.
      */
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 79fbcc3..efa9f3c 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -64,8 +64,12 @@
     <string name="wifi_security_eap" translatable="false">WPA/WPA2/WPA3-Enterprise</string>
     <!-- Do not translate.  Concise terminology for wifi with WPA 802.1x EAP security -->
     <string name="wifi_security_eap_wpa" translatable="false">WPA-Enterprise</string>
+    <!-- Do not translate.  Concise terminology for wifi with 802.1x EAP security -->
+    <string name="wifi_security_eap_wpa_wpa2" translatable="false">WPA/WPA2-Enterprise</string>
     <!-- Do not translate.  Concise terminology for wifi with WPA2/WPA3 802.1x EAP security -->
     <string name="wifi_security_eap_wpa2_wpa3" translatable="false">WPA2/WPA3-Enterprise</string>
+    <!-- Do not translate.  Concise terminology for wifi with WPA3 802.1x EAP security -->
+    <string name="wifi_security_eap_wpa3" translatable="false">WPA3-Enterprise</string>
     <!-- Do not translate.  Concise terminology for Passpoint network -->
     <string name="wifi_security_passpoint" translatable="false">Passpoint</string>
     <!-- Do not translate.  Terminology for wifi with WPA3 security -->
@@ -1059,7 +1063,14 @@
     <!-- Title for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
     <string name="accessibility_display_daltonizer_preference_title">Color correction</string>
     <!-- Subtitle for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_display_daltonizer_preference_subtitle"><![CDATA[Color correction allows you to adjust how colors are displayed on your device]]></string>
+    <string name="accessibility_display_daltonizer_preference_subtitle">
+        <![CDATA[
+        Adjust how colors display on your device. This can be helpful when you want to:<br/><br/>
+        <ol>
+            <li> See colors more accurately</li>
+            <li> Remove colors to help you focus</li>
+        </ol>
+        ]]></string>
     <!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
     <string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
 
@@ -1295,6 +1306,8 @@
 
     <!-- Name of the phone device. [CHAR LIMIT=30] -->
     <string name="media_transfer_this_device_name">Phone speaker</string>
+    <!-- Name of the phone device with an active remote session. [CHAR LIMIT=30] -->
+    <string name="media_transfer_this_phone">This phone</string>
 
     <!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
     <string name="profile_connect_timeout_subtext">Problem connecting. Turn device off &amp; back on</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
index 43717ab..dfde3c7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
@@ -27,7 +27,7 @@
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.provider.Settings;
-import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -72,7 +72,10 @@
             checkIfAllSubsystemsRestartsAreDone();
         }
     };
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+    private final MobileTelephonyCallback mTelephonyCallback = new MobileTelephonyCallback();
+
+    private class MobileTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.RadioPowerStateListener {
         @Override
         public void onRadioPowerStateChanged(int state) {
             if (!mTelephonyRestartInProgress || mCurrentRecoveryCallback == null) {
@@ -85,7 +88,7 @@
                 checkIfAllSubsystemsRestartsAreDone();
             }
         }
-    };
+    }
 
     public ConnectivitySubsystemsRecoveryManager(@NonNull Context context,
             @NonNull Handler handler) {
@@ -201,12 +204,12 @@
     }
 
     private void startTrackingTelephonyRestart() {
-        mTelephonyManager.registerPhoneStateListener(new HandlerExecutor(mHandler),
-                mPhoneStateListener);
+        mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(mHandler),
+                mTelephonyCallback);
     }
 
     private void stopTrackingTelephonyRestart() {
-        mTelephonyManager.unregisterPhoneStateListener(mPhoneStateListener);
+        mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
     }
 
     private void checkIfAllSubsystemsRestartsAreDone() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 0cd5e4d..1a08366 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -17,11 +17,11 @@
 
 import android.os.Handler;
 import android.os.Looper;
-import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -40,9 +40,9 @@
     private final SubscriptionInfo mSubscriptionInfo;
     private final Callback mCallback;
     private final MobileStatus mMobileStatus;
-    private final PhoneStateListener mPhoneStateListener;
     private final SubscriptionDefaults mDefaults;
     private final Handler mReceiverHandler;
+    private final MobileTelephonyCallback mTelephonyCallback;
 
     /**
      * MobileStatusTracker constructors
@@ -58,7 +58,7 @@
             SubscriptionInfo info, SubscriptionDefaults defaults, Callback callback) {
         mPhone = phone;
         mReceiverHandler = new Handler(receiverLooper);
-        mPhoneStateListener = new MobilePhoneStateListener();
+        mTelephonyCallback = new MobileTelephonyCallback();
         mSubscriptionInfo = info;
         mDefaults = defaults;
         mCallback = callback;
@@ -68,8 +68,8 @@
                 /* updateTelephony= */false, new MobileStatus(mMobileStatus)));
     }
 
-    public PhoneStateListener getPhoneStateListener() {
-        return mPhoneStateListener;
+    public MobileTelephonyCallback getTelephonyCallback() {
+        return mTelephonyCallback;
     }
 
     /**
@@ -77,9 +77,9 @@
      */
     public void setListening(boolean listening) {
         if (listening) {
-            mPhone.registerPhoneStateListener(mReceiverHandler::post, mPhoneStateListener);
+            mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback);
         } else {
-            mPhone.unregisterPhoneStateListener(mPhoneStateListener);
+            mPhone.unregisterTelephonyCallback(mTelephonyCallback);
         }
     }
 
@@ -106,15 +106,14 @@
                 || activity == TelephonyManager.DATA_ACTIVITY_OUT;
     }
 
-    private class MobilePhoneStateListener extends PhoneStateListener implements
-            PhoneStateListener.ServiceStateChangedListener,
-            PhoneStateListener.SignalStrengthsChangedListener,
-            PhoneStateListener.CallStateChangedListener,
-            PhoneStateListener.DataConnectionStateChangedListener,
-            PhoneStateListener.DataActivityListener,
-            PhoneStateListener.CarrierNetworkChangeListener,
-            PhoneStateListener.ActiveDataSubscriptionIdChangedListener,
-            PhoneStateListener.DisplayInfoChangedListener{
+    public class MobileTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.ServiceStateListener,
+            TelephonyCallback.SignalStrengthsListener,
+            TelephonyCallback.DataConnectionStateListener,
+            TelephonyCallback.DataActivityListener,
+            TelephonyCallback.CarrierNetworkListener,
+            TelephonyCallback.ActiveDataSubscriptionIdListener,
+            TelephonyCallback.DisplayInfoListener{
 
         @Override
         public void onSignalStrengthsChanged(SignalStrength signalStrength) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index a81a05f..303ee3c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -223,7 +223,8 @@
     public static final int SECURITY_OWE = 4;
     public static final int SECURITY_SAE = 5;
     public static final int SECURITY_EAP_SUITE_B = 6;
-    public static final int SECURITY_MAX_VAL = 7; // Has to be the last
+    public static final int SECURITY_EAP_WPA3_ENTERPRISE = 7;
+    public static final int SECURITY_MAX_VAL = 8; // Has to be the last
 
     private static final int PSK_UNKNOWN = 0;
     private static final int PSK_WPA = 1;
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
index 13572fa..db712e4 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -17,6 +17,7 @@
 <com.android.systemui.util.NeverExactlyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
+    android:minHeight="48dp"
     android:clickable="true"
     android:paddingBottom="@dimen/qs_tile_padding_top"
     android:paddingTop="@dimen/qs_tile_padding_top"
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml b/packages/SystemUI/res/layout/udfps_animation_view_bp.xml
new file mode 100644
index 0000000..0cfbf2e
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_animation_view_bp.xml
@@ -0,0 +1,22 @@
+<?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.
+  -->
+<com.android.systemui.biometrics.UdfpsAnimationViewBp
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/udfps_animation_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+</com.android.systemui.biometrics.UdfpsAnimationViewBp>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 3bc1c80..b3fca36 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -183,7 +183,9 @@
     <color name="biometric_dialog_error">#ffd93025</color>                  <!-- red 600 -->
 
     <!-- UDFPS colors -->
-    <color name="udfps_enroll_icon">#000000</color> <!-- 100% black -->
+    <color name="udfps_enroll_icon">#000000</color>                         <!-- 100% black -->
+    <color name="udfps_moving_target_fill">#cc4285f4</color>                <!-- 80% blue -->
+    <color name="udfps_moving_target_stroke">#ff669DF6</color>              <!-- 100% blue -->
 
     <!-- Logout button -->
     <color name="logout_button_bg_color">#ccffffff</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 4bdc16b..b75a0bc 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -412,6 +412,9 @@
     <!-- Whether or not child notifications that are part of a group will have shadows. -->
     <bool name="config_enableShadowOnChildNotifications">true</bool>
 
+    <!-- If true, group numbers are shown in the expander instead of via "+N" overflow number -->
+    <bool name="config_showNotificationGroupCountInExpander">true</bool>
+
     <!-- Whether or not a view containing child notifications will have a custom background when
          it has been expanded to reveal its children. -->
     <bool name="config_showGroupNotificationBgWhenExpanded">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5f8df5a..b5ded01 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -294,8 +294,10 @@
     <string name="screenrecord_share_label">Share</string>
     <!-- A toast message shown after successfully canceling a screen recording [CHAR LIMIT=NONE] -->
     <string name="screenrecord_cancel_success">Screen recording canceled</string>
-    <!-- Notification text shown after saving a screen recording to prompt the user to view it [CHAR LIMIT=100] -->
-    <string name="screenrecord_save_message">Screen recording saved, tap to view</string>
+    <!-- Notification text shown after saving a screen recording [CHAR LIMIT=100] -->
+    <string name="screenrecord_save_title">Screen recording saved</string>
+    <!-- Subtext for a notification shown after saving a screen recording to prompt the user to view it [CHAR_LIMIT=100] -->
+    <string name="screenrecord_save_text">Tap to view</string>
     <!-- A toast message shown when there is an error deleting a screen recording [CHAR LIMIT=NONE] -->
     <string name="screenrecord_delete_error">Error deleting screen recording</string>
     <!-- A toast message shown when the screen recording cannot be started due to insufficient permissions [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2d202fb..ec26b8d 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -608,6 +608,11 @@
         <item name="android:windowCloseOnTouchOutside">true</item>
     </style>
 
+    <!-- Privacy dialog -->
+    <style name="PrivacyDialog" parent="ScreenRecord">
+        <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
+    </style>
+
     <!-- USB Contaminant dialog -->
     <style name ="USBContaminant" />
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
index e5ced3e..54242be 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
@@ -21,5 +21,5 @@
  */
 oneway interface ISplitScreenListener {
     void onStagePositionChanged(int stage, int position);
-    void onTaskStageChanged(int taskId, int stage);
-}
\ No newline at end of file
+    void onTaskStageChanged(int taskId, int stage, boolean visible);
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 5c943f6..bac4c43 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -19,6 +19,7 @@
 import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
 import android.graphics.Insets;
@@ -34,7 +35,7 @@
 
 /**
  * Temporary callbacks into SystemUI.
- * Next id = 30
+ * Next id = 43
  */
 interface ISystemUiProxy {
 
@@ -251,5 +252,7 @@
     void startShortcut(in String packageName, in String shortcutId, in int stage, in int position,
             in Bundle options, in UserHandle user) = 40;
     void startIntent(
-            in PendingIntent intent, in int stage, in int position, in Bundle options) = 41;
+            in PendingIntent intent, in Intent fillInIntent, in int stage, in int position,
+            in Bundle options) = 41;
+    void removeFromSideStage(in int taskId) = 42;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index e6477f1..400bf15 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -113,17 +113,6 @@
                 // TODO(bc-unlock): Build wrapped object for non-apps target.
                 final RemoteAnimationTargetCompat[] nonAppsCompat =
                         new RemoteAnimationTargetCompat[0];
-                final Runnable animationFinishedCallback = new Runnable() {
-                    @Override
-                    public void run() {
-                        try {
-                            finishCallback.onTransitionFinished(null /* wct */);
-                        } catch (RemoteException e) {
-                            Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
-                                    + " finished callback", e);
-                        }
-                    }
-                };
 
                 // TODO(b/177438007): Move this set-up logic into launcher's animation impl.
                 boolean isReturnToHome = false;
@@ -143,8 +132,8 @@
                         final TransitionInfo.Change change = info.getChanges().get(i);
                         final SurfaceControl leash = change.getLeash();
                         final int mode = info.getChanges().get(i).getMode();
-                        // Only deal with roots
-                        if (change.getParent() != null) continue;
+                        // Only deal with independent layers
+                        if (!TransitionInfo.isIndependent(change, info)) continue;
                         if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
                             t.setLayer(leash, info.getChanges().size() * 3 - i);
                         }
@@ -156,6 +145,18 @@
                     }
                 }
                 t.apply();
+
+                final Runnable animationFinishedCallback = new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            finishCallback.onTransitionFinished(null /* wct */);
+                        } catch (RemoteException e) {
+                            Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
+                                    + " finished callback", e);
+                        }
+                    }
+                };
                 // TODO(bc-unlcok): Pass correct transit type.
                 remoteAnimationAdapter.onAnimationStart(
                         TRANSIT_OLD_NONE,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 2373d75..83c2d1e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -405,14 +405,19 @@
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
 
+        /**
+         * Set the amount (ratio) that the device has transitioned to doze.
+         *
+         * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
+         */
         public void setDarkAmount(float darkAmount) {
-            boolean isAwake = darkAmount != 0;
-            boolean wasAwake = mDarkAmount != 0;
-            if (isAwake == wasAwake) {
+            boolean isDozing = darkAmount != 0;
+            boolean wasDozing = mDarkAmount != 0;
+            if (isDozing == wasDozing) {
                 return;
             }
             mDarkAmount = darkAmount;
-            setLayoutAnimationListener(isAwake ? null : mKeepAwakeListener);
+            setLayoutAnimationListener(isDozing ? null : mKeepAwakeListener);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
index a029003..a51b6fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
@@ -17,6 +17,7 @@
 package com.android.systemui.biometrics;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.view.View;
@@ -46,12 +47,17 @@
     }
 
     public void onSensorRectUpdated(@NonNull RectF sensorRect) {
-        int margin =  (int) (sensorRect.bottom - sensorRect.top) / 5;
-        mFingerprintDrawable.setBounds(
-                (int) sensorRect.left + margin,
+        final int margin =  (int) sensorRect.height() / 8;
+
+        final Rect bounds = new Rect((int) sensorRect.left + margin,
                 (int) sensorRect.top + margin,
                 (int) sensorRect.right - margin,
                 (int) sensorRect.bottom - margin);
+        updateFingerprintIconBounds(bounds);
+    }
+
+    protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
+        mFingerprintDrawable.setBounds(bounds);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index 28b5719..015a598 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -22,13 +22,14 @@
 import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.Rect;
 import android.graphics.RectF;
-import android.util.Log;
+import android.graphics.drawable.Drawable;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.settingslib.Utils;
 import com.android.systemui.R;
 
 /**
@@ -40,9 +41,13 @@
     private static final float SHADOW_RADIUS = 5.f;
     private static final float PROGRESS_BAR_RADIUS = 140.f;
 
-    @Nullable private RectF mSensorRect;
+    @NonNull private final Drawable mMovingTargetFpIcon;
     @NonNull private final Paint mSensorPaint;
-    private final int mNotificationShadeColor;
+    @NonNull private final Paint mBlueFill;
+    @NonNull private final Paint mBlueStroke;;
+
+    @Nullable private RectF mSensorRect;
+    @Nullable private UdfpsEnrollHelper mEnrollHelper;
 
     UdfpsAnimationEnroll(@NonNull Context context) {
         super(context);
@@ -53,8 +58,24 @@
         mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, Color.BLACK);
         mSensorPaint.setStyle(Paint.Style.FILL);
 
-        mNotificationShadeColor = Utils.getColorAttr(context,
-                android.R.attr.colorBackgroundFloating).getDefaultColor();
+        mBlueFill = new Paint(0 /* flags */);
+        mBlueFill.setAntiAlias(true);
+        mBlueFill.setColor(context.getColor(R.color.udfps_moving_target_fill));
+        mBlueFill.setStyle(Paint.Style.FILL);
+
+        mBlueStroke = new Paint(0 /* flags */);
+        mBlueStroke.setAntiAlias(true);
+        mBlueStroke.setColor(context.getColor(R.color.udfps_moving_target_stroke));
+        mBlueStroke.setStyle(Paint.Style.STROKE);
+        mBlueStroke.setStrokeWidth(12);
+
+        mMovingTargetFpIcon = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
+        mMovingTargetFpIcon.setTint(Color.WHITE);
+        mMovingTargetFpIcon.mutate();
+    }
+
+    void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
+        mEnrollHelper = helper;
     }
 
     @Override
@@ -74,6 +95,12 @@
     }
 
     @Override
+    protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
+        super.updateFingerprintIconBounds(bounds);
+        mMovingTargetFpIcon.setBounds(bounds);
+    }
+
+    @Override
     public void draw(@NonNull Canvas canvas) {
         if (isIlluminationShowing()) {
             return;
@@ -87,6 +114,24 @@
             }
         }
         mFingerprintDrawable.draw(canvas);
+
+        // Draw moving target
+        if (mEnrollHelper.isCenterEnrollmentComplete()) {
+            mFingerprintDrawable.setAlpha(64);
+
+            canvas.save();
+            final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
+            canvas.translate(point.x, point.y);
+            if (mSensorRect != null) {
+                canvas.drawOval(mSensorRect, mBlueFill);
+                canvas.drawOval(mSensorRect, mBlueStroke);
+            }
+
+            mMovingTargetFpIcon.draw(canvas);
+            canvas.restore();
+        } else {
+            mFingerprintDrawable.setAlpha(255);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index f4dd181..43ecf67 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.PointF;
 import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
@@ -62,7 +63,10 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        getUdfpsAnimation().onDestroy();
+
+        if (getUdfpsAnimation() != null) {
+            getUdfpsAnimation().onDestroy();
+        }
     }
 
     private int expansionToAlpha(float expansion) {
@@ -78,11 +82,19 @@
     }
 
     void onIlluminationStarting() {
+        if (getUdfpsAnimation() == null) {
+            return;
+        }
+
         getUdfpsAnimation().setIlluminationShowing(true);
         postInvalidate();
     }
 
     void onIlluminationStopped() {
+        if (getUdfpsAnimation() == null) {
+            return;
+        }
+
         getUdfpsAnimation().setIlluminationShowing(false);
         postInvalidate();
     }
@@ -131,4 +143,14 @@
         }
         return getUdfpsAnimation().getPaddingY();
     }
+
+    /**
+     * @return the amount of translation needed if the view currently requires the user to touch
+     *         somewhere other than the exact center of the sensor. For example, this can happen
+     *         during guided enrollment.
+     */
+    @NonNull
+    PointF getTouchTranslation() {
+        return new PointF(0, 0);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java
new file mode 100644
index 0000000..515b442
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.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.systemui.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Class that coordinates non-HBM animations during BiometricPrompt.
+ *
+ * Note that {@link AuthBiometricUdfpsView} also shows UDFPS animations. At some point we should
+ * de-dupe this if necessary. This will probably happen once the top-level TODO in UdfpsController
+ * is completed (inflate operation-specific views, instead of inflating generic udfps_view and
+ * adding operation-specific animations to it).
+ */
+public class UdfpsAnimationViewBp extends UdfpsAnimationView {
+    public UdfpsAnimationViewBp(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Nullable
+    @Override
+    protected UdfpsAnimation getUdfpsAnimation() {
+        return null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
index 19e77493..543df33 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.PointF;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
@@ -33,7 +34,7 @@
 
     private static final String TAG = "UdfpsAnimationViewEnroll";
 
-    @NonNull private UdfpsAnimation mUdfpsAnimation;
+    @NonNull private UdfpsAnimationEnroll mUdfpsAnimation;
     @NonNull private UdfpsProgressBar mProgressBar;
     @Nullable private UdfpsEnrollHelper mEnrollHelper;
 
@@ -50,6 +51,7 @@
 
     public void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
         mEnrollHelper = helper;
+        mUdfpsAnimation.setEnrollHelper(helper);
     }
 
     @Override
@@ -81,4 +83,14 @@
 
         mProgressBar.setProgress(interpolatedProgress, true);
     }
+
+    @NonNull
+    @Override
+    PointF getTouchTranslation() {
+        if (!mEnrollHelper.isCenterEnrollmentComplete()) {
+            return new PointF(0, 0);
+        } else {
+            return mEnrollHelper.getNextGuidedEnrollmentPoint();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index e1d7eb3..852cdcb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -97,7 +97,7 @@
         public void showUdfpsOverlay(int sensorId, int reason) {
             if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
                     || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) {
-                mEnrollHelper = new UdfpsEnrollHelper(reason);
+                mEnrollHelper = new UdfpsEnrollHelper(mContext, reason);
             } else {
                 mEnrollHelper = null;
             }
@@ -231,6 +231,9 @@
 
     @Override
     public void dozeTimeTick() {
+        if (mView == null) {
+            return;
+        }
         mView.dozeTimeTick();
     }
 
@@ -358,23 +361,29 @@
         switch (reason) {
             case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
             case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: {
-                final UdfpsAnimationViewEnroll animation = (UdfpsAnimationViewEnroll)
+                final UdfpsAnimationViewEnroll view = (UdfpsAnimationViewEnroll)
                         inflater.inflate(R.layout.udfps_animation_view_enroll, null, false);
-                animation.setEnrollHelper(mEnrollHelper);
-                return animation;
+                view.setEnrollHelper(mEnrollHelper);
+                return view;
+            }
+
+            case IUdfpsOverlayController.REASON_AUTH_BP: {
+                final UdfpsAnimationViewBp view = (UdfpsAnimationViewBp)
+                        inflater.inflate(R.layout.udfps_animation_view_bp, null, false);
+                return view;
             }
 
             case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: {
-                final UdfpsAnimationViewKeyguard animation = (UdfpsAnimationViewKeyguard)
+                final UdfpsAnimationViewKeyguard view = (UdfpsAnimationViewKeyguard)
                         inflater.inflate(R.layout.udfps_animation_view_keyguard, null, false);
-                animation.setStatusBarStateController(mStatusBarStateController);
-                return animation;
+                view.setStatusBarStateController(mStatusBarStateController);
+                return view;
             }
 
             case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: {
-                final UdfpsAnimationViewFpmOther animation = (UdfpsAnimationViewFpmOther)
+                final UdfpsAnimationViewFpmOther view = (UdfpsAnimationViewFpmOther)
                         inflater.inflate(R.layout.udfps_animation_view_fpm_other, null, false);
-                return animation;
+                return view;
             }
 
             default:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 942fa7a..14eca1b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -18,7 +18,13 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.PointF;
 import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.util.TypedValue;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Helps keep track of enrollment state and animates the progress bar accordingly.
@@ -26,20 +32,48 @@
 public class UdfpsEnrollHelper {
     private static final String TAG = "UdfpsEnrollHelper";
 
+    // Enroll with two center touches before going to guided enrollment
+    private static final int NUM_CENTER_TOUCHES = 2;
+
     interface Listener {
         void onEnrollmentProgress(int remaining, int totalSteps);
     }
 
     // IUdfpsOverlayController reason
     private final int mEnrollReason;
+    private final List<PointF> mGuidedEnrollmentPoints;
 
     private int mTotalSteps = -1;
-    private int mCurrentProgress = 0;
+    private int mRemainingSteps = -1;
+
+    // Note that this is actually not equal to "mTotalSteps - mRemainingSteps", because the
+    // interface makes no promises about monotonically increasing by one each time.
+    private int mLocationsEnrolled = 0;
 
     @Nullable Listener mListener;
 
-    public UdfpsEnrollHelper(int reason) {
+    public UdfpsEnrollHelper(@NonNull Context context, int reason) {
         mEnrollReason = reason;
+        mGuidedEnrollmentPoints = new ArrayList<>();
+
+        // Number of pixels per mm
+        float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1,
+                context.getResources().getDisplayMetrics());
+
+        mGuidedEnrollmentPoints.add(new PointF( 2.00f * px,  0.00f * px));
+        mGuidedEnrollmentPoints.add(new PointF( 0.87f * px, -2.70f * px));
+        mGuidedEnrollmentPoints.add(new PointF(-1.80f * px, -1.31f * px));
+        mGuidedEnrollmentPoints.add(new PointF(-1.80f * px,  1.31f * px));
+        mGuidedEnrollmentPoints.add(new PointF( 0.88f * px,  2.70f * px));
+        mGuidedEnrollmentPoints.add(new PointF( 3.94f * px, -1.06f * px));
+        mGuidedEnrollmentPoints.add(new PointF( 2.90f * px, -4.14f * px));
+        mGuidedEnrollmentPoints.add(new PointF(-0.52f * px, -5.95f * px));
+        mGuidedEnrollmentPoints.add(new PointF(-3.33f * px, -3.33f * px));
+        mGuidedEnrollmentPoints.add(new PointF(-3.99f * px, -0.35f * px));
+        mGuidedEnrollmentPoints.add(new PointF(-3.62f * px,  2.54f * px));
+        mGuidedEnrollmentPoints.add(new PointF(-1.49f * px,  5.57f * px));
+        mGuidedEnrollmentPoints.add(new PointF( 2.29f * px,  4.92f * px));
+        mGuidedEnrollmentPoints.add(new PointF( 3.82f * px,  1.78f * px));
     }
 
     boolean shouldShowProgressBar() {
@@ -51,6 +85,12 @@
             mTotalSteps = remaining;
         }
 
+        if (remaining != mRemainingSteps) {
+            mLocationsEnrolled++;
+        }
+
+        mRemainingSteps = remaining;
+
         if (mListener != null) {
             mListener.onEnrollmentProgress(remaining, mTotalSteps);
         }
@@ -67,8 +107,21 @@
         // bar can be updated. If enrollment has not started yet, the progress bar will be empty
         // anyway.
         if (mTotalSteps != -1) {
-            final int remainingSteps = mTotalSteps - mCurrentProgress;
-            mListener.onEnrollmentProgress(remainingSteps, mTotalSteps);
+            mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps);
         }
     }
+
+    boolean isCenterEnrollmentComplete() {
+        if (mTotalSteps == -1 || mRemainingSteps == -1) {
+            return false;
+        }
+        final int stepsEnrolled = mTotalSteps - mRemainingSteps;
+        return stepsEnrolled >= NUM_CENTER_TOUCHES;
+    }
+
+    @NonNull
+    PointF getNextGuidedEnrollmentPoint() {
+        final int index = mLocationsEnrolled - NUM_CENTER_TOUCHES;
+        return mGuidedEnrollmentPoints.get(index % mGuidedEnrollmentPoints.size());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index cd849e6..75a3621 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -27,6 +27,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.PointF;
 import android.graphics.RectF;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.text.TextUtils;
@@ -180,8 +181,9 @@
 
     boolean isValidTouch(float x, float y, float pressure) {
         // The X and Y coordinates of the sensor's center.
-        final float cx = mSensorRect.centerX();
-        final float cy = mSensorRect.centerY();
+        final PointF translation = mAnimationView.getTouchTranslation();
+        final float cx = mSensorRect.centerX() + translation.x;
+        final float cy = mSensorRect.centerY() + translation.y;
         // Radii along the X and Y axes.
         final float rx = (mSensorRect.right - mSensorRect.left) / 2.0f;
         final float ry = (mSensorRect.bottom - mSensorRect.top) / 2.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 04c4e97..1c5715c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -25,6 +25,7 @@
 import android.app.IWallpaperManager;
 import android.app.KeyguardManager;
 import android.app.NotificationManager;
+import android.app.StatsManager;
 import android.app.WallpaperManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.role.RoleManager;
@@ -321,6 +322,12 @@
 
     @Provides
     @Singleton
+    static StatsManager provideStatsManager(Context context) {
+        return context.getSystemService(StatsManager.class);
+    }
+
+    @Provides
+    @Singleton
     @Nullable
     static TelecomManager provideTelecomManager(Context context) {
         return context.getSystemService(TelecomManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
index bd3f5a6..7fb7d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
@@ -20,9 +20,13 @@
 import android.content.Context
 import android.content.Intent
 import android.text.TextUtils
+import android.util.Log
 import com.android.settingslib.media.MediaOutputConstants
 import javax.inject.Inject
 
+private const val TAG = "MediaOutputDlgReceiver"
+private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+
 /**
  * BroadcastReceiver for handling media output intent
  */
@@ -32,8 +36,13 @@
     override fun onReceive(context: Context, intent: Intent) {
         if (TextUtils.equals(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG,
                         intent.action)) {
-            mediaOutputDialogFactory.create(
-                    intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME), false)
+            val packageName: String? =
+                    intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
+            if (!TextUtils.isEmpty(packageName)) {
+                mediaOutputDialogFactory.create(packageName!!, false)
+            } else if (DEBUG) {
+                Log.e(TAG, "Unable to launch media output dialog. Package name is empty.")
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 70b7b04..4491cc1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -180,7 +180,7 @@
             }
         } else {
             for (int i = 0; i < mNavigationBars.size(); i++) {
-                mNavigationBars.get(i).onConfigurationChanged(newConfig);
+                mNavigationBars.valueAt(i).onConfigurationChanged(newConfig);
             }
         }
     }
@@ -193,7 +193,7 @@
                 if (navBar == null) {
                     continue;
                 }
-                NavigationBarView view = (NavigationBarView) mNavigationBars.get(i).getView();
+                NavigationBarView view = (NavigationBarView) navBar.getView();
                 if (view != null) {
                     view.updateStates();
                 }
@@ -399,7 +399,7 @@
             if (i > 0) {
                 pw.println();
             }
-            mNavigationBars.get(i).dump(pw);
+            mNavigationBars.valueAt(i).dump(pw);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 2ea8657..a69ec27 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -23,7 +23,6 @@
 import android.app.INotificationManager;
 import android.app.people.IPeopleManager;
 import android.app.people.PeopleSpaceTile;
-import android.appwidget.AppWidgetManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
@@ -34,12 +33,10 @@
 import android.util.Log;
 import android.view.ViewGroup;
 
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.systemui.R;
+import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
-import java.util.Collections;
 import java.util.List;
 
 import javax.inject.Inject;
@@ -54,15 +51,14 @@
 
     private ViewGroup mPeopleSpaceLayout;
     private IPeopleManager mPeopleManager;
+    private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
     private INotificationManager mNotificationManager;
     private PackageManager mPackageManager;
     private LauncherApps mLauncherApps;
     private Context mContext;
-    private AppWidgetManager mAppWidgetManager;
     private NotificationEntryManager mNotificationEntryManager;
     private int mAppWidgetId;
     private boolean mShowSingleConversation;
-    private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
 
     @Inject
     public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager) {
@@ -81,8 +77,8 @@
         mPackageManager = getPackageManager();
         mPeopleManager = IPeopleManager.Stub.asInterface(
                 ServiceManager.getService(Context.PEOPLE_SERVICE));
+        mPeopleSpaceWidgetManager = new PeopleSpaceWidgetManager(mContext);
         mLauncherApps = mContext.getSystemService(LauncherApps.class);
-        mAppWidgetManager = AppWidgetManager.getInstance(mContext);
         setTileViewsWithPriorityConversations();
         mAppWidgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID,
                 INVALID_APPWIDGET_ID);
@@ -142,29 +138,13 @@
                         + mAppWidgetId);
             }
         }
-
-        PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId);
-        int[] widgetIds = new int[mAppWidgetId];
-        // TODO: Populate new widget with existing conversation notification, if there is any.
-        PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
-                mPeopleManager);
-        if (mLauncherApps != null) {
-            try {
-                if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
-                mLauncherApps.cacheShortcuts(tile.getPackageName(),
-                        Collections.singletonList(tile.getId()),
-                        tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
-            } catch (Exception e) {
-                Log.w(TAG, "Exception caching shortcut:" + e);
-            }
-        }
+        mPeopleSpaceWidgetManager.addNewWidget(tile, mAppWidgetId);
         finishActivity();
     }
 
     /** Finish activity with a successful widget configuration result. */
     private void finishActivity() {
         if (DEBUG) Log.d(TAG, "Widget added!");
-        mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED);
         setActivityResult(RESULT_OK);
         finish();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 41080af..f9a16c1 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -220,7 +220,7 @@
     }
 
     @Nullable
-    private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
+    public static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
             AppWidgetManager appWidgetManager,
             Context context, int appWidgetId) {
         try {
@@ -230,7 +230,7 @@
             String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
             int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
             String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
-            if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) {
+            if (!validKey(shortcutId, pkg, userId)) {
                 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
                 shortcutId = sp.getString(String.valueOf(appWidgetId), null);
                 if (shortcutId == null) {
@@ -268,6 +268,17 @@
         }
     }
 
+    /** Returns stored widgets for the conversation specified. */
+    public static Set<String> getStoredWidgetIds(SharedPreferences sp, String shortcutId,
+            String packageName, int userId) {
+        if (shortcutId == null || packageName == null) {
+            return new HashSet<>();
+        }
+        String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+        return new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+    }
+
+
     /** Best-effort attempts to migrate existing users to the new storage format. */
     // TODO: Remove after sufficient time. Temporary migration storage for existing users.
     private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
@@ -286,7 +297,11 @@
                 if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
                 setStorageForTile(context, entry.get(), appWidgetId);
             } else {
-                Log.e(TAG, "Could not migrate user");
+                Log.e(TAG, "Could not migrate user. Delete old storage");
+                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+                SharedPreferences.Editor editor = sp.edit();
+                editor.remove(String.valueOf(appWidgetId));
+                editor.apply();
             }
         } catch (Exception e) {
             Log.e(TAG, "Could not query conversations");
@@ -320,17 +335,10 @@
     }
 
     /** Removes stored data when tile is deleted. */
-    public static void removeStorageForTile(Context context, int widgetId) {
-        SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
-                Context.MODE_PRIVATE);
-        String packageName = widgetSp.getString(PACKAGE_NAME, null);
-        String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
-        int userId = widgetSp.getInt(USER_ID, -1);
-
+    public static void removeStorageForTile(Context context, String key, int widgetId) {
         // Delete widgetId mapping to key.
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
         SharedPreferences.Editor editor = sp.edit();
-        String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
         Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
         storedWidgetIds.remove(String.valueOf(widgetId));
         editor.putStringSet(key, storedWidgetIds);
@@ -338,6 +346,8 @@
         editor.apply();
 
         // Delete all data specifically mapped to widgetId.
+        SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
+                Context.MODE_PRIVATE);
         SharedPreferences.Editor widgetEditor = widgetSp.edit();
         widgetEditor.remove(PACKAGE_NAME);
         widgetEditor.remove(USER_ID);
@@ -345,21 +355,6 @@
         widgetEditor.apply();
     }
 
-    /**
-     * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the
-     * requested update data.
-     */
-    public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId,
-            String packageName, int userId) {
-        SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId),
-                Context.MODE_PRIVATE);
-        String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING);
-        int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID);
-        String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING);
-        return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId)
-                && storedUserId == userId;
-    }
-
     static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context,
             List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) {
         if (notificationEntryManager == null) {
@@ -396,39 +391,8 @@
         return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn());
     }
 
-    /**
-     * If incoming notification changed tile, store the changes in the tile options.
-     */
-    public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager,
-            Context context,
-            StatusBarNotification sbn,
-            NotificationAction notificationAction, AppWidgetManager appWidgetManager,
-            int appWidgetId) {
-        PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
-                appWidgetId);
-        if (storedTile == null) {
-            if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
-            return;
-        }
-        if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
-            if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
-            storedTile = augmentTileFromNotification(context, storedTile, sbn);
-        } else {
-            if (DEBUG) {
-                Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
-            }
-            storedTile = storedTile
-                    .toBuilder()
-                    .setNotificationKey(null)
-                    .setNotificationContent(null)
-                    .setNotificationDataUri(null)
-                    .setNotificationCategory(null)
-                    .build();
-        }
-        updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
-    }
-
-    static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile,
+    /** Augments {@code tile} with the notification content from {@code sbn}. */
+    public static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile,
             StatusBarNotification sbn) {
         Notification notification = sbn.getNotification();
         if (notification == null) {
@@ -992,7 +956,7 @@
     }
 
     /** Update app widget options and the current view. */
-    private static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager,
+    public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager,
             Context context, int appWidgetId, PeopleSpaceTile tile) {
         updateAppWidgetOptions(appWidgetManager, appWidgetId, tile);
         RemoteViews views = createRemoteViews(context, tile, appWidgetId);
@@ -1065,10 +1029,19 @@
      *     <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
      * </ul>
      */
+    @Nullable
     public static String getKey(String shortcutId, String packageName, int userId) {
+        if (!validKey(shortcutId, packageName, userId)) {
+            return null;
+        }
         return shortcutId + "/" + userId + "/" + packageName;
     }
 
+    /** Returns whether the key is valid. */
+    public static boolean validKey(String shortcutId, String packageName, int userId) {
+        return !TextUtils.isEmpty(shortcutId) && !TextUtils.isEmpty(packageName) && userId >= 0;
+    }
+
     /** Returns the userId associated with a {@link PeopleSpaceTile} */
     public static int getUserId(PeopleSpaceTile tile) {
         return tile.getUserHandle().getIdentifier();
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index 9e5c786..22ee9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,12 +16,28 @@
 
 package com.android.systemui.people.widget;
 
+import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
+import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
+import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID;
+import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
+import static com.android.systemui.people.PeopleSpaceUtils.augmentTileFromNotification;
+import static com.android.systemui.people.PeopleSpaceUtils.getPeopleSpaceTile;
+import static com.android.systemui.people.PeopleSpaceUtils.getStoredWidgetIds;
+import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView;
+
 import android.app.NotificationChannel;
+import android.app.Person;
+import android.app.people.ConversationChannel;
 import android.app.people.IPeopleManager;
+import android.app.people.PeopleManager;
+import android.app.people.PeopleSpaceTile;
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.net.Uri;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.preference.PreferenceManager;
@@ -30,14 +46,18 @@
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.appwidget.IAppWidgetService;
-import com.android.systemui.R;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.systemui.people.PeopleSpaceUtils;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -49,52 +69,49 @@
     private static final String TAG = "PeopleSpaceWidgetMgr";
     private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
 
+    private final Object mLock = new Object();
     private final Context mContext;
-    private IAppWidgetService mAppWidgetService;
+    private LauncherApps mLauncherApps;
     private AppWidgetManager mAppWidgetManager;
-    private IPeopleManager mPeopleManager;
+    private IPeopleManager mIPeopleManager;
+    private SharedPreferences mSharedPrefs;
+    private PeopleManager mPeopleManager;
+    public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+    @GuardedBy("mLock")
+    public static Map<String, PeopleSpaceWidgetProvider.TileConversationListener> mListeners =
+            new HashMap<>();
 
     @Inject
-    public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
+    public PeopleSpaceWidgetManager(Context context) {
         if (DEBUG) Log.d(TAG, "constructor");
         mContext = context;
-        mAppWidgetService = appWidgetService;
         mAppWidgetManager = AppWidgetManager.getInstance(context);
-        mPeopleManager = IPeopleManager.Stub.asInterface(
+        mIPeopleManager = IPeopleManager.Stub.asInterface(
                 ServiceManager.getService(Context.PEOPLE_SERVICE));
-    }
-
-    /**
-     * Constructor used for testing.
-     */
-    @VisibleForTesting
-    protected PeopleSpaceWidgetManager(Context context) {
-        if (DEBUG) Log.d(TAG, "constructor");
-        mContext = context;
-        mAppWidgetService = IAppWidgetService.Stub.asInterface(
-                ServiceManager.getService(Context.APPWIDGET_SERVICE));
+        mLauncherApps = context.getSystemService(LauncherApps.class);
+        mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+        mPeopleManager = mContext.getSystemService(PeopleManager.class);
     }
 
     /**
      * AppWidgetManager setter used for testing.
      */
     @VisibleForTesting
-    protected void setAppWidgetManager(IAppWidgetService appWidgetService,
-            AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
-        mAppWidgetService = appWidgetService;
+    protected void setAppWidgetManager(
+            AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager,
+            PeopleManager peopleManager, LauncherApps launcherApps) {
         mAppWidgetManager = appWidgetManager;
+        mIPeopleManager = iPeopleManager;
         mPeopleManager = peopleManager;
+        mLauncherApps = launcherApps;
     }
 
     /**
      * Updates People Space widgets.
      */
-    public void updateWidgets() {
+    public void updateWidgets(int[] widgetIds) {
         try {
             if (DEBUG) Log.d(TAG, "updateWidgets called");
-            int[] widgetIds = mAppWidgetService.getAppWidgetIds(
-                    new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
-            );
             if (widgetIds.length == 0) {
                 if (DEBUG) Log.d(TAG, "no widgets to update");
                 return;
@@ -103,14 +120,11 @@
             if (DEBUG) Log.d(TAG, "updating " + widgetIds.length + " widgets");
             boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
-
             if (showSingleConversation) {
-                PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
-                        mAppWidgetManager, mPeopleManager);
-            } else {
-                mAppWidgetService
-                        .notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
-                                R.id.widget_list_view);
+                synchronized (mLock) {
+                    PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
+                            mAppWidgetManager, mIPeopleManager);
+                }
             }
         } catch (Exception e) {
             Log.e(TAG, "Exception: " + e);
@@ -121,9 +135,9 @@
      * Check if any existing People tiles match the incoming notification change, and store the
      * change in the tile if so.
      */
-    public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
+    public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn,
             PeopleSpaceUtils.NotificationAction notificationAction) {
-        if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
+        if (DEBUG) Log.d(TAG, "updateWidgetsWithNotificationChanged called");
         boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
         if (!showSingleConversation) {
@@ -135,26 +149,22 @@
                 if (DEBUG) Log.d(TAG, "Sbn shortcut id is null");
                 return;
             }
-            int[] widgetIds = mAppWidgetService.getAppWidgetIds(
+            int[] widgetIds = mAppWidgetManager.getAppWidgetIds(
                     new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
             );
             if (widgetIds.length == 0) {
                 Log.d(TAG, "No app widget ids returned");
                 return;
             }
-            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-            int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier();
-            String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
-            Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
-            if (storedWidgetIds.isEmpty()) {
-                Log.d(TAG, "No stored widget ids");
-                return;
-            }
-            for (String widgetIdString : storedWidgetIds) {
-                int widgetId = Integer.parseInt(widgetIdString);
-                if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
-                PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext,
-                        sbn, notificationAction, mAppWidgetManager, widgetId);
+            synchronized (mLock) {
+                Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, sbnShortcutId,
+                        sbn.getPackageName(),
+                        UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier());
+                for (String widgetIdString : storedWidgetIds) {
+                    int widgetId = Integer.parseInt(widgetIdString);
+                    if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
+                    updateStorageAndViewWithNotificationData(sbn, notificationAction, widgetId);
+                }
             }
         } catch (Exception e) {
             Log.e(TAG, "Exception: " + e);
@@ -162,6 +172,91 @@
     }
 
     /**
+     * Update the tiles associated with the incoming conversation update.
+     */
+    public void updateWidgetsWithConversationChanged(ConversationChannel conversation) {
+        ShortcutInfo info = conversation.getShortcutInfo();
+        synchronized (mLock) {
+            Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, info.getId(),
+                    info.getPackage(),
+                    info.getUserId());
+            for (String widgetIdString : storedWidgetIds) {
+                if (DEBUG) {
+                    Log.d(TAG,
+                            "Conversation update for widget " + widgetIdString + " , "
+                                    + info.getLabel());
+                }
+                updateStorageAndViewWithConversationData(conversation,
+                        Integer.valueOf(widgetIdString));
+            }
+        }
+    }
+
+    /**
+     * Update {@code appWidgetId} with the new data provided by {@code conversation}.
+     */
+    private void updateStorageAndViewWithConversationData(ConversationChannel conversation,
+            int appWidgetId) {
+        PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager,
+                mContext,
+                appWidgetId);
+        if (storedTile == null) {
+            if (DEBUG) Log.d(TAG, "Could not find stored tile to add conversation to");
+            return;
+        }
+        ShortcutInfo info = conversation.getShortcutInfo();
+        Uri uri = null;
+        if (info.getPersons() != null && info.getPersons().length > 0) {
+            Person person = info.getPersons()[0];
+            uri = person.getUri() == null ? null : Uri.parse(person.getUri());
+        }
+        storedTile = storedTile.toBuilder()
+                .setUserName(info.getLabel())
+                .setUserIcon(
+                        PeopleSpaceTile.convertDrawableToIcon(mLauncherApps.getShortcutIconDrawable(
+                                info, 0))
+                )
+                .setContactUri(uri)
+                .setStatuses(conversation.getStatuses())
+                .setLastInteractionTimestamp(conversation.getLastEventTimestamp())
+                .setIsImportantConversation(conversation.getParentNotificationChannel() != null
+                        && conversation.getParentNotificationChannel().isImportantConversation())
+                .build();
+        updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile);
+    }
+
+    /**
+     * Update {@code appWidgetId} with the new data provided by {@code sbn}.
+     */
+    private void updateStorageAndViewWithNotificationData(
+            StatusBarNotification sbn,
+            PeopleSpaceUtils.NotificationAction notificationAction,
+            int appWidgetId) {
+        PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager,
+                mContext,
+                appWidgetId);
+        if (storedTile == null) {
+            if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
+            return;
+        }
+        if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
+            if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
+            storedTile = augmentTileFromNotification(mContext, storedTile, sbn);
+        } else {
+            if (DEBUG) {
+                Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
+            }
+            storedTile = storedTile
+                    .toBuilder()
+                    .setNotificationKey(null)
+                    .setNotificationContent(null)
+                    .setNotificationDataUri(null)
+                    .build();
+        }
+        updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile);
+    }
+
+    /**
      * Attaches the manager to the pipeline, making it ready to receive events. Should only be
      * called once.
      */
@@ -174,8 +269,7 @@
         @Override
         public void onNotificationPosted(
                 StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
-            if (DEBUG) Log.d(TAG, "onNotificationPosted");
-            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
+            updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
         }
 
         @Override
@@ -183,8 +277,7 @@
                 StatusBarNotification sbn,
                 NotificationListenerService.RankingMap rankingMap
         ) {
-            if (DEBUG) Log.d(TAG, "onNotificationRemoved");
-            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
+            updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
         }
 
         @Override
@@ -192,8 +285,7 @@
                 StatusBarNotification sbn,
                 NotificationListenerService.RankingMap rankingMap,
                 int reason) {
-            if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason);
-            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
+            updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
         }
 
         @Override
@@ -204,7 +296,6 @@
         @Override
         public void onNotificationsInitialized() {
             if (DEBUG) Log.d(TAG, "onNotificationsInitialized");
-            updateWidgets();
         }
 
         @Override
@@ -213,11 +304,131 @@
                 UserHandle user,
                 NotificationChannel channel,
                 int modificationType) {
-            if (DEBUG) Log.d(TAG, "onNotificationChannelModified");
             if (channel.isConversation()) {
-                updateWidgets();
+                updateWidgets(mAppWidgetManager.getAppWidgetIds(
+                        new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
+                ));
             }
         }
     };
 
-}
\ No newline at end of file
+    /** Adds {@code tile} mapped to {@code appWidgetId}. */
+    public void addNewWidget(PeopleSpaceTile tile, int appWidgetId) {
+        mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED);
+        synchronized (mLock) {
+            if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getUserName());
+            PeopleSpaceUtils.setStorageForTile(mContext, tile, appWidgetId);
+        }
+        try {
+            if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
+            mLauncherApps.cacheShortcuts(tile.getPackageName(),
+                    Collections.singletonList(tile.getId()),
+                    tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
+        } catch (Exception e) {
+            Log.w(TAG, "Exception caching shortcut:" + e);
+        }
+        PeopleSpaceWidgetProvider provider = new PeopleSpaceWidgetProvider();
+        provider.onUpdate(mContext, mAppWidgetManager, new int[]{appWidgetId});
+    }
+
+    /** Registers a conversation listener for {@code appWidgetId} if not already registered. */
+    public void registerConversationListenerIfNeeded(int widgetId,
+            PeopleSpaceWidgetProvider.TileConversationListener newListener) {
+        // Retrieve storage needed for registration.
+        String packageName;
+        String shortcutId;
+        int userId;
+        String key;
+        synchronized (mLock) {
+            SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId),
+                    Context.MODE_PRIVATE);
+            packageName = widgetSp.getString(PACKAGE_NAME, null);
+            shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+            userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+            key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+            if (key == null) {
+                if (DEBUG) Log.e(TAG, "Could not register " + widgetId);
+                return;
+            }
+        }
+        synchronized (mListeners) {
+            if (mListeners.containsKey(key)) {
+                if (DEBUG) Log.d(TAG, "Already registered listener");
+                return;
+            }
+            if (DEBUG) Log.d(TAG, "Register listener for " + widgetId + " with " + key);
+            mListeners.put(key, newListener);
+        }
+        mPeopleManager.registerConversationListener(packageName,
+                userId,
+                shortcutId, newListener,
+                mContext.getMainExecutor());
+    }
+
+    /** Deletes all storage, listeners, and caching for {@code appWidgetIds}. */
+    public void deleteWidgets(int[] appWidgetIds) {
+        for (int widgetId : appWidgetIds) {
+            if (DEBUG) Log.d(TAG, "Widget removed: " + widgetId);
+            mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
+            // Retrieve storage needed for widget deletion.
+            String packageName;
+            String shortcutId;
+            int userId;
+            String key;
+            Set<String> storedWidgetIdsForKey;
+            synchronized (mLock) {
+                SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId),
+                        Context.MODE_PRIVATE);
+                packageName = widgetSp.getString(PACKAGE_NAME, null);
+                shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+                userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+                key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+                if (key == null) {
+                    if (DEBUG) Log.e(TAG, "Could not delete " + widgetId);
+                    return;
+                }
+                storedWidgetIdsForKey = new HashSet<>(
+                        mSharedPrefs.getStringSet(key, new HashSet<>()));
+            }
+            synchronized (mLock) {
+                PeopleSpaceUtils.removeStorageForTile(mContext, key, widgetId);
+            }
+            // If another tile with the conversation is still stored, we need to keep the listener.
+            if (DEBUG) Log.d(TAG, "Stored widget IDs: " + storedWidgetIdsForKey.toString());
+            if (storedWidgetIdsForKey.contains(String.valueOf(widgetId))
+                    && storedWidgetIdsForKey.size() == 1) {
+                if (DEBUG) Log.d(TAG, "Remove caching and listener");
+                unregisterConversationListener(key, widgetId);
+                uncacheConversationShortcut(shortcutId, packageName, userId);
+            }
+        }
+    }
+
+    /** Unregisters the conversation listener for {@code appWidgetId}. */
+    private void unregisterConversationListener(String key, int appWidgetId) {
+        PeopleSpaceWidgetProvider.TileConversationListener registeredListener;
+        synchronized (mListeners) {
+            registeredListener = mListeners.get(key);
+            if (registeredListener == null) {
+                if (DEBUG) Log.d(TAG, "Cannot find listener to unregister");
+                return;
+            }
+            if (DEBUG) Log.d(TAG, "Unregister listener for " + appWidgetId + " with " + key);
+            mListeners.remove(key);
+        }
+        mPeopleManager.unregisterConversationListener(registeredListener);
+    }
+
+    /** Uncaches the conversation shortcut. */
+    private void uncacheConversationShortcut(String shortcutId, String packageName, int userId) {
+        try {
+            if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId);
+            mLauncherApps.uncacheShortcuts(packageName,
+                    Collections.singletonList(shortcutId),
+                    UserHandle.of(userId),
+                    LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
+        } catch (Exception e) {
+            Log.d(TAG, "Exception uncaching 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 90baf56..c0c1847 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -16,31 +16,17 @@
 
 package com.android.systemui.people.widget;
 
-import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
-import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID;
-import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
-
-import android.app.PendingIntent;
-import android.app.people.IPeopleManager;
+import android.annotation.NonNull;
+import android.app.people.ConversationChannel;
+import android.app.people.PeopleManager;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProvider;
 import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.LauncherApps;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.Log;
-import android.widget.RemoteViews;
 
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.systemui.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.people.PeopleSpaceUtils;
 
-import java.util.Collections;
-
 /** People Space Widget Provider class. */
 public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
     private static final String TAG = "PeopleSpaceWidgetPvd";
@@ -50,7 +36,26 @@
     public static final String EXTRA_PACKAGE_NAME = "extra_package_name";
     public static final String EXTRA_USER_HANDLE = "extra_user_handle";
 
-    public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+    public PeopleSpaceWidgetManager peopleSpaceWidgetManager;
+
+    /** Listener for the shortcut data changes. */
+    public class TileConversationListener implements PeopleManager.ConversationListener {
+
+        @Override
+        public void onConversationUpdate(@NonNull ConversationChannel conversation) {
+            if (DEBUG) {
+                Log.d(TAG,
+                        "Received updated conversation: "
+                                + conversation.getShortcutInfo().getLabel());
+            }
+            if (peopleSpaceWidgetManager == null) {
+                // This shouldn't happen since onUpdate is called at reboot.
+                Log.e(TAG, "Skipping conversation update: WidgetManager uninitialized");
+                return;
+            }
+            peopleSpaceWidgetManager.updateWidgetsWithConversationChanged(conversation);
+        }
+    }
 
     /** Called when widget updates. */
     @Override
@@ -58,70 +63,32 @@
         super.onUpdate(context, appWidgetManager, appWidgetIds);
 
         if (DEBUG) Log.d(TAG, "onUpdate called");
-        boolean showSingleConversation = Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
-        if (showSingleConversation) {
-            PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds,
-                    appWidgetManager, IPeopleManager.Stub.asInterface(
-                            ServiceManager.getService(Context.PEOPLE_SERVICE)));
-            return;
-        }
-        // Perform this loop procedure for each App Widget that belongs to this provider
+        ensurePeopleSpaceWidgetManagerInitialized(context);
+        peopleSpaceWidgetManager.updateWidgets(appWidgetIds);
         for (int appWidgetId : appWidgetIds) {
-            RemoteViews views =
-                    new RemoteViews(context.getPackageName(), R.layout.people_space_widget);
+            PeopleSpaceWidgetProvider.TileConversationListener
+                    newListener = new PeopleSpaceWidgetProvider.TileConversationListener();
+            peopleSpaceWidgetManager.registerConversationListenerIfNeeded(appWidgetId,
+                    newListener);
+        }
+        return;
+    }
 
-            Intent intent = new Intent(context, PeopleSpaceWidgetService.class);
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
-            views.setRemoteAdapter(R.id.widget_list_view, intent);
-
-            Intent activityIntent = new Intent(context, LaunchConversationActivity.class);
-            activityIntent.addFlags(
-                    Intent.FLAG_ACTIVITY_NEW_TASK
-                            | Intent.FLAG_ACTIVITY_CLEAR_TASK
-                            | Intent.FLAG_ACTIVITY_NO_HISTORY
-                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            PendingIntent pendingIntent = PendingIntent.getActivity(
-                    context,
-                    appWidgetId,
-                    activityIntent,
-                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
-            views.setPendingIntentTemplate(R.id.widget_list_view, pendingIntent);
-
-            // Tell the AppWidgetManager to perform an update on the current app widget
-            appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_list_view);
-            appWidgetManager.updateAppWidget(appWidgetId, views);
+    private void ensurePeopleSpaceWidgetManagerInitialized(Context context) {
+        if (peopleSpaceWidgetManager == null) {
+            peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context);
         }
     }
 
     @Override
     public void onDeleted(Context context, int[] appWidgetIds) {
         super.onDeleted(context, appWidgetIds);
-        LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+        ensurePeopleSpaceWidgetManagerInitialized(context);
+        peopleSpaceWidgetManager.deleteWidgets(appWidgetIds);
+    }
 
-        for (int widgetId : appWidgetIds) {
-            if (DEBUG) Log.d(TAG, "Widget removed");
-            mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
-            if (launcherApps != null) {
-                SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
-                        Context.MODE_PRIVATE);
-                String packageName = widgetSp.getString(PACKAGE_NAME, null);
-                String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
-                int userId = widgetSp.getInt(USER_ID, -1);
-
-                if (packageName != null && shortcutId != null && userId != -1) {
-                    try {
-                        if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId);
-                        launcherApps.uncacheShortcuts(packageName,
-                                Collections.singletonList(shortcutId),
-                                UserHandle.of(userId),
-                                LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
-                    } catch (Exception e) {
-                        Log.d(TAG, "Exception uncaching shortcut:" + e);
-                    }
-                }
-            }
-            PeopleSpaceUtils.removeStorageForTile(context, widgetId);
-        }
+    @VisibleForTesting
+    public void setPeopleSpaceWidgetManager(PeopleSpaceWidgetManager manager) {
+        peopleSpaceWidgetManager = manager;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index 7679d48..8ec9b68 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
@@ -47,7 +47,7 @@
     context: Context,
     private val list: List<PrivacyElement>,
     activityStarter: (String, Int) -> Unit
-) : SystemUIDialog(context, R.style.ScreenRecord) {
+) : SystemUIDialog(context, R.style.PrivacyDialog) {
 
     private val dismissListeners = mutableListOf<WeakReference<OnDialogDismissed>>()
     private val dismissed = AtomicBoolean(false)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 38e2ba4..33ca7d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -259,23 +259,30 @@
         mIcon.setIcon(state, allowAnimations);
         setContentDescription(state.contentDescription);
         final StringBuilder stateDescription = new StringBuilder();
+        String text = "";
         switch (state.state) {
             case Tile.STATE_UNAVAILABLE:
-                stateDescription.append(mContext.getString(R.string.tile_unavailable));
+                text = mContext.getString(R.string.tile_unavailable);
                 break;
             case Tile.STATE_INACTIVE:
                 if (state instanceof QSTile.BooleanState) {
-                    stateDescription.append(mContext.getString(R.string.switch_bar_off));
+                    text = mContext.getString(R.string.switch_bar_off);
                 }
                 break;
             case Tile.STATE_ACTIVE:
                 if (state instanceof QSTile.BooleanState) {
-                    stateDescription.append(mContext.getString(R.string.switch_bar_on));
+                    text = mContext.getString(R.string.switch_bar_on);
                 }
                 break;
             default:
                 break;
         }
+        if (!TextUtils.isEmpty(text)) {
+            stateDescription.append(text);
+            if (TextUtils.isEmpty(state.secondaryLabel)) {
+                state.secondaryLabel = text;
+            }
+        }
         if (!TextUtils.isEmpty(state.stateDescription)) {
             stateDescription.append(", ");
             stateDescription.append(state.stateDescription);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 5b2a7e7..e7d4283 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -662,14 +662,29 @@
         }
 
         @Override
-        public void startIntent(PendingIntent intent, int stage, int position, Bundle options) {
+        public void startIntent(PendingIntent intent, Intent fillInIntent,
+                int stage, int position, Bundle options) {
             if (!verifyCaller("startIntent")) {
                 return;
             }
             final long token = Binder.clearCallingIdentity();
             try {
                 mSplitScreenOptional.ifPresent(s ->
-                        s.startIntent(intent, stage, position, options));
+                        s.startIntent(intent, mContext, fillInIntent, stage, position, options));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void removeFromSideStage(int taskId) {
+            if (!verifyCaller("removeFromSideStage")) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mSplitScreenOptional.ifPresent(
+                        s -> s.removeFromSideStage(taskId));
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -789,10 +804,10 @@
         }
 
         @Override
-        public void onTaskStageChanged(int taskId, int stage) {
+        public void onTaskStageChanged(int taskId, int stage, boolean visible) {
             try {
                 if (mISplitScreenListener != null) {
-                    mISplitScreenListener.onTaskStageChanged(taskId, stage);
+                    mISplitScreenListener.onTaskStageChanged(taskId, stage, visible);
                 }
             } catch (RemoteException e) {
                 Log.e(TAG_OPS, "onTaskStageChanged", e);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 1975821..bd46ffe 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -351,7 +351,8 @@
 
         Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
                 .setSmallIcon(R.drawable.ic_screenrecord)
-                .setContentTitle(getResources().getString(R.string.screenrecord_save_message))
+                .setContentTitle(getResources().getString(R.string.screenrecord_save_title))
+                .setContentText(getResources().getString(R.string.screenrecord_save_text))
                 .setContentIntent(PendingIntent.getActivity(
                         this,
                         REQUEST_CODE,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 6cdf6ab..58a54f6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -82,7 +82,7 @@
                 dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
         if (intent != null) {
             final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
-                    mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED, null, UserHandle.CURRENT);
+                    mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
             b.setContentIntent(pendingIntent);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index d2ddd21..5e8245f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -527,7 +527,7 @@
         handleCustomTransformHeight(view, expandingAnimated, iconState);
 
         float fullTransitionAmount;
-        float transitionAmount;
+        float iconTransitionAmount;
         float contentTransformationAmount;
         float shelfStart = getTranslationY();
         boolean fullyInOrOut = true;
@@ -549,18 +549,19 @@
                 fullTransitionAmount = 1.0f - interpolatedAmount;
 
                 if (isLastChild) {
-                    // If it's the last child we should use all of the notification to transform
-                    // instead of just to the icon, since that can be quite low.
-                    transitionAmount = (shelfStart - viewStart) / transformDistance;
+                    // Reduce icon transform distance to completely fade in shelf icon
+                    // by the time the notification icon fades out, and vice versa
+                    iconTransitionAmount = (shelfStart - viewStart)
+                            / (iconTransformStart - viewStart);
                 } else {
-                    transitionAmount = (shelfStart - iconTransformStart) / transformDistance;
+                    iconTransitionAmount = (shelfStart - iconTransformStart) / transformDistance;
                 }
-                transitionAmount = MathUtils.constrain(transitionAmount, 0.0f, 1.0f);
-                transitionAmount = 1.0f - transitionAmount;
+                iconTransitionAmount = MathUtils.constrain(iconTransitionAmount, 0.0f, 1.0f);
+                iconTransitionAmount = 1.0f - iconTransitionAmount;
                 fullyInOrOut = false;
             } else {
                 fullTransitionAmount = 1.0f;
-                transitionAmount = 1.0f;
+                iconTransitionAmount = 1.0f;
             }
 
             // Transforming the content
@@ -569,7 +570,7 @@
             contentTransformationAmount = 1.0f - contentTransformationAmount;
         } else {
             fullTransitionAmount = 0.0f;
-            transitionAmount = 0.0f;
+            iconTransitionAmount = 0.0f;
             contentTransformationAmount = 0.0f;
         }
         if (iconState != null && fullyInOrOut && !expandingAnimated && iconState.isLastExpandIcon) {
@@ -585,7 +586,7 @@
         view.setContentTransformationAmount(contentTransformationAmount, isLastChild);
 
         // Update the positioning of the icon
-        updateIconPositioning(view, transitionAmount, fullTransitionAmount,
+        updateIconPositioning(view, iconTransitionAmount, fullTransitionAmount,
                 transformDistance, scrolling, scrollingFast, expandingAnimated, isLastChild);
 
         return fullTransitionAmount;
@@ -679,8 +680,7 @@
                 || iconState.useLinearTransitionAmount) {
             transitionAmount = iconTransitionAmount;
         } else {
-            // We take the clamped position instead
-            transitionAmount = clampedAmount;
+            transitionAmount = iconTransitionAmount;
             iconState.needsCannedAnimation = iconState.clampedAppearAmount != clampedAmount
                     && !mNoAnimationsInThisFrame;
         }
@@ -689,8 +689,7 @@
                 ? fullTransitionAmount
                 : transitionAmount;
         iconState.clampedAppearAmount = clampedAmount;
-        setIconTransformationAmount(view, transitionAmount, iconTransformDistance,
-                clampedAmount != transitionAmount, isLastChild);
+        setIconTransformationAmount(view, transitionAmount, isLastChild);
     }
 
     private boolean isTargetClipped(ExpandableView view) {
@@ -708,7 +707,7 @@
     }
 
     private void setIconTransformationAmount(ExpandableView view,
-            float transitionAmount, float iconTransformDistance, boolean usingLinearInterpolation,
+            float transitionAmount,
             boolean isLastChild) {
         if (!(view instanceof ExpandableNotificationRow)) {
             return;
@@ -720,42 +719,13 @@
         View rowIcon = row.getShelfTransformationTarget();
 
         // Let's resolve the relative positions of the icons
-        float notificationIconSize = 0.0f;
-        int iconTopPadding;
         int iconStartPadding;
         if (rowIcon != null) {
-            iconTopPadding = row.getRelativeTopPadding(rowIcon);
             iconStartPadding = row.getRelativeStartPadding(rowIcon);
-            notificationIconSize = rowIcon.getHeight();
         } else {
-            iconTopPadding = mIconAppearTopPadding;
             iconStartPadding = 0;
         }
-
-        float shelfIconSize = mAmbientState.isFullyHidden() ? mHiddenShelfIconSize : mIconSize;
-        shelfIconSize = shelfIconSize * icon.getIconScale();
-
-        // Get the icon correctly positioned in Y
-        float notificationIconPositionY = row.getTranslationY() + row.getContentTranslation();
-        float targetYPosition = 0;
         boolean stayingInShelf = row.isInShelf() && !row.isTransformingIntoShelf();
-        if (usingLinearInterpolation && !stayingInShelf) {
-            // If we interpolate from the notification position, this might lead to a slightly
-            // odd interpolation, since the notification position changes as well.
-            // Let's instead interpolate directly to the top left of the notification
-            targetYPosition =  NotificationUtils.interpolate(
-                    Math.min(notificationIconPositionY + mIconAppearTopPadding
-                            - getTranslationY(), 0),
-                    0,
-                    transitionAmount);
-        }
-        notificationIconPositionY += iconTopPadding;
-        float shelfIconPositionY = getTranslationY() + icon.getTop();
-        shelfIconPositionY += (icon.getHeight() - shelfIconSize) / 2.0f;
-        float iconYTranslation = NotificationUtils.interpolate(
-                notificationIconPositionY - shelfIconPositionY,
-                targetYPosition,
-                transitionAmount);
 
         // Get the icon correctly positioned in X
         // Even in RTL it's the left, since we're inverting the location in post
@@ -767,28 +737,19 @@
                 transitionAmount);
 
         // Let's handle the case that there's no Icon
-        float alpha = 1.0f;
         boolean noIcon = !row.isShowingIcon();
         if (noIcon) {
             // The view currently doesn't have an icon, lets transform it in!
-            alpha = transitionAmount;
-            notificationIconSize = shelfIconSize / 2.0f;
             iconXTranslation = mShelfIcons.getActualPaddingStart();
         }
-        // The notification size is different from the size in the shelf / statusbar
-        float newSize = NotificationUtils.interpolate(notificationIconSize, shelfIconSize,
-                transitionAmount);
         if (iconState != null) {
-            iconState.scaleX = newSize / shelfIconSize;
-            iconState.scaleY = iconState.scaleX;
             iconState.hidden = transitionAmount == 0.0f && !iconState.isAnimating(icon);
             boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
             if (isAppearing) {
                 iconState.hidden = true;
                 iconState.iconAppearAmount = 0.0f;
             }
-            iconState.alpha = alpha;
-            iconState.yTranslation = iconYTranslation;
+            iconState.alpha = transitionAmount;
             iconState.xTranslation = iconXTranslation;
             if (stayingInShelf) {
                 iconState.iconAppearAmount = 1.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index d1ab7ea..004cf99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -180,9 +180,6 @@
                                 }
                         if (changed) {
                             notificationGroupManager.updateIsolation(entry)
-                            // ensure that the conversation icon isn't hidden
-                            // (ex: if it was showing in the shelf)
-                            entry.row?.updateIconVisibilities()
                         }
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 8a22b9f..dbd8580 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -176,7 +176,6 @@
     private int mBucket = BUCKET_ALERTING;
     @Nullable private Long mPendingAnimationDuration;
     private boolean mIsMarkedForUserTriggeredMovement;
-    private boolean mShelfIconVisible;
     private boolean mIsAlerting;
 
     public boolean mRemoteEditImeVisible;
@@ -417,7 +416,6 @@
     //TODO: This will go away when we have a way to bind an entry to a row
     public void setRow(ExpandableNotificationRow row) {
         this.row = row;
-        updateShelfIconVisibility();
     }
 
     public ExpandableNotificationRowController getRowController() {
@@ -938,19 +936,6 @@
         return mIsMarkedForUserTriggeredMovement;
     }
 
-    /** Whether or not the icon for this notification is visible in the shelf. */
-    public void setShelfIconVisible(boolean shelfIconVisible) {
-        if (row == null) return;
-        mShelfIconVisible = shelfIconVisible;
-        updateShelfIconVisibility();
-    }
-
-    private void updateShelfIconVisibility() {
-        if (row != null) {
-            row.setShelfIconVisible(mShelfIconVisible);
-        }
-    }
-
     /**
      * Mark this entry for movement triggered by a user action (ex: changing the priorirty of a
      * conversation). This can then be used for custom animations.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index ba45f9a..5375ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -95,11 +95,6 @@
         // Construct the shelf icon view.
         val shelfIcon = iconBuilder.createIconView(entry)
         shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
-
-        // TODO: This doesn't belong here
-        shelfIcon.setOnVisibilityChangedListener { newVisibility: Int ->
-            entry.setShelfIconVisible(newVisibility == View.VISIBLE)
-        }
         shelfIcon.visibility = View.INVISIBLE
 
         // Construct the aod icon view.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 1251b58..0f23b77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -331,7 +331,6 @@
     private boolean mHeadsupDisappearRunning;
     private View mChildAfterViewWhenDismissed;
     private View mGroupParentWhenDismissed;
-    private boolean mShelfIconVisible;
     private boolean mAboveShelf;
     private OnUserInteractionCallback mOnUserInteractionCallback;
     private NotificationGutsManager mNotificationGutsManager;
@@ -568,7 +567,6 @@
         // The public layouts expand button is always visible
         mPublicLayout.updateExpandButtons(true);
         updateLimits();
-        updateIconVisibilities();
         updateShelfIconColor();
         updateRippleAllowed();
         if (mUpdateBackgroundOnUpdate) {
@@ -883,7 +881,6 @@
             setDistanceToTopRoundness(NO_ROUNDNESS);
             mNotificationParent.updateBackgroundForGroupState();
         }
-        updateIconVisibilities();
         updateBackgroundClipping();
     }
 
@@ -1481,21 +1478,6 @@
         return getShelfTransformationTarget() != null;
     }
 
-    /**
-     * Set the icons to be visible of this notification.
-     */
-    public void setShelfIconVisible(boolean iconVisible) {
-        if (iconVisible != mShelfIconVisible) {
-            mShelfIconVisible = iconVisible;
-            updateIconVisibilities();
-        }
-    }
-
-    @Override
-    protected void onBelowSpeedBumpChanged() {
-        updateIconVisibilities();
-    }
-
     @Override
     protected void updateContentTransformation() {
         if (mExpandAnimationRunning) {
@@ -1522,18 +1504,6 @@
         }
     }
 
-    /** Refreshes the visibility of notification icons */
-    public void updateIconVisibilities() {
-        // The shelf icon is never hidden for children in groups
-        boolean visible = !isChildInGroup() && mShelfIconVisible;
-        for (NotificationContentView l : mLayouts) {
-            l.setShelfIconVisible(visible);
-        }
-        if (mChildrenContainer != null) {
-            mChildrenContainer.setShelfIconVisible(visible);
-        }
-    }
-
     public void setIsLowPriority(boolean isLowPriority) {
         mIsLowPriority = isLowPriority;
         mPrivateLayout.setIsLowPriority(isLowPriority);
@@ -2052,8 +2022,7 @@
                 mNotificationLaunchHeight,
                 zProgress);
         setTranslationZ(translationZ);
-        float extraWidthForClipping = params.getWidth() - getWidth()
-                + MathUtils.lerp(0, mOutlineRadius * 2, params.getProgress());
+        float extraWidthForClipping = params.getWidth() - getWidth();
         setExtraWidthForClipping(extraWidthForClipping);
         int top = params.getTop();
         float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index b0b91bd..88cf2db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -172,7 +172,6 @@
     private int mContentHeightAtAnimationStart = UNDEFINED;
     private boolean mFocusOnVisibilityChange;
     private boolean mHeadsUpAnimatingAway;
-    private boolean mShelfIconVisible;
     private int mClipBottomAmount;
     private boolean mIsLowPriority;
     private boolean mIsContentExpandable;
@@ -1739,23 +1738,6 @@
         mFocusOnVisibilityChange = true;
     }
 
-    public void setShelfIconVisible(boolean iconsVisible) {
-        mShelfIconVisible = iconsVisible;
-        updateIconVisibilities();
-    }
-
-    private void updateIconVisibilities() {
-        if (mContractedWrapper != null) {
-            mContractedWrapper.setShelfIconVisible(mShelfIconVisible);
-        }
-        if (mHeadsUpWrapper != null) {
-            mHeadsUpWrapper.setShelfIconVisible(mShelfIconVisible);
-        }
-        if (mExpandedWrapper != null) {
-            mExpandedWrapper.setShelfIconVisible(mShelfIconVisible);
-        }
-    }
-
     @Override
     public void onVisibilityAggregated(boolean isVisible) {
         super.onVisibilityAggregated(isVisible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 7248bce..151840a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -44,9 +44,10 @@
 
     private void updateImageTag(StatusBarNotification notification) {
         final Bundle extras = notification.getNotification().extras;
-        Icon overRiddenIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG);
-        if (overRiddenIcon != null) {
-            mPicture.setTag(ImageTransformState.ICON_TAG, overRiddenIcon);
+        Icon overriddenIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG);
+        if (overriddenIcon != null) {
+            mRightIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon);
+            mLeftIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 905bccf..fb0fdcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -132,20 +132,6 @@
         )
     }
 
-    override fun setShelfIconVisible(visible: Boolean) {
-        if (conversationLayout.isImportantConversation) {
-            if (conversationIconView.visibility != View.GONE) {
-                conversationIconView.isForceHidden = visible
-                // We don't want the small icon to be hidden by the extended wrapper, as force
-                // hiding the conversationIcon will already do that via its listener.
-                return
-            }
-        } else {
-            conversationIconView.isForceHidden = false
-        }
-        super.setShelfIconVisible(visible)
-    }
-
     override fun getShelfTransformationTarget(): View? =
             if (conversationLayout.isImportantConversation)
                 if (conversationIconView.visibility != View.GONE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index eb79e3c..bdafd23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -313,12 +313,6 @@
     }
 
     @Override
-    public void setShelfIconVisible(boolean visible) {
-        super.setShelfIconVisible(visible);
-        mIcon.setForceHidden(visible);
-    }
-
-    @Override
     public TransformState getCurrentState(int fadingView) {
         return mTransformationHelper.getCurrentState(fadingView);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index e9934c0..e0b5812 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -51,7 +51,8 @@
 public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper {
 
     private final int mFullHeaderTranslation;
-    protected ImageView mPicture;
+    protected ImageView mRightIcon;
+    protected ImageView mLeftIcon;
     private ProgressBar mProgressBar;
     private TextView mTitle;
     private TextView mText;
@@ -140,9 +141,14 @@
     }
 
     private void resolveTemplateViews(StatusBarNotification notification) {
-        mPicture = mView.findViewById(com.android.internal.R.id.right_icon);
-        if (mPicture != null) {
-            mPicture.setTag(ImageTransformState.ICON_TAG,
+        mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon);
+        if (mRightIcon != null) {
+            mRightIcon.setTag(ImageTransformState.ICON_TAG,
+                    notification.getNotification().getLargeIcon());
+        }
+        mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon);
+        if (mLeftIcon != null) {
+            mLeftIcon.setTag(ImageTransformState.ICON_TAG,
                     notification.getNotification().getLargeIcon());
         }
         mTitle = mView.findViewById(com.android.internal.R.id.title);
@@ -240,9 +246,9 @@
         resolveTemplateViews(row.getEntry().getSbn());
         super.onContentUpdated(row);
         // With the modern templates, a large icon visually overlaps the header, so we can't
-        // simply hide the header -- just show the
+        // hide the header, we must show it.
         mCanHideHeader = mNotificationHeader != null
-                && (mPicture == null || mPicture.getVisibility() != VISIBLE);
+                && (mRightIcon == null || mRightIcon.getVisibility() != VISIBLE);
         if (row.getHeaderVisibleAmount() != DEFAULT_HEADER_VISIBLE_AMOUNT) {
             setHeaderVisibleAmount(row.getHeaderVisibleAmount());
         }
@@ -260,14 +266,15 @@
             mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT,
                     mText);
         }
-        if (mPicture != null) {
+        if (mRightIcon != null) {
             mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE,
-                    mPicture);
+                    mRightIcon);
         }
         if (mProgressBar != null) {
             mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS,
                     mProgressBar);
         }
+        addViewsTransformingToSimilar(mLeftIcon);
     }
 
     @Override
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 89babf0..9ced12d 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
@@ -331,11 +331,6 @@
         return null;
     }
 
-    /**
-     * Set the shelf icon to be visible and hide our own icons.
-     */
-    public void setShelfIconVisible(boolean shelfIconVisible) {}
-
     public int getHeaderTranslation(boolean forceNoHeader) {
         return 0;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 756fe6c..8446b4e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -133,7 +133,7 @@
      */
     public static int getNotificationLaunchHeight(Context context) {
         int zDistance = getZDistanceBetweenElements(context);
-        return getBaseHeight(zDistance) * 2;
+        return NOTIFICATIONS_HAVE_SHADOWS ? 2 * getBaseHeight(zDistance) : 4 * zDistance;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 3833637..d8ee102 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -20,10 +20,12 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.drawable.ColorDrawable;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.util.Pair;
+import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.NotificationHeaderView;
 import android.view.View;
@@ -33,6 +35,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.CachingIconView;
+import com.android.internal.widget.NotificationExpandButton;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.NotificationGroupingUtil;
@@ -103,6 +106,8 @@
     private ViewGroup mCurrentHeader;
     private boolean mIsConversation;
 
+    private boolean mTintWithThemeAccent;
+    private boolean mShowGroupCountInExpander;
     private boolean mShowDividersWhenExpanded;
     private boolean mHideDividersDuringExpand;
     private int mTranslationForHeader;
@@ -145,6 +150,10 @@
                 com.android.internal.R.dimen.notification_content_margin);
         mEnableShadowOnChildNotifications =
                 res.getBoolean(R.bool.config_enableShadowOnChildNotifications);
+        mTintWithThemeAccent =
+                res.getBoolean(com.android.internal.R.bool.config_tintNotificationsWithTheme);
+        mShowGroupCountInExpander =
+                res.getBoolean(R.bool.config_showNotificationGroupCountInExpander);
         mShowDividersWhenExpanded =
                 res.getBoolean(R.bool.config_showDividersWhenGroupNotificationExpanded);
         mHideDividersDuringExpand =
@@ -229,7 +238,6 @@
             mNotificationHeader.measure(widthMeasureSpec, headerHeightSpec);
         }
         if (mNotificationHeaderLowPriority != null) {
-            headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
             mNotificationHeaderLowPriority.measure(widthMeasureSpec, headerHeightSpec);
         }
 
@@ -397,7 +405,20 @@
         mGroupingUtil.updateChildrenAppearance();
     }
 
+    private void setExpandButtonNumber(NotificationViewWrapper wrapper) {
+        View expandButton = wrapper == null
+                ? null : wrapper.getExpandButton();
+        if (expandButton instanceof NotificationExpandButton) {
+            ((NotificationExpandButton) expandButton).setNumber(mUntruncatedChildCount);
+        }
+    }
+
     public void updateGroupOverflow() {
+        if (mShowGroupCountInExpander) {
+            setExpandButtonNumber(mNotificationHeaderWrapper);
+            setExpandButtonNumber(mNotificationHeaderWrapperLowPriority);
+            return;
+        }
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
         if (mUntruncatedChildCount > maxAllowedVisibleChildren) {
             int number = mUntruncatedChildCount - maxAllowedVisibleChildren;
@@ -1201,8 +1222,21 @@
     }
 
     public void onNotificationUpdated() {
-        mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
-                mContainingNotification.getNotificationColor());
+        if (mShowGroupCountInExpander) {
+            // The overflow number is not used, so its color is irrelevant; skip this
+            return;
+        }
+        int color = mContainingNotification.getNotificationColor();
+        if (mTintWithThemeAccent) {
+            // We're using the theme accent, color with the accent color instead of the notif color
+            Resources.Theme theme = new ContextThemeWrapper(mContext,
+                    com.android.internal.R.style.Theme_DeviceDefault_DayNight).getTheme();
+            TypedArray ta = theme.obtainStyledAttributes(
+                    new int[]{com.android.internal.R.attr.colorAccent});
+            color = ta.getColor(0, color);
+            ta.recycle();
+        }
+        mHybridGroupManager.setOverflowNumberColor(mOverflowNumber, color);
     }
 
     public int getPositionInLinearLayout(View childInGroup) {
@@ -1225,21 +1259,6 @@
         return 0;
     }
 
-    public void setShelfIconVisible(boolean iconVisible) {
-        if (mNotificationHeaderWrapper != null) {
-            CachingIconView icon = mNotificationHeaderWrapper.getIcon();
-            if (icon != null) {
-                icon.setForceHidden(iconVisible);
-            }
-        }
-        if (mNotificationHeaderWrapperLowPriority != null) {
-            CachingIconView icon = mNotificationHeaderWrapperLowPriority.getIcon();
-            if (icon != null) {
-                icon.setForceHidden(iconVisible);
-            }
-        }
-    }
-
     public void setClipBottomAmount(int clipBottomAmount) {
         mClipBottomAmount = clipBottomAmount;
         updateChildrenClipping();
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 bf36435..0807f8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4398,7 +4398,10 @@
      */
     public static Bundle getActivityOptions(int displayId,
             @Nullable RemoteAnimationAdapter animationAdapter) {
-        return getDefaultActivityOptions(animationAdapter).toBundle();
+        ActivityOptions options = getDefaultActivityOptions(animationAdapter);
+        options.setLaunchDisplayId(displayId);
+        options.setCallerDisplayId(displayId);
+        return options.toBundle();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 38f3bc8..59c1138 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -94,15 +94,6 @@
                             goingToFullShade,
                             oldState);
                 }
-
-                @Override
-                public void onDozeAmountChanged(float linearAmount, float amount) {
-                    if (DEBUG) {
-                        Log.d(TAG, String.format("onDozeAmountChanged: linearAmount=%f amount=%f",
-                                linearAmount, amount));
-                    }
-                    setDarkAmount(amount);
-                }
             };
 
     @Inject
@@ -294,20 +285,6 @@
         }
     }
 
-    /**
-     * Set the amount (ratio) that the device has transitioned to doze.
-     *
-     * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
-     */
-    private void setDarkAmount(float darkAmount) {
-        boolean isAwake = darkAmount != 0;
-        if (darkAmount == mDarkAmount) {
-            return;
-        }
-        mDarkAmount = darkAmount;
-        mView.setVisibility(isAwake ? View.VISIBLE : View.GONE);
-    }
-
     private boolean isListAnimating() {
         return mKeyguardVisibilityHelper.isVisibilityAnimating();
     }
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 8845a05..5a80c05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -205,7 +205,7 @@
     protected void onViewAttached() {
         if (DEBUG) Log.d(TAG, "onViewAttached");
         mAdapter.registerDataSetObserver(mDataSetObserver);
-        mDataSetObserver.onChanged();
+        mAdapter.notifyDataSetChanged();
         mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
         mStatusBarStateController.addCallback(mStatusBarStateListener);
         mScreenLifecycle.addObserver(mScreenObserver);
@@ -373,14 +373,13 @@
      * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
      */
     private void setDarkAmount(float darkAmount) {
-        boolean isAwake = darkAmount != 0;
+        boolean isFullyDozed = darkAmount == 1;
         if (darkAmount == mDarkAmount) {
             return;
         }
         mDarkAmount = darkAmount;
         mListView.setDarkAmount(darkAmount);
-        mView.setVisibility(isAwake ? View.VISIBLE : View.GONE);
-        if (!isAwake) {
+        if (isFullyDozed) {
             closeSwitcherIfOpenAndNotSimple(false);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 738cab1..5638503 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -439,7 +439,7 @@
     private final NetworkCallback mNetworkCallback = new NetworkCallback() {
         @Override
         public void onAvailable(Network network) {
-            if (DEBUG) Log.d(TAG, "onAvailable " + network.netId);
+            if (DEBUG) Log.d(TAG, "onAvailable " + network.getNetId());
             updateState();
             fireCallbacks();
         };
@@ -448,7 +448,7 @@
         // how long the VPN connection is held on to.
         @Override
         public void onLost(Network network) {
-            if (DEBUG) Log.d(TAG, "onLost " + network.netId);
+            if (DEBUG) Log.d(TAG, "onLost " + network.getNetId());
             updateState();
             fireCallbacks();
         };
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 800d859..f60fa09 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
@@ -19,14 +19,21 @@
 import static android.app.Notification.CATEGORY_MISSED_CALL;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
+import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
+import static android.app.people.ConversationStatus.ACTIVITY_GAME;
 
+import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
 import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
+import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
+import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.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;
@@ -38,12 +45,15 @@
 import android.app.NotificationChannel;
 import android.app.Person;
 import android.app.people.ConversationChannel;
+import android.app.people.ConversationStatus;
 import android.app.people.IPeopleManager;
+import android.app.people.PeopleManager;
 import android.app.people.PeopleSpaceTile;
 import android.appwidget.AppWidgetManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
@@ -56,7 +66,6 @@
 import androidx.preference.PreferenceManager;
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.appwidget.IAppWidgetService;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.people.PeopleSpaceUtils;
@@ -76,7 +85,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Arrays;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 @SmallTest
@@ -99,6 +110,7 @@
     private static final String NOTIFICATION_CONTENT = "message text";
     private static final Uri URI = Uri.parse("fake_uri");
     private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
+    private static final String KEY = PeopleSpaceUtils.getKey(SHORTCUT_ID, TEST_PACKAGE_A, 0);
     private static final Person PERSON = new Person.Builder()
             .setName("name")
             .setKey("abc")
@@ -121,12 +133,15 @@
 
     @Mock
     private NotificationListener mListenerService;
-    @Mock
-    private IAppWidgetService mIAppWidgetService;
+
     @Mock
     private AppWidgetManager mAppWidgetManager;
     @Mock
     private IPeopleManager mIPeopleManager;
+    @Mock
+    private PeopleManager mPeopleManager;
+    @Mock
+    private LauncherApps mLauncherApps;
 
     @Captor
     private ArgumentCaptor<NotificationHandler> mListenerCaptor;
@@ -136,13 +151,19 @@
     private final NoManSimulator mNoMan = new NoManSimulator();
     private final FakeSystemClock mClock = new FakeSystemClock();
 
+    private PeopleSpaceWidgetProvider mProvider;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mLauncherApps = mock(LauncherApps.class);
         mManager =
                 new PeopleSpaceWidgetManager(mContext);
-        mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager);
+        mManager.setAppWidgetManager(mAppWidgetManager, mIPeopleManager, mPeopleManager,
+                mLauncherApps);
         mManager.attach(mListenerService);
+        mProvider = new PeopleSpaceWidgetProvider();
+        mProvider.setPeopleSpaceWidgetManager(mManager);
 
         verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
         NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
@@ -166,7 +187,7 @@
     @Test
     public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception {
         int[] widgetIdsArray = {};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createNotification(
                 OTHER_SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false);
@@ -182,7 +203,7 @@
     @Test
     public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception {
         int[] widgetIdsArray = {};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         Notification notificationWithoutShortcut = new Notification.Builder(mContext)
                 .setContentTitle("TEST_TITLE")
@@ -206,7 +227,7 @@
     @Test
     public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception {
         int[] widgetIdsArray = {};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
                 .setNotification(createMessagingStyleNotification(
@@ -224,7 +245,7 @@
     @Test
     public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception {
         int[] widgetIdsArray = {1};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         NotificationChannel channel =
                 new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT);
@@ -240,7 +261,7 @@
     @Test
     public void testUpdateAppWidgetIfConversationChannelModified() throws Exception {
         int[] widgetIdsArray = {1};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         NotificationChannel channel =
                 new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT);
@@ -257,7 +278,7 @@
     @Test
     public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createNotification(
                 OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -277,7 +298,7 @@
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
                 .setNotification(createMessagingStyleNotification(
@@ -298,7 +319,7 @@
     @Test
     public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createNotification(
                 OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -318,7 +339,7 @@
     @Test
     public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
                 .setNotification(createMessagingStyleNotification(
@@ -339,9 +360,74 @@
     }
 
     @Test
+    public void testDoNotUpdateStatusPostedIfDifferentShortcutId() throws Exception {
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        ConversationStatus status1 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID,
+                ACTIVITY_GAME).setDescription("Playing a game!").build();
+        ConversationStatus status2 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID,
+                ACTIVITY_BIRTHDAY).build();
+        ConversationChannel conversationChannel = getConversationWithShortcutId(OTHER_SHORTCUT_ID,
+                Arrays.asList(status1, status2));
+        mManager.updateWidgetsWithConversationChanged(conversationChannel);
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, never())
+                .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
+    }
+
+    @Test
+    public void testUpdateStatusPostedIfExistingTile() throws Exception {
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID,
+                ACTIVITY_GAME).setDescription("Playing a game!").build();
+        ConversationChannel conversationChannel = getConversationWithShortcutId(SHORTCUT_ID,
+                Arrays.asList(status));
+        mManager.updateWidgetsWithConversationChanged(conversationChannel);
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundle = mBundleArgumentCaptor.getValue();
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        assertThat(tile.getStatuses()).containsExactly(status);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
+    public void testUpdateStatusPostedOnTwoExistingTiles() throws Exception {
+        addSecondWidgetForPersonTile();
+
+        ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID,
+                ACTIVITY_ANNIVERSARY).build();
+        ConversationChannel conversationChannel = getConversationWithShortcutId(SHORTCUT_ID,
+                Arrays.asList(status));
+        mManager.updateWidgetsWithConversationChanged(conversationChannel);
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
     public void testUpdateNotificationPostedIfExistingTile() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(createNotification(
@@ -362,17 +448,7 @@
 
     @Test
     public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception {
-        Bundle options = new Bundle();
-        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
-        when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
-                .thenReturn(options);
-        // Set the same Person associated on another People Tile widget ID.
-        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
-        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
-
-        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
-                SECOND_WIDGET_ID_WITH_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        addSecondWidgetForPersonTile();
 
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(createNotification(
@@ -395,19 +471,9 @@
     @Test
     public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()
             throws Exception {
-        Bundle options = new Bundle();
-        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
-        when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
-                .thenReturn(options);
-        // Set the same Person associated on another People Tile widget ID.
-        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
-        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+        addSecondWidgetForPersonTile();
 
-        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
-                SECOND_WIDGET_ID_WITH_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
+        PeopleSpaceUtils.removeStorageForTile(mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(createNotification(
                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
@@ -430,7 +496,7 @@
     public void testUpdateMissedCallNotificationWithoutContentPostedIfExistingTile()
             throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
         setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
 
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -456,7 +522,7 @@
     public void testUpdateMissedCallNotificationWithContentPostedIfExistingTile()
             throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
         setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
 
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -480,7 +546,7 @@
     @Test
     public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createNotification(
                 SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -502,14 +568,82 @@
                 any());
     }
 
+    @Test
+    public void testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners() {
+        addSecondWidgetForPersonTile();
+        mProvider.onUpdate(mContext, mAppWidgetManager,
+                new int[]{WIDGET_ID_WITH_SHORTCUT, SECOND_WIDGET_ID_WITH_SHORTCUT});
+
+        // Delete only one widget for the conversation.
+        mManager.deleteWidgets(new int[]{WIDGET_ID_WITH_SHORTCUT});
+
+        // Check deleted storage.
+        SharedPreferences widgetSp = mContext.getSharedPreferences(
+                String.valueOf(WIDGET_ID_WITH_SHORTCUT),
+                Context.MODE_PRIVATE);
+        assertThat(widgetSp.getString(PACKAGE_NAME, null)).isNull();
+        assertThat(widgetSp.getString(SHORTCUT_ID, null)).isNull();
+        assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        assertThat(sp.getStringSet(KEY, new HashSet<>())).containsExactly(
+                String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT));
+        // Check listener & shortcut caching remain for other widget.
+        verify(mPeopleManager, never()).unregisterConversationListener(any());
+        verify(mLauncherApps, never()).uncacheShortcuts(eq(TEST_PACKAGE_A),
+                eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
+                eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
+
+        // Delete all widgets for the conversation.
+        mProvider.onDeleted(mContext, new int[]{SECOND_WIDGET_ID_WITH_SHORTCUT});
+
+        // Check deleted storage.
+        SharedPreferences secondWidgetSp = mContext.getSharedPreferences(
+                String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                Context.MODE_PRIVATE);
+        assertThat(secondWidgetSp.getString(PACKAGE_NAME, null)).isNull();
+        assertThat(secondWidgetSp.getString(SHORTCUT_ID, null)).isNull();
+        assertThat(secondWidgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
+        assertThat(sp.getStringSet(KEY, new HashSet<>())).isEmpty();
+        // Check listener is removed and shortcut is uncached.
+        verify(mPeopleManager, times(1)).unregisterConversationListener(any());
+        verify(mLauncherApps, times(1)).uncacheShortcuts(eq(TEST_PACKAGE_A),
+                eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
+                eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
+    }
+
+    /**
+     * Adds another widget for {@code PERSON_TILE} with widget ID: {@code
+     * SECOND_WIDGET_ID_WITH_SHORTCUT}.
+     */
+    private void addSecondWidgetForPersonTile() {
+        Bundle options = new Bundle();
+        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+        when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+                .thenReturn(options);
+        // Set the same Person associated on another People Tile widget ID.
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+                SECOND_WIDGET_ID_WITH_SHORTCUT};
+        when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+    }
+
     /**
      * Returns a single conversation associated with {@code shortcutId}.
      */
     private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception {
+        return getConversationWithShortcutId(shortcutId, Arrays.asList());
+    }
+
+    /**
+     * Returns a single conversation associated with {@code shortcutId} and {@code statuses}.
+     */
+    private ConversationChannel getConversationWithShortcutId(String shortcutId,
+            List<ConversationStatus> statuses) throws Exception {
         ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
                 "name").build();
         ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
-                0L, false);
+                0L, false, false, statuses);
         return convo;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt
new file mode 100644
index 0000000..998e070
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tileimpl
+
+import android.service.quicksettings.Tile
+import android.testing.AndroidTestingRunner
+import android.text.TextUtils
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSIconView
+import com.android.systemui.plugins.qs.QSTile
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class QSTileBaseViewTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var iconView: QSIconView
+
+    private lateinit var tileView: QSTileBaseView
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        tileView = QSTileBaseView(context, iconView, false)
+    }
+
+    @Test
+    fun testSecondaryLabelNotModified_unavailable() {
+        val state = QSTile.State()
+        val testString = "TEST STRING"
+        state.state = Tile.STATE_UNAVAILABLE
+        state.secondaryLabel = testString
+
+        tileView.handleStateChanged(state)
+
+        assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
+    }
+
+    @Test
+    fun testSecondaryLabelNotModified_booleanInactive() {
+        val state = QSTile.BooleanState()
+        val testString = "TEST STRING"
+        state.state = Tile.STATE_INACTIVE
+        state.secondaryLabel = testString
+
+        tileView.handleStateChanged(state)
+
+        assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
+    }
+
+    @Test
+    fun testSecondaryLabelNotModified_booleanActive() {
+        val state = QSTile.BooleanState()
+        val testString = "TEST STRING"
+        state.state = Tile.STATE_ACTIVE
+        state.secondaryLabel = testString
+
+        tileView.handleStateChanged(state)
+
+        assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
+    }
+
+    @Test
+    fun testSecondaryLabelNotModified_availableNotBoolean_inactive() {
+        val state = QSTile.State()
+        state.state = Tile.STATE_INACTIVE
+        state.secondaryLabel = ""
+
+        tileView.handleStateChanged(state)
+
+        assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
+    }
+
+    @Test
+    fun testSecondaryLabelNotModified_availableNotBoolean_active() {
+        val state = QSTile.State()
+        state.state = Tile.STATE_ACTIVE
+        state.secondaryLabel = ""
+
+        tileView.handleStateChanged(state)
+
+        assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
+    }
+
+    @Test
+    fun testSecondaryLabelDescription_unavailable() {
+        val state = QSTile.State()
+        state.state = Tile.STATE_UNAVAILABLE
+        state.secondaryLabel = ""
+
+        tileView.handleStateChanged(state)
+
+        assertThat(state.secondaryLabel as CharSequence).isEqualTo(
+            context.getString(R.string.tile_unavailable)
+        )
+    }
+
+    @Test
+    fun testSecondaryLabelDescription_booleanInactive() {
+        val state = QSTile.BooleanState()
+        state.state = Tile.STATE_INACTIVE
+        state.secondaryLabel = ""
+
+        tileView.handleStateChanged(state)
+
+        assertThat(state.secondaryLabel as CharSequence).isEqualTo(
+            context.getString(R.string.switch_bar_off)
+        )
+    }
+
+    @Test
+    fun testSecondaryLabelDescription_booleanActive() {
+        val state = QSTile.BooleanState()
+        state.state = Tile.STATE_ACTIVE
+        state.secondaryLabel = ""
+
+        tileView.handleStateChanged(state)
+
+        assertThat(state.secondaryLabel as CharSequence).isEqualTo(
+            context.getString(R.string.switch_bar_on)
+        )
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index b1b71cc..999d282 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -50,11 +50,11 @@
 import android.provider.Settings.Global;
 import android.telephony.CellSignalStrength;
 import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.testing.TestableLooper;
@@ -106,7 +106,6 @@
 
     protected NetworkControllerImpl mNetworkController;
     protected MobileSignalController mMobileSignalController;
-    protected PhoneStateListener mPhoneStateListener;
     protected SignalStrength mSignalStrength;
     protected ServiceState mServiceState;
     protected TelephonyDisplayInfo mTelephonyDisplayInfo;
@@ -250,8 +249,6 @@
         setDefaultSubId(mSubId);
         setSubscriptions(mSubId);
         mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
-        mPhoneStateListener = mMobileSignalController.mMobileStatusTracker.getPhoneStateListener();
-
         ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackArg =
             ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
         verify(mMockCm, atLeastOnce())
@@ -455,18 +452,16 @@
 
     private void updateSignalStrength() {
         Log.d(TAG, "Sending Signal Strength: " + mSignalStrength);
-        mPhoneStateListener.onSignalStrengthsChanged(mSignalStrength);
+        mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+                .onSignalStrengthsChanged(mSignalStrength);
     }
 
     protected void updateServiceState() {
         Log.d(TAG, "Sending Service State: " + mServiceState);
-        mPhoneStateListener.onServiceStateChanged(mServiceState);
-        mPhoneStateListener.onDisplayInfoChanged(mTelephonyDisplayInfo);
-    }
-
-    public void updateCallState(int state) {
-        // Inputs not currently used in NetworkControllerImpl.
-        mPhoneStateListener.onCallStateChanged(state, "0123456789");
+        mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+                .onServiceStateChanged(mServiceState);
+        mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+                .onDisplayInfoChanged(mTelephonyDisplayInfo);
     }
 
     public void updateDataConnectionState(int dataState, int dataNetType) {
@@ -478,16 +473,19 @@
         when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN))
                 .thenReturn(fakeRegInfo);
         when(mTelephonyDisplayInfo.getNetworkType()).thenReturn(dataNetType);
-        mPhoneStateListener.onDataConnectionStateChanged(dataState, dataNetType);
+        mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+                .onDataConnectionStateChanged(dataState, dataNetType);
     }
 
     public void updateDataActivity(int dataActivity) {
-        mPhoneStateListener.onDataActivity(dataActivity);
+        mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+                .onDataActivity(dataActivity);
     }
 
     public void setCarrierNetworkChange(boolean enable) {
         Log.d(TAG, "setCarrierNetworkChange(" + enable + ")");
-        mPhoneStateListener.onCarrierNetworkChange(enable);
+        mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+                .onCarrierNetworkChange(enable);
     }
 
     protected void verifyHasNoSims(boolean hasNoSimsVisible) {
diff --git a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
index bafb641..6828dd9 100644
--- a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
+++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
@@ -40,29 +40,34 @@
     private final IAccessibilityInteractionConnectionCallback mServiceCallback;
     private final IAccessibilityInteractionConnection mConnectionWithReplacementActions;
     private final int mInteractionId;
+    private final int mNodeWithReplacementActionsInteractionId;
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
-    List<AccessibilityNodeInfo> mNodesWithReplacementActions;
+    private boolean mReplacementNodeIsReadyOrFailed;
+
+    @GuardedBy("mLock")
+    AccessibilityNodeInfo mNodeWithReplacementActions;
 
     @GuardedBy("mLock")
     List<AccessibilityNodeInfo> mNodesFromOriginalWindow;
 
     @GuardedBy("mLock")
+    boolean mSetFindNodeFromOriginalWindowCalled = false;
+
+    @GuardedBy("mLock")
     AccessibilityNodeInfo mNodeFromOriginalWindow;
 
-    // Keep track of whether or not we've been called back for a single node
     @GuardedBy("mLock")
-    boolean mSingleNodeCallbackHappened;
+    boolean mSetFindNodesFromOriginalWindowCalled = false;
 
-    // Keep track of whether or not we've been called back for multiple node
-    @GuardedBy("mLock")
-    boolean mMultiNodeCallbackHappened;
 
-    // We shouldn't get any more callbacks after we've called back the original service, but
-    // keep track to make sure we catch such strange things
     @GuardedBy("mLock")
-    boolean mDone;
+    List<AccessibilityNodeInfo> mPrefetchedNodesFromOriginalWindow;
+
+    @GuardedBy("mLock")
+    boolean mSetPrefetchFromOriginalWindowCalled = false;
+
 
     public ActionReplacingCallback(IAccessibilityInteractionConnectionCallback serviceCallback,
             IAccessibilityInteractionConnection connectionWithReplacementActions,
@@ -70,19 +75,20 @@
         mServiceCallback = serviceCallback;
         mConnectionWithReplacementActions = connectionWithReplacementActions;
         mInteractionId = interactionId;
+        mNodeWithReplacementActionsInteractionId = interactionId + 1;
 
         // Request the root node of the replacing window
         final long identityToken = Binder.clearCallingIdentity();
         try {
             mConnectionWithReplacementActions.findAccessibilityNodeInfoByAccessibilityId(
-                    AccessibilityNodeInfo.ROOT_NODE_ID, null, interactionId + 1, this, 0,
+                    AccessibilityNodeInfo.ROOT_NODE_ID, null,
+                    mNodeWithReplacementActionsInteractionId, this, 0,
                     interrogatingPid, interrogatingTid, null, null);
         } catch (RemoteException re) {
             if (DEBUG) {
                 Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
             }
-            // Pretend we already got a (null) list of replacement nodes
-            mMultiNodeCallbackHappened = true;
+            mReplacementNodeIsReadyOrFailed = true;
         } finally {
             Binder.restoreCallingIdentity(identityToken);
         }
@@ -90,46 +96,67 @@
 
     @Override
     public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info, int interactionId) {
-        boolean readyForCallback;
-        synchronized(mLock) {
+        synchronized (mLock) {
             if (interactionId == mInteractionId) {
                 mNodeFromOriginalWindow = info;
+                mSetFindNodeFromOriginalWindowCalled = true;
+            } else if (interactionId == mNodeWithReplacementActionsInteractionId) {
+                mNodeWithReplacementActions = info;
+                mReplacementNodeIsReadyOrFailed = true;
             } else {
                 Slog.e(LOG_TAG, "Callback with unexpected interactionId");
                 return;
             }
-
-            mSingleNodeCallbackHappened = true;
-            readyForCallback = mMultiNodeCallbackHappened;
         }
-        if (readyForCallback) {
-            replaceInfoActionsAndCallService();
-        }
+        replaceInfoActionsAndCallServiceIfReady();
     }
 
     @Override
     public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
             int interactionId) {
-        boolean callbackForSingleNode;
-        boolean callbackForMultipleNodes;
-        synchronized(mLock) {
+        synchronized (mLock) {
             if (interactionId == mInteractionId) {
                 mNodesFromOriginalWindow = infos;
-            } else if (interactionId == mInteractionId + 1) {
-                mNodesWithReplacementActions = infos;
+                mSetFindNodesFromOriginalWindowCalled = true;
+            } else if (interactionId == mNodeWithReplacementActionsInteractionId) {
+                setNodeWithReplacementActionsFromList(infos);
+                mReplacementNodeIsReadyOrFailed = true;
             } else {
                 Slog.e(LOG_TAG, "Callback with unexpected interactionId");
                 return;
             }
-            callbackForSingleNode = mSingleNodeCallbackHappened;
-            callbackForMultipleNodes = mMultiNodeCallbackHappened;
-            mMultiNodeCallbackHappened = true;
         }
-        if (callbackForSingleNode) {
-            replaceInfoActionsAndCallService();
+        replaceInfoActionsAndCallServiceIfReady();
+    }
+
+    @Override
+    public void setPrefetchAccessibilityNodeInfoResult(List<AccessibilityNodeInfo> infos,
+                                                       int interactionId)
+            throws RemoteException {
+        synchronized (mLock) {
+            if (interactionId == mInteractionId) {
+                mPrefetchedNodesFromOriginalWindow = infos;
+                mSetPrefetchFromOriginalWindowCalled = true;
+            }  else {
+                Slog.e(LOG_TAG, "Callback with unexpected interactionId");
+                return;
+            }
         }
-        if (callbackForMultipleNodes) {
-            replaceInfosActionsAndCallService();
+        replaceInfoActionsAndCallServiceIfReady();
+    }
+
+    private void replaceInfoActionsAndCallServiceIfReady() {
+        replaceInfoActionsAndCallService();
+        replaceInfosActionsAndCallService();
+        replacePrefetchInfosActionsAndCallService();
+    }
+
+    private void setNodeWithReplacementActionsFromList(List<AccessibilityNodeInfo> infos) {
+        for (int i = 0; i < infos.size(); i++) {
+            AccessibilityNodeInfo info = infos.get(i);
+            if (info.getSourceNodeId() == AccessibilityNodeInfo.ROOT_NODE_ID) {
+                mNodeWithReplacementActions = info;
+            }
         }
     }
 
@@ -142,55 +169,81 @@
 
     private void replaceInfoActionsAndCallService() {
         final AccessibilityNodeInfo nodeToReturn;
+        boolean doCallback = false;
         synchronized (mLock) {
-            if (mDone) {
-                if (DEBUG) {
-                    Slog.e(LOG_TAG, "Extra callback");
-                }
-                return;
-            }
-            if (mNodeFromOriginalWindow != null) {
+            doCallback = mReplacementNodeIsReadyOrFailed
+                    && mSetFindNodeFromOriginalWindowCalled;
+            if (doCallback && mNodeFromOriginalWindow != null) {
                 replaceActionsOnInfoLocked(mNodeFromOriginalWindow);
+                mSetFindNodeFromOriginalWindowCalled = false;
             }
-            recycleReplaceActionNodesLocked();
             nodeToReturn = mNodeFromOriginalWindow;
-            mDone = true;
         }
-        try {
-            mServiceCallback.setFindAccessibilityNodeInfoResult(nodeToReturn, mInteractionId);
-        } catch (RemoteException re) {
-            if (DEBUG) {
-                Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfoResult");
+        if (doCallback) {
+            try {
+                mServiceCallback.setFindAccessibilityNodeInfoResult(nodeToReturn, mInteractionId);
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfoResult");
+                }
             }
         }
     }
 
     private void replaceInfosActionsAndCallService() {
-        final List<AccessibilityNodeInfo> nodesToReturn;
+        List<AccessibilityNodeInfo> nodesToReturn = null;
+        boolean doCallback = false;
         synchronized (mLock) {
-            if (mDone) {
+            doCallback = mReplacementNodeIsReadyOrFailed
+                    && mSetFindNodesFromOriginalWindowCalled;
+            if (doCallback) {
+                nodesToReturn = replaceActionsLocked(mNodesFromOriginalWindow);
+                mSetFindNodesFromOriginalWindowCalled = false;
+            }
+        }
+        if (doCallback) {
+            try {
+                mServiceCallback.setFindAccessibilityNodeInfosResult(nodesToReturn, mInteractionId);
+            } catch (RemoteException re) {
                 if (DEBUG) {
-                    Slog.e(LOG_TAG, "Extra callback");
-                }
-                return;
-            }
-            if (mNodesFromOriginalWindow != null) {
-                for (int i = 0; i < mNodesFromOriginalWindow.size(); i++) {
-                    replaceActionsOnInfoLocked(mNodesFromOriginalWindow.get(i));
+                    Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfosResult");
                 }
             }
-            recycleReplaceActionNodesLocked();
-            nodesToReturn = (mNodesFromOriginalWindow == null)
-                    ? null : new ArrayList<>(mNodesFromOriginalWindow);
-            mDone = true;
         }
-        try {
-            mServiceCallback.setFindAccessibilityNodeInfosResult(nodesToReturn, mInteractionId);
-        } catch (RemoteException re) {
-            if (DEBUG) {
-                Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfosResult");
+    }
+
+    private void replacePrefetchInfosActionsAndCallService() {
+        List<AccessibilityNodeInfo> nodesToReturn = null;
+        boolean doCallback = false;
+        synchronized (mLock) {
+            doCallback = mReplacementNodeIsReadyOrFailed
+                    && mSetPrefetchFromOriginalWindowCalled;
+            if (doCallback) {
+                nodesToReturn = replaceActionsLocked(mPrefetchedNodesFromOriginalWindow);
+                mSetPrefetchFromOriginalWindowCalled = false;
             }
         }
+        if (doCallback) {
+            try {
+                mServiceCallback.setPrefetchAccessibilityNodeInfoResult(
+                        nodesToReturn, mInteractionId);
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfosResult");
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private List<AccessibilityNodeInfo> replaceActionsLocked(List<AccessibilityNodeInfo> infos) {
+        if (infos != null) {
+            for (int i = 0; i < infos.size(); i++) {
+                replaceActionsOnInfoLocked(infos.get(i));
+            }
+        }
+        return (infos == null)
+                ? null : new ArrayList<>(infos);
     }
 
     @GuardedBy("mLock")
@@ -204,40 +257,22 @@
         info.setDismissable(false);
         // We currently only replace actions for the root node
         if ((info.getSourceNodeId() == AccessibilityNodeInfo.ROOT_NODE_ID)
-                && mNodesWithReplacementActions != null) {
-            // This list should always contain a single node with the root ID
-            for (int i = 0; i < mNodesWithReplacementActions.size(); i++) {
-                AccessibilityNodeInfo nodeWithReplacementActions =
-                        mNodesWithReplacementActions.get(i);
-                if (nodeWithReplacementActions.getSourceNodeId()
-                        == AccessibilityNodeInfo.ROOT_NODE_ID) {
-                    List<AccessibilityAction> actions = nodeWithReplacementActions.getActionList();
-                    if (actions != null) {
-                        for (int j = 0; j < actions.size(); j++) {
-                            info.addAction(actions.get(j));
-                        }
-                        // The PIP needs to be able to take accessibility focus
-                        info.addAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS);
-                        info.addAction(AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
-                    }
-                    info.setClickable(nodeWithReplacementActions.isClickable());
-                    info.setFocusable(nodeWithReplacementActions.isFocusable());
-                    info.setContextClickable(nodeWithReplacementActions.isContextClickable());
-                    info.setScrollable(nodeWithReplacementActions.isScrollable());
-                    info.setLongClickable(nodeWithReplacementActions.isLongClickable());
-                    info.setDismissable(nodeWithReplacementActions.isDismissable());
+                && mNodeWithReplacementActions != null) {
+            List<AccessibilityAction> actions = mNodeWithReplacementActions.getActionList();
+            if (actions != null) {
+                for (int j = 0; j < actions.size(); j++) {
+                    info.addAction(actions.get(j));
                 }
+                // The PIP needs to be able to take accessibility focus
+                info.addAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS);
+                info.addAction(AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
             }
+            info.setClickable(mNodeWithReplacementActions.isClickable());
+            info.setFocusable(mNodeWithReplacementActions.isFocusable());
+            info.setContextClickable(mNodeWithReplacementActions.isContextClickable());
+            info.setScrollable(mNodeWithReplacementActions.isScrollable());
+            info.setLongClickable(mNodeWithReplacementActions.isLongClickable());
+            info.setDismissable(mNodeWithReplacementActions.isDismissable());
         }
     }
-
-    @GuardedBy("mLock")
-    private void recycleReplaceActionNodesLocked() {
-        if (mNodesWithReplacementActions == null) return;
-        for (int i = mNodesWithReplacementActions.size() - 1; i >= 0; i--) {
-            AccessibilityNodeInfo nodeWithReplacementAction = mNodesWithReplacementActions.get(i);
-            nodeWithReplacementAction.recycle();
-        }
-        mNodesWithReplacementActions = null;
-    }
 }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 21cae45..52237c9 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -38,6 +38,7 @@
 import static java.util.Objects.requireNonNull;
 import static java.util.concurrent.TimeUnit.MINUTES;
 
+import android.Manifest;
 import android.annotation.CheckResult;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -409,6 +410,7 @@
             checkCallerIsSystemOr(callingPackage);
             int userId = getCallingUserId();
             checkUsesFeature(callingPackage, userId);
+            checkProfilePermissions(request);
 
             mFindDeviceCallback = callback;
             mRequest = request;
@@ -519,6 +521,21 @@
             }
         }
 
+        private void checkProfilePermissions(AssociationRequest request) {
+            checkProfilePermission(request,
+                    AssociationRequest.DEVICE_PROFILE_WATCH,
+                    Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH);
+        }
+
+        private void checkProfilePermission(
+                AssociationRequest request, String profile, String permission) {
+            if (profile.equals(request.getDeviceProfile())
+                    && getContext().checkCallingOrSelfPermission(permission)
+                            != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Using " + profile + " requires " + permission);
+            }
+        }
+
         @Override
         public PendingIntent requestNotificationAccess(ComponentName component)
                 throws RemoteException {
@@ -1330,7 +1347,7 @@
         mPermissionControllerManager.getPrivilegesDescriptionStringForProfile(
                 deviceProfile, FgThread.getExecutor(), desc -> {
                         try {
-                            result.complete(desc);
+                            result.complete(String.valueOf(desc));
                         } catch (Exception e) {
                             result.completeExceptionally(e);
                         }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 02930dc..f4a8ccd 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -69,6 +69,7 @@
 import android.service.contentcapture.ActivityEvent.ActivityEventType;
 import android.service.contentcapture.IDataShareCallback;
 import android.service.contentcapture.IDataShareReadAdapter;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Pair;
@@ -81,6 +82,7 @@
 import android.view.contentcapture.DataRemovalRequest;
 import android.view.contentcapture.DataShareRequest;
 import android.view.contentcapture.IContentCaptureManager;
+import android.view.contentcapture.IContentCaptureOptionsCallback;
 import android.view.contentcapture.IDataShareWriteAdapter;
 
 import com.android.internal.annotations.GuardedBy;
@@ -134,6 +136,9 @@
 
     private final LocalService mLocalService = new LocalService();
 
+    private final ContentCaptureManagerServiceStub mContentCaptureManagerServiceStub =
+            new ContentCaptureManagerServiceStub();
+
     @Nullable
     final LocalLog mRequestsHistory;
 
@@ -224,8 +229,7 @@
 
     @Override // from SystemService
     public void onStart() {
-        publishBinderService(CONTENT_CAPTURE_MANAGER_SERVICE,
-                new ContentCaptureManagerServiceStub());
+        publishBinderService(CONTENT_CAPTURE_MANAGER_SERVICE, mContentCaptureManagerServiceStub);
         publishLocalService(ContentCaptureManagerInternal.class, mLocalService);
     }
 
@@ -492,6 +496,19 @@
         }
     }
 
+    void updateOptions(String packageName, ContentCaptureOptions options) {
+        ArraySet<CallbackRecord> records;
+        synchronized (mLock) {
+            records = mContentCaptureManagerServiceStub.mCallbacks.get(packageName);
+            if (records != null) {
+                int N = records.size();
+                for (int i = 0; i < N; i++) {
+                    records.valueAt(i).setContentCaptureOptions(options);
+                }
+            }
+        }
+    }
+
     private ActivityManagerInternal getAmInternal() {
         synchronized (mLock) {
             if (mAm == null) {
@@ -599,6 +616,8 @@
     }
 
     final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
+        @GuardedBy("mLock")
+        private final ArrayMap<String, ArraySet<CallbackRecord>> mCallbacks = new ArrayMap<>();
 
         @Override
         public void startSession(@NonNull IBinder activityToken,
@@ -755,6 +774,46 @@
         }
 
         @Override
+        public void registerContentCaptureOptionsCallback(@NonNull String packageName,
+                IContentCaptureOptionsCallback callback) {
+            assertCalledByPackageOwner(packageName);
+
+            CallbackRecord record = new CallbackRecord(callback, packageName);
+            record.registerObserver();
+
+            synchronized (mLock) {
+                ArraySet<CallbackRecord> records = mCallbacks.get(packageName);
+                if (records == null) {
+                    records = new ArraySet<>();
+                }
+                records.add(record);
+                mCallbacks.put(packageName, records);
+            }
+
+            // Set options here in case it was updated before this was registered.
+            final int userId = UserHandle.getCallingUserId();
+            final ContentCaptureOptions options = mGlobalContentCaptureOptions.getOptions(userId,
+                    packageName);
+            if (options != null) {
+                record.setContentCaptureOptions(options);
+            }
+        }
+
+        private void unregisterContentCaptureOptionsCallback(CallbackRecord record) {
+            synchronized (mLock) {
+                ArraySet<CallbackRecord> records = mCallbacks.get(record.mPackageName);
+                if (records != null) {
+                    records.remove(record);
+                }
+
+                if (records == null || records.isEmpty()) {
+                    mCallbacks.remove(record.mPackageName);
+                }
+            }
+            record.unregisterObserver();
+        }
+
+        @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
 
@@ -1218,4 +1277,39 @@
                     mDataShareRequest.getPackageName());
         }
     }
+
+    private final class CallbackRecord implements IBinder.DeathRecipient {
+        private final String mPackageName;
+        private final IContentCaptureOptionsCallback mCallback;
+
+        private CallbackRecord(IContentCaptureOptionsCallback callback, String packageName) {
+            mCallback = callback;
+            mPackageName = packageName;
+        }
+
+        private void setContentCaptureOptions(ContentCaptureOptions options) {
+            try {
+                mCallback.setContentCaptureOptions(options);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Unable to send setContentCaptureOptions(): " + e);
+            }
+        }
+
+        private void registerObserver() {
+            try {
+                mCallback.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to register callback cleanup " + e);
+            }
+        }
+
+        private void unregisterObserver() {
+            mCallback.asBinder().unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            mContentCaptureManagerServiceStub.unregisterContentCaptureOptionsCallback(this);
+        }
+    }
 }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 53cdc33..225a8d4 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -597,9 +597,15 @@
                         ? "null_activities" : activities.size() + " activities") + ")"
                         + " for user " + mUserId);
             }
+
+            ArraySet<String> oldList =
+                    mMaster.mGlobalContentCaptureOptions.getWhitelistedPackages(mUserId);
+
             mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
             writeSetWhitelistEvent(getServiceComponentName(), packages, activities);
 
+            updateContentCaptureOptions(oldList);
+
             // Must disable session that are not the allowlist anymore...
             final int numSessions = mSessions.size();
             if (numSessions <= 0) return;
@@ -671,5 +677,23 @@
             ContentCaptureMetricsLogger.writeSessionFlush(sessionId, getServiceComponentName(), app,
                     flushMetrics, options, flushReason);
         }
+
+        /** Updates {@link ContentCaptureOptions} for all newly added packages on allowlist. */
+        private void updateContentCaptureOptions(@Nullable ArraySet<String> oldList) {
+            ArraySet<String> adding = mMaster.mGlobalContentCaptureOptions
+                    .getWhitelistedPackages(mUserId);
+
+            if (oldList != null && adding != null) {
+                adding.removeAll(oldList);
+            }
+
+            int N = adding != null ? adding.size() : 0;
+            for (int i = 0; i < N; i++) {
+                String packageName = adding.valueAt(i);
+                ContentCaptureOptions options = mMaster.mGlobalContentCaptureOptions
+                        .getOptions(mUserId, packageName);
+                mMaster.updateOptions(packageName, options);
+            }
+        }
     }
 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 99ce2db..8ccfad6 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -223,7 +223,6 @@
         "java/com/android/server/TestNetworkService.java",
         "java/com/android/server/connectivity/AutodestructReference.java",
         "java/com/android/server/connectivity/ConnectivityConstants.java",
-        "java/com/android/server/connectivity/DataConnectionStats.java",
         "java/com/android/server/connectivity/DnsManager.java",
         "java/com/android/server/connectivity/KeepaliveTracker.java",
         "java/com/android/server/connectivity/LingerMonitor.java",
diff --git a/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
similarity index 76%
rename from core/java/com/android/server/BootReceiver.java
rename to services/core/java/com/android/server/BootReceiver.java
index fe3042d..d83e2fd 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static android.system.OsConstants.O_RDONLY;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -24,12 +26,15 @@
 import android.os.DropBoxManager;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.MessageQueue.OnFileDescriptorEventListener;
 import android.os.RecoverySystem;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.storage.StorageManager;
 import android.provider.Downloads;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.text.TextUtils;
 import android.util.AtomicFile;
 import android.util.EventLog;
@@ -46,11 +51,15 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.regex.Matcher;
@@ -116,6 +125,12 @@
     private static final String METRIC_SYSTEM_SERVER = "shutdown_system_server";
     private static final String METRIC_SHUTDOWN_TIME_START = "begin_shutdown";
 
+    // Location of ftrace pipe for notifications from kernel memory tools like KFENCE and KASAN.
+    private static final String ERROR_REPORT_TRACE_PIPE =
+            "/sys/kernel/tracing/instances/bootreceiver/trace_pipe";
+    // Avoid reporing the same bug from processDmesg() twice.
+    private static String sLastReportedBug = null;
+
     @Override
     public void onReceive(final Context context, Intent intent) {
         // Log boot events in the background to avoid blocking the main thread with I/O
@@ -143,6 +158,209 @@
 
             }
         }.start();
+
+        FileDescriptor tracefd = null;
+        try {
+            tracefd = Os.open(ERROR_REPORT_TRACE_PIPE, O_RDONLY, 0600);
+        } catch (ErrnoException e) {
+            Slog.wtf(TAG, "Could not open " + ERROR_REPORT_TRACE_PIPE, e);
+            return;
+        }
+
+        /*
+         * Event listener to watch for memory tool error reports.
+         * We read from /sys/kernel/tracing/instances/bootreceiver/trace_pipe (set up by the
+         * system), which will print an ftrace event when a memory corruption is detected in the
+         * kernel.
+         * When an error is detected, we run the dmesg shell command and process its output.
+         */
+        OnFileDescriptorEventListener traceCallback = new OnFileDescriptorEventListener() {
+            final int mBufferSize = 1024;
+            byte[] mTraceBuffer = new byte[mBufferSize];
+            @Override
+            public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+                /*
+                 * Read from the tracing pipe set up to monitor the error_report_end events.
+                 * When a tracing event occurs, the kernel writes a short (~100 bytes) line to the
+                 * pipe, e.g.:
+                 *   ...-11210  [004] d..1   285.322307: error_report_end: [kfence] ffffff8938a05000
+                 * The buffer size we use for reading should be enough to read the whole
+                 * line, but to be on the safe side we keep reading until the buffer
+                 * contains a '\n' character. In the unlikely case of a very buggy kernel
+                 * the buffer may contain multiple tracing events that cannot be attributed
+                 * to particular error reports. In that case the latest error report
+                 * residing in dmesg is picked.
+                 */
+                try {
+                    int nbytes = Os.read(fd, mTraceBuffer, 0, mBufferSize);
+                    if (nbytes > 0) {
+                        String readStr = new String(mTraceBuffer);
+                        if (readStr.indexOf("\n") == -1) {
+                            return OnFileDescriptorEventListener.EVENT_INPUT;
+                        }
+                        processDmesg(context);
+                    }
+                } catch (Exception e) {
+                    Slog.wtf(TAG, "Error processing dmesg output", e);
+                    return 0;  // Unregister the handler.
+                }
+                return OnFileDescriptorEventListener.EVENT_INPUT;
+            }
+        };
+
+        IoThread.get().getLooper().getQueue().addOnFileDescriptorEventListener(
+                tracefd, OnFileDescriptorEventListener.EVENT_INPUT, traceCallback);
+
+    }
+
+    /**
+     * Check whether it is safe to collect this dmesg line or not.
+     *
+     * We only consider lines belonging to KASAN or KFENCE reports, but those may still contain
+     * user information, namely the process name:
+     *
+     *   [   69.547684] [ T6006]c7   6006  CPU: 7 PID: 6006 Comm: sh Tainted: G S       C O      ...
+     *
+     * hardware information:
+     *
+     *   [   69.558923] [ T6006]c7   6006  Hardware name: <REDACTED>
+     *
+     * or register dump (in KASAN reports only):
+     *
+     *   ... RIP: 0033:0x7f96443109da
+     *   ... RSP: 002b:00007ffcf0b51b08 EFLAGS: 00000202 ORIG_RAX: 00000000000000af
+     *   ... RAX: ffffffffffffffda RBX: 000055dc3ee521a0 RCX: 00007f96443109da
+     *
+     * (on x86_64)
+     *
+     *   ... pc : lpm_cpuidle_enter+0x258/0x384
+     *   ... lr : lpm_cpuidle_enter+0x1d4/0x384
+     *   ... sp : ffffff800820bea0
+     *   ... x29: ffffff800820bea0 x28: ffffffc2305f3ce0
+     *   ... ...
+     *   ... x9 : 0000000000000001 x8 : 0000000000000000
+     * (on ARM64)
+     *
+     * We therefore omit the lines that contain "Comm:", "Hardware name:", or match the general
+     * purpose register regexp.
+     *
+     * @param  line single line of `dmesg` output.
+     * @return      updated line with sensitive data removed, or null if the line must be skipped.
+     */
+    public static String stripSensitiveData(String line) {
+        /*
+         * General purpose register names begin with "R" on x86_64 and "x" on ARM64. The letter is
+         * followed by two symbols (numbers, letters or spaces) and a colon, which is followed by a
+         * 16-digit hex number. The optional "_" prefix accounts for ORIG_RAX on x86.
+         */
+        final String registerRegex = "[ _][Rx]..: [0-9a-f]{16}";
+        final Pattern registerPattern = Pattern.compile(registerRegex);
+
+        final String corruptionRegex = "Detected corrupted memory at 0x[0-9a-f]+";
+        final Pattern corruptionPattern = Pattern.compile(corruptionRegex);
+
+        if (line.contains("Comm: ") || line.contains("Hardware name: ")) return null;
+        if (registerPattern.matcher(line).find()) return null;
+
+        Matcher cm = corruptionPattern.matcher(line);
+        if (cm.find()) return cm.group(0);
+        return line;
+    }
+
+    /*
+     * Search dmesg output for the last error report from KFENCE or KASAN and copy it to Dropbox.
+     *
+     * Example report printed by the kernel (redacted to fit into 100 column limit):
+     *   [   69.236673] [ T6006]c7   6006  =========================================================
+     *   [   69.245688] [ T6006]c7   6006  BUG: KFENCE: out-of-bounds in kfence_handle_page_fault
+     *   [   69.245688] [ T6006]c7   6006
+     *   [   69.257816] [ T6006]c7   6006  Out-of-bounds access at 0xffffffca75c45000 (...)
+     *   [   69.267102] [ T6006]c7   6006   kfence_handle_page_fault+0x1bc/0x208
+     *   [   69.273536] [ T6006]c7   6006   __do_kernel_fault+0xa8/0x11c
+     *   ...
+     *   [   69.355427] [ T6006]c7   6006  kfence-#2 [0xffffffca75c46f30-0xffffffca75c46fff, ...
+     *   [   69.366938] [ T6006]c7   6006   __d_alloc+0x3c/0x1b4
+     *   [   69.371946] [ T6006]c7   6006   d_alloc_parallel+0x48/0x538
+     *   [   69.377578] [ T6006]c7   6006   __lookup_slow+0x60/0x15c
+     *   ...
+     *   [   69.547684] [ T6006]c7   6006  CPU: 7 PID: 6006 Comm: sh Tainted: G S       C O      ...
+     *   [   69.558923] [ T6006]c7   6006  Hardware name: <REDACTED>
+     *   [   69.567059] [ T6006]c7   6006  =========================================================
+     *
+     *   We rely on the kernel printing task/CPU ID for every log line (CONFIG_PRINTK_CALLER=y).
+     *   E.g. for the above report the task ID is T6006. Report lines may interleave with lines
+     *   printed by other kernel tasks, which will have different task IDs, so in order to collect
+     *   the report we:
+     *    - find the next occurrence of the "BUG: " line in the kernel log, parse it to obtain the
+     *      task ID and the tool name;
+     *    - scan the rest of dmesg output and pick every line that has the same task ID, until we
+     *      encounter a horizontal ruler, i.e.:
+     *      [   69.567059] [ T6006]c7   6006  ======================================================
+     *    - add that line to the error report, unless it contains sensitive information (see
+     *      logLinePotentiallySensitive())
+     *    - repeat the above steps till the last report is found.
+     */
+    private void processDmesg(Context ctx) throws IOException {
+
+        /*
+         * Only SYSTEM_KASAN_ERROR_REPORT and SYSTEM_KFENCE_ERROR_REPORT are supported at the
+         * moment.
+         */
+        final String[] bugTypes = new String[] { "KASAN", "KFENCE" };
+        final String tsRegex = "^\\[[^]]+\\] ";
+        final String bugRegex =
+                tsRegex + "\\[([^]]+)\\].*BUG: (" + String.join("|", bugTypes) + "):";
+        final Pattern bugPattern = Pattern.compile(bugRegex);
+
+        Process p = new ProcessBuilder("/system/bin/timeout", "-k", "90s", "60s",
+                                       "dmesg").redirectErrorStream(true).start();
+        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+        String line = null;
+        String task = null;
+        String tool = null;
+        String bugTitle = null;
+        Pattern reportPattern = null;
+        ArrayList<String> currentReport = null;
+        String lastReport = null;
+
+        while ((line = reader.readLine()) != null) {
+            if (currentReport == null) {
+                Matcher bm = bugPattern.matcher(line);
+                if (!bm.find()) continue;
+                task = bm.group(1);
+                tool = bm.group(2);
+                bugTitle = line;
+                currentReport = new ArrayList<String>();
+                currentReport.add(line);
+                String reportRegex = tsRegex + "\\[" + task + "\\].*";
+                reportPattern = Pattern.compile(reportRegex);
+                continue;
+            }
+            Matcher rm = reportPattern.matcher(line);
+            if (!rm.matches()) continue;
+            if ((line = stripSensitiveData(line)) == null) continue;
+            if (line.contains("================================")) {
+                lastReport = String.join("\n", currentReport);
+                currentReport = null;
+                continue;
+            }
+            currentReport.add(line);
+        }
+        if (lastReport == null) {
+            Slog.w(TAG, "Could not find report in dmesg.");
+            return;
+        }
+
+        // Avoid sending the same bug report twice.
+        if (bugTitle == sLastReportedBug) return;
+
+        final String reportTag = "SYSTEM_" + tool + "_ERROR_REPORT";
+        final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
+        final String headers = getCurrentBootHeaders();
+        final String reportText = headers + lastReport;
+
+        addTextToDropBox(db, reportTag, reportText, "/dev/kmsg", LOG_SIZE);
+        sLastReportedBug = bugTitle;
     }
 
     private void removeOldUpdatePackages(Context context) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 986e2acf..d755987 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -70,6 +70,7 @@
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.app.PendingIntent;
+import android.app.usage.NetworkStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -95,7 +96,6 @@
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkPolicyListener;
-import android.net.INetworkStatsService;
 import android.net.IOnSetOemNetworkPreferenceListener;
 import android.net.IQosCallback;
 import android.net.ISocketKeepaliveCallback;
@@ -190,7 +190,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.BitUtils;
 import com.android.internal.util.IndentingPrintWriter;
@@ -204,7 +203,6 @@
 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;
 import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
 import com.android.server.connectivity.KeepaliveTracker;
@@ -331,7 +329,7 @@
     protected IDnsResolver mDnsResolver;
     @VisibleForTesting
     protected INetd mNetd;
-    private INetworkStatsService mStatsService;
+    private NetworkStatsManager mStatsManager;
     private NetworkPolicyManager mPolicyManager;
     private NetworkPolicyManagerInternal mPolicyManagerInternal;
     private final NetdCallback mNetdCallback;
@@ -1042,15 +1040,14 @@
         }
     }
 
-    public ConnectivityService(Context context, INetworkStatsService statsService) {
-        this(context, statsService, getDnsResolver(context), new IpConnectivityLog(),
+    public ConnectivityService(Context context) {
+        this(context, getDnsResolver(context), new IpConnectivityLog(),
                 NetdService.getInstance(), new Dependencies());
     }
 
     @VisibleForTesting
-    protected ConnectivityService(Context context, INetworkStatsService statsService,
-            IDnsResolver dnsresolver, IpConnectivityLog logger,
-            INetd netd, Dependencies deps) {
+    protected ConnectivityService(Context context, IDnsResolver dnsresolver,
+            IpConnectivityLog logger, INetd netd, Dependencies deps) {
         if (DBG) log("ConnectivityService starting up");
 
         mDeps = Objects.requireNonNull(deps, "missing Dependencies");
@@ -1096,7 +1093,7 @@
         // TODO: Consider making the timer customizable.
         mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
 
-        mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
+        mStatsManager = mContext.getSystemService(NetworkStatsManager.class);
         mPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
         mPolicyManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(NetworkPolicyManagerInternal.class),
@@ -1215,9 +1212,6 @@
         mSettingsObserver = new SettingsObserver(mContext, mHandler);
         registerSettingsCallbacks();
 
-        final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext, mHandler);
-        dataConnectionStats.startMonitoring();
-
         mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler);
         mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager);
         mQosCallbackTracker = new QosCallbackTracker(mHandler, mNetworkRequestCounter);
@@ -1480,7 +1474,10 @@
     @NonNull
     private NetworkInfo filterNetworkInfo(@NonNull NetworkInfo networkInfo, int type,
             @NonNull NetworkCapabilities nc, int uid, boolean ignoreBlocked) {
-        NetworkInfo filtered = new NetworkInfo(networkInfo);
+        final NetworkInfo filtered = new NetworkInfo(networkInfo);
+        // Many legacy types (e.g,. TYPE_MOBILE_HIPRI) are not actually a property of the network
+        // but only exists if an app asks about them or requests them. Ensure the requesting app
+        // gets the type it asks for.
         filtered.setType(type);
         final DetailedState state = isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)
                 ? DetailedState.BLOCKED
@@ -1889,27 +1886,49 @@
         }
     }
 
+    // TODO: Consider delete this function or turn it into a no-op method.
     @Override
     public NetworkState[] getAllNetworkState() {
         // This contains IMSI details, so make sure the caller is privileged.
         PermissionUtils.enforceNetworkStackPermission(mContext);
 
         final ArrayList<NetworkState> result = new ArrayList<>();
-        for (Network network : getAllNetworks()) {
-            final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
-            // TODO: Consider include SUSPENDED networks.
+        for (NetworkStateSnapshot snapshot : getAllNetworkStateSnapshot()) {
+            // NetworkStateSnapshot doesn't contain NetworkInfo, so need to fetch it from the
+            // NetworkAgentInfo.
+            final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(snapshot.network);
             if (nai != null && nai.networkInfo.isConnected()) {
-                // TODO (b/73321673) : NetworkState contains a copy of the
-                // NetworkCapabilities, which may contain UIDs of apps to which the
-                // network applies. Should the UIDs be cleared so as not to leak or
-                // interfere ?
-                result.add(nai.getNetworkState());
+                result.add(new NetworkState(new NetworkInfo(nai.networkInfo),
+                        snapshot.linkProperties, snapshot.networkCapabilities, snapshot.network,
+                        snapshot.subscriberId));
             }
         }
         return result.toArray(new NetworkState[result.size()]);
     }
 
     @Override
+    @NonNull
+    public List<NetworkStateSnapshot> getAllNetworkStateSnapshot() {
+        // This contains IMSI details, so make sure the caller is privileged.
+        PermissionUtils.enforceNetworkStackPermission(mContext);
+
+        final ArrayList<NetworkStateSnapshot> result = new ArrayList<>();
+        for (Network network : getAllNetworks()) {
+            final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+            // TODO: Consider include SUSPENDED networks, which should be considered as
+            //  temporary shortage of connectivity of a connected network.
+            if (nai != null && nai.networkInfo.isConnected()) {
+                // TODO (b/73321673) : NetworkStateSnapshot contains a copy of the
+                // NetworkCapabilities, which may contain UIDs of apps to which the
+                // network applies. Should the UIDs be cleared so as not to leak or
+                // interfere ?
+                result.add(nai.getNetworkStateSnapshot());
+            }
+        }
+        return result;
+    }
+
+    @Override
     public boolean isActiveNetworkMetered() {
         enforceAccessPermission();
 
@@ -2387,13 +2406,6 @@
                 final BroadcastOptions opts = BroadcastOptions.makeBasic();
                 opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
                 options = opts.toBundle();
-                final IBatteryStats bs = mDeps.getBatteryStatsService();
-                try {
-                    bs.noteConnectivityChanged(intent.getIntExtra(
-                            ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_NONE),
-                            ni.getState().toString());
-                } catch (RemoteException e) {
-                }
                 intent.addFlags(Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
             }
             try {
@@ -3193,16 +3205,16 @@
             // Invoke ConnectivityReport generation for this Network test event.
             final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(mNetId);
             if (nai == null) return;
-            final Message m = mConnectivityDiagnosticsHandler.obtainMessage(
-                    ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED,
-                    new ConnectivityReportEvent(p.timestampMillis, nai));
 
             final PersistableBundle extras = new PersistableBundle();
             extras.putInt(KEY_NETWORK_VALIDATION_RESULT, p.result);
             extras.putInt(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK, p.probesSucceeded);
             extras.putInt(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK, p.probesAttempted);
 
-            m.setData(new Bundle(extras));
+            ConnectivityReportEvent reportEvent =
+                    new ConnectivityReportEvent(p.timestampMillis, nai, extras);
+            final Message m = mConnectivityDiagnosticsHandler.obtainMessage(
+                    ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED, reportEvent);
             mConnectivityDiagnosticsHandler.sendMessage(m);
         }
 
@@ -3289,8 +3301,7 @@
 
         final Message msg = mConnectivityDiagnosticsHandler.obtainMessage(
                 ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED, detectionMethod, netId,
-                p.timestampMillis);
-        msg.setData(new Bundle(extras));
+                new Pair<>(p.timestampMillis, extras));
 
         // NetworkStateTrackerHandler currently doesn't take any actions based on data
         // stalls so send the message directly to ConnectivityDiagnosticsHandler and avoid
@@ -3831,7 +3842,24 @@
                 removeListenRequestFromNetworks(req);
             }
         }
-        mDefaultNetworkRequests.remove(nri);
+        if (mDefaultNetworkRequests.remove(nri)) {
+            // If this request was one of the defaults, then the UID rules need to be updated
+            // WARNING : if the app(s) for which this network request is the default are doing
+            // traffic, this will kill their connected sockets, even if an equivalent request
+            // is going to be reinstated right away ; unconnected traffic will go on the default
+            // until the new default is set, which will happen very soon.
+            // TODO : The only way out of this is to diff old defaults and new defaults, and only
+            // remove ranges for those requests that won't have a replacement
+            final NetworkAgentInfo satisfier = nri.getSatisfier();
+            if (null != satisfier) {
+                try {
+                    mNetd.networkRemoveUidRanges(satisfier.network.getNetId(),
+                            toUidRangeStableParcels(nri.getUids()));
+                } catch (RemoteException e) {
+                    loge("Exception setting network preference default network", e);
+                }
+            }
+        }
         mNetworkRequestCounter.decrementCount(nri.mUid);
         mNetworkRequestInfoLogs.log("RELEASE " + nri);
 
@@ -4144,13 +4172,6 @@
             // nai.networkMonitor() is thread-safe
             return nai.networkMonitor();
         }
-
-        @Override
-        public void logEvent(int eventId, String packageName) {
-            enforceSettingsPermission();
-
-            new MetricsLogger().action(eventId, packageName);
-        }
     }
 
     public boolean avoidBadWifi() {
@@ -4480,16 +4501,13 @@
                 case EVENT_SET_REQUIRE_VPN_FOR_UIDS:
                     handleSetRequireVpnForUids(toBool(msg.arg1), (UidRange[]) msg.obj);
                     break;
-                case EVENT_SET_OEM_NETWORK_PREFERENCE:
+                case EVENT_SET_OEM_NETWORK_PREFERENCE: {
                     final Pair<OemNetworkPreferences, IOnSetOemNetworkPreferenceListener> arg =
                             (Pair<OemNetworkPreferences,
                                     IOnSetOemNetworkPreferenceListener>) msg.obj;
-                    try {
-                        handleSetOemNetworkPreference(arg.first, arg.second);
-                    } catch (RemoteException e) {
-                        loge("handleMessage.EVENT_SET_OEM_NETWORK_PREFERENCE failed", e);
-                    }
+                    handleSetOemNetworkPreference(arg.first, arg.second);
                     break;
+                }
                 case EVENT_REPORT_NETWORK_ACTIVITY:
                     mNetworkActivityTracker.handleReportNetworkActivity();
                     break;
@@ -5031,10 +5049,16 @@
 
     private void onUserAdded(UserHandle user) {
         mPermissionMonitor.onUserAdded(user);
+        if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
+            handleSetOemNetworkPreference(mOemNetworkPreferences, null);
+        }
     }
 
     private void onUserRemoved(UserHandle user) {
         mPermissionMonitor.onUserRemoved(user);
+        if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
+            handleSetOemNetworkPreference(mOemNetworkPreferences, null);
+        }
     }
 
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@@ -5247,11 +5271,20 @@
             ensureAllNetworkRequestsHaveType(r);
             mRequests = initializeRequests(r);
             mNetworkRequestForCallback = nri.getNetworkRequestForCallback();
+            // Note here that the satisfier may have corresponded to an old request, that
+            // this code doesn't try to take over. While it is a small discrepancy in the
+            // structure of these requests, it will be fixed by the next rematch and it's
+            // not as bad as having an NRI not storing its real satisfier.
+            // Fixing this discrepancy would require figuring out in the copying code what
+            // is the new request satisfied by this, which is a bit complex and not very
+            // useful as no code is using it until rematch fixes it.
+            mSatisfier = nri.mSatisfier;
             mMessenger = nri.mMessenger;
             mBinder = nri.mBinder;
             mPid = nri.mPid;
             mUid = nri.mUid;
             mPendingIntent = nri.mPendingIntent;
+            mNetworkRequestCounter.incrementCountOrThrow(mUid);
             mCallingAttributionTag = nri.mCallingAttributionTag;
         }
 
@@ -5298,6 +5331,8 @@
         public String toString() {
             return "uid/pid:" + mUid + "/" + mPid + " active request Id: "
                     + (mActiveRequest == null ? null : mActiveRequest.requestId)
+                    + " callback request Id: "
+                    + mNetworkRequestForCallback.requestId
                     + " " + mRequests
                     + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
         }
@@ -7154,7 +7189,7 @@
                         toUidRangeStableParcels(nri.getUids()));
             }
         } catch (RemoteException | ServiceSpecificException e) {
-            loge("Exception setting OEM network preference default network :" + e);
+            loge("Exception setting app default network", e);
         }
     }
 
@@ -7209,13 +7244,13 @@
     private static class NetworkReassignment {
         static class RequestReassignment {
             @NonNull public final NetworkRequestInfo mNetworkRequestInfo;
-            @NonNull public final NetworkRequest mOldNetworkRequest;
-            @NonNull public final NetworkRequest mNewNetworkRequest;
+            @Nullable public final NetworkRequest mOldNetworkRequest;
+            @Nullable public final NetworkRequest mNewNetworkRequest;
             @Nullable public final NetworkAgentInfo mOldNetwork;
             @Nullable public final NetworkAgentInfo mNewNetwork;
             RequestReassignment(@NonNull final NetworkRequestInfo networkRequestInfo,
-                    @NonNull final NetworkRequest oldNetworkRequest,
-                    @NonNull final NetworkRequest newNetworkRequest,
+                    @Nullable final NetworkRequest oldNetworkRequest,
+                    @Nullable final NetworkRequest newNetworkRequest,
                     @Nullable final NetworkAgentInfo oldNetwork,
                     @Nullable final NetworkAgentInfo newNetwork) {
                 mNetworkRequestInfo = networkRequestInfo;
@@ -7226,7 +7261,9 @@
             }
 
             public String toString() {
-                return mNetworkRequestInfo.mRequests.get(0).requestId + " : "
+                final NetworkRequest requestToShow = null != mNewNetworkRequest
+                        ? mNewNetworkRequest : mNetworkRequestInfo.mRequests.get(0);
+                return requestToShow.requestId + " : "
                         + (null != mOldNetwork ? mOldNetwork.network.getNetId() : "null")
                         + " → " + (null != mNewNetwork ? mNewNetwork.network.getNetId() : "null");
             }
@@ -7239,7 +7276,7 @@
         }
 
         void addRequestReassignment(@NonNull final RequestReassignment reassignment) {
-            if (!Build.IS_USER) {
+            if (Build.IS_DEBUGGABLE) {
                 // The code is never supposed to add two reassignments of the same request. Make
                 // sure this stays true, but without imposing this expensive check on all
                 // reassignments on all user devices.
@@ -7286,14 +7323,14 @@
     }
 
     private void updateSatisfiersForRematchRequest(@NonNull final NetworkRequestInfo nri,
-            @NonNull final NetworkRequest previousRequest,
-            @NonNull final NetworkRequest newRequest,
+            @Nullable final NetworkRequest previousRequest,
+            @Nullable final NetworkRequest newRequest,
             @Nullable final NetworkAgentInfo previousSatisfier,
             @Nullable final NetworkAgentInfo newSatisfier,
             final long now) {
         if (null != newSatisfier && mNoServiceNetwork != newSatisfier) {
             if (VDBG) log("rematch for " + newSatisfier.toShortString());
-            if (null != previousSatisfier && mNoServiceNetwork != previousSatisfier) {
+            if (null != previousRequest && null != previousSatisfier) {
                 if (VDBG || DDBG) {
                     log("   accepting network in place of " + previousSatisfier.toShortString());
                 }
@@ -7310,12 +7347,13 @@
                 newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE);
             }
 
+            // if newSatisfier is not null, then newRequest may not be null.
             newSatisfier.unlingerRequest(newRequest.requestId);
             if (!newSatisfier.addRequest(newRequest)) {
                 Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
                         + newRequest);
             }
-        } else if (null != previousSatisfier) {
+        } else if (null != previousRequest && null != previousSatisfier) {
             if (DBG) {
                 log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
                         + " request " + previousRequest.requestId);
@@ -7913,7 +7951,8 @@
      *
      * Must be called on the handler thread.
      */
-    private Network[] getDefaultNetworks() {
+    @NonNull
+    private ArrayList<Network> getDefaultNetworks() {
         ensureRunningOnConnectivityServiceThread();
         final ArrayList<Network> defaultNetworks = new ArrayList<>();
         final Set<Integer> activeNetIds = new ArraySet<>();
@@ -7927,7 +7966,7 @@
                 defaultNetworks.add(nai.network);
             }
         }
-        return defaultNetworks.toArray(new Network[0]);
+        return defaultNetworks;
     }
 
     /**
@@ -7952,8 +7991,8 @@
                         state.legacyNetworkType);
                 snapshots.add(snapshot);
             }
-            mStatsService.forceUpdateIfaces(getDefaultNetworks(), snapshots.toArray(
-                    new NetworkStateSnapshot[0]), activeIface, underlyingNetworkInfos);
+            mStatsManager.notifyNetworkStatus(getDefaultNetworks(),
+                    snapshots, activeIface, Arrays.asList(underlyingNetworkInfos));
         } catch (Exception ignored) {
         }
     }
@@ -8272,24 +8311,16 @@
                     final ConnectivityReportEvent reportEvent =
                             (ConnectivityReportEvent) msg.obj;
 
-                    // This is safe because {@link
-                    // NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} receives a
-                    // PersistableBundle and converts it to the Bundle in the incoming Message. If
-                    // {@link NetworkMonitorCallbacks#notifyNetworkTested} is called, msg.data will
-                    // not be set. This is also safe, as msg.getData() will return an empty Bundle.
-                    final PersistableBundle extras = new PersistableBundle(msg.getData());
-                    handleNetworkTestedWithExtras(reportEvent, extras);
+                    handleNetworkTestedWithExtras(reportEvent, reportEvent.mExtras);
                     break;
                 }
                 case EVENT_DATA_STALL_SUSPECTED: {
                     final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+                    final Pair<Long, PersistableBundle> arg =
+                            (Pair<Long, PersistableBundle>) msg.obj;
                     if (nai == null) break;
 
-                    // This is safe because NetworkMonitorCallbacks#notifyDataStallSuspected
-                    // receives a PersistableBundle and converts it to the Bundle in the incoming
-                    // Message.
-                    final PersistableBundle extras = new PersistableBundle(msg.getData());
-                    handleDataStallSuspected(nai, (long) msg.obj, msg.arg1, extras);
+                    handleDataStallSuspected(nai, arg.first, msg.arg1, arg.second);
                     break;
                 }
                 case EVENT_NETWORK_CONNECTIVITY_REPORTED: {
@@ -8353,10 +8384,13 @@
     private static class ConnectivityReportEvent {
         private final long mTimestampMillis;
         @NonNull private final NetworkAgentInfo mNai;
+        private final PersistableBundle mExtras;
 
-        private ConnectivityReportEvent(long timestampMillis, @NonNull NetworkAgentInfo nai) {
+        private ConnectivityReportEvent(long timestampMillis, @NonNull NetworkAgentInfo nai,
+                PersistableBundle p) {
             mTimestampMillis = timestampMillis;
             mNai = nai;
+            mExtras = p;
         }
     }
 
@@ -9033,23 +9067,27 @@
 
     private void handleSetOemNetworkPreference(
             @NonNull final OemNetworkPreferences preference,
-            @NonNull final IOnSetOemNetworkPreferenceListener listener) throws RemoteException {
+            @Nullable final IOnSetOemNetworkPreferenceListener listener) {
         Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
         if (DBG) {
             log("set OEM network preferences :" + preference.toString());
         }
         final ArraySet<NetworkRequestInfo> nris =
                 new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
-        updateDefaultNetworksForOemNetworkPreference(nris);
+        replaceDefaultNetworkRequestsForPreference(nris);
         mOemNetworkPreferences = preference;
         // TODO http://b/176496396 persist data to shared preferences.
 
         if (null != listener) {
-            listener.onComplete();
+            try {
+                listener.onComplete();
+            } catch (RemoteException e) {
+                loge("Can't send onComplete in handleSetOemNetworkPreference", e);
+            }
         }
     }
 
-    private void updateDefaultNetworksForOemNetworkPreference(
+    private void replaceDefaultNetworkRequestsForPreference(
             @NonNull final Set<NetworkRequestInfo> nris) {
         // Pass in a defensive copy as this collection will be updated on remove.
         handleRemoveNetworkRequests(new ArraySet<>(mDefaultNetworkRequests));
@@ -9061,10 +9099,10 @@
         mDefaultNetworkRequests.addAll(nris);
         final ArraySet<NetworkRequestInfo> perAppCallbackRequestsToUpdate =
                 getPerAppCallbackRequestsToUpdate();
-        handleRemoveNetworkRequests(perAppCallbackRequestsToUpdate);
         final ArraySet<NetworkRequestInfo> nrisToRegister = new ArraySet<>(nris);
         nrisToRegister.addAll(
                 createPerAppCallbackRequestsToRegister(perAppCallbackRequestsToUpdate));
+        handleRemoveNetworkRequests(perAppCallbackRequestsToUpdate);
         handleRegisterNetworkRequests(nrisToRegister);
     }
 
@@ -9135,6 +9173,14 @@
         return callbackRequestsToRegister;
     }
 
+    private static void setNetworkRequestUids(@NonNull final List<NetworkRequest> requests,
+            @NonNull final Set<UidRange> uids) {
+        final Set<UidRange> ranges = new ArraySet<>(uids);
+        for (final NetworkRequest req : requests) {
+            req.networkCapabilities.setUids(ranges);
+        }
+    }
+
     /**
      * Class used to generate {@link NetworkRequestInfo} based off of {@link OemNetworkPreferences}.
      */
@@ -9163,6 +9209,14 @@
                 @NonNull final OemNetworkPreferences preference) {
             final SparseArray<Set<Integer>> uids = new SparseArray<>();
             final PackageManager pm = mContext.getPackageManager();
+            final List<UserHandle> users =
+                    mContext.getSystemService(UserManager.class).getUserHandles(true);
+            if (null == users || users.size() == 0) {
+                if (VDBG || DDBG) {
+                    log("No users currently available for setting the OEM network preference.");
+                }
+                return uids;
+            }
             for (final Map.Entry<String, Integer> entry :
                     preference.getNetworkPreferences().entrySet()) {
                 @OemNetworkPreferences.OemNetworkPreference final int pref = entry.getValue();
@@ -9171,7 +9225,10 @@
                     if (!uids.contains(pref)) {
                         uids.put(pref, new ArraySet<>());
                     }
-                    uids.get(pref).add(uid);
+                    for (final UserHandle ui : users) {
+                        // Add the rules for all users as this policy is device wide.
+                        uids.get(pref).add(UserHandle.getUid(ui, uid));
+                    }
                 } catch (PackageManager.NameNotFoundException e) {
                     // Although this may seem like an error scenario, it is ok that uninstalled
                     // packages are sent on a network preference as the system will watch for
@@ -9211,7 +9268,11 @@
                             + " called with invalid preference of " + preference);
             }
 
-            setOemNetworkRequestUids(requests, uids);
+            final ArraySet ranges = new ArraySet<Integer>();
+            for (final int uid : uids) {
+                ranges.add(new UidRange(uid, uid));
+            }
+            setNetworkRequestUids(requests, ranges);
             return new NetworkRequestInfo(requests);
         }
 
@@ -9244,16 +9305,5 @@
             netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
             return netCap;
         }
-
-        private void setOemNetworkRequestUids(@NonNull final List<NetworkRequest> requests,
-                @NonNull final Set<Integer> uids) {
-            final Set<UidRange> ranges = new ArraySet<>();
-            for (final int uid : uids) {
-                ranges.add(new UidRange(uid, uid));
-            }
-            for (final NetworkRequest req : requests) {
-                req.networkCapabilities.setUids(ranges);
-            }
-        }
     }
 }
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
index 097441f..b992208 100644
--- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -20,8 +20,6 @@
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 
 import android.content.Context;
-import android.net.INetworkStatsService;
-import android.os.ServiceManager;
 import android.util.Log;
 
 /**
@@ -37,7 +35,7 @@
         // Load JNI libraries used by ConnectivityService and its dependencies
         System.loadLibrary("service-connectivity");
         // TODO: Define formal APIs to get the needed services.
-        mConnectivity = new ConnectivityService(context, getNetworkStatsService());
+        mConnectivity = new ConnectivityService(context);
     }
 
     @Override
@@ -46,9 +44,4 @@
         publishBinderService(Context.CONNECTIVITY_SERVICE, mConnectivity,
                 /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
     }
-
-    private INetworkStatsService getNetworkStatsService() {
-        return INetworkStatsService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
-    }
 }
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 2906cee..9067028 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -839,23 +839,34 @@
         }
     };
 
+    // Helper method to copy some of the maps.
+    private static <E> void copyInto(ArrayMap<String, E[]> l, ArrayMap<String, E[]> r) {
+        final int end = r.size();
+        l.ensureCapacity(end);
+        for (int i = 0; i < end; i++) {
+            final E[] val = r.valueAt(i);
+            final String key = r.keyAt(i);
+            l.put(key, Arrays.copyOf(val, val.length));
+        }
+    }
+
     // Make <this> a copy of <orig>.  The presumption is that <this> is empty but all
     // arrays are cleared out explicitly, just to be sure.
     protected void copyFrom(IntentResolver orig) {
         mFilters.clear();
         mFilters.addAll(orig.mFilters);
         mTypeToFilter.clear();
-        mTypeToFilter.putAll(orig.mTypeToFilter);
+        copyInto(mTypeToFilter, orig.mTypeToFilter);
         mBaseTypeToFilter.clear();
-        mBaseTypeToFilter.putAll(orig.mBaseTypeToFilter);
+        copyInto(mBaseTypeToFilter, orig.mBaseTypeToFilter);
         mWildTypeToFilter.clear();
-        mWildTypeToFilter.putAll(orig.mWildTypeToFilter);
+        copyInto(mWildTypeToFilter, orig.mWildTypeToFilter);
         mSchemeToFilter.clear();
-        mSchemeToFilter.putAll(orig.mSchemeToFilter);
+        copyInto(mSchemeToFilter, orig.mSchemeToFilter);
         mActionToFilter.clear();
-        mActionToFilter.putAll(orig.mActionToFilter);
+        copyInto(mActionToFilter, orig.mActionToFilter);
         mTypedActionToFilter.clear();
-        mTypedActionToFilter.putAll(orig.mTypedActionToFilter);
+        copyInto(mTypedActionToFilter, orig.mTypedActionToFilter);
     }
 
     /**
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c5233f4..27b648e 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -578,6 +578,12 @@
      */
     private static final int PBKDF2_HASH_ROUNDS = 1024;
 
+    private static final String ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY =
+            "anr_delay_millis";
+
+    private static final String ANR_DELAY_NOTIFY_EXTERNAL_STORAGE_SERVICE_DEVICE_CONFIG_KEY =
+            "anr_delay_notify_external_storage_service";
+
     /**
      * Mounted OBB tracking information. Used to track the current state of all
      * OBBs.
@@ -948,25 +954,51 @@
         }
     }
 
-    // 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");
+            if (!isAppIoBlocked(uid)) {
+                return 0;
+            }
+
+            int delay = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                    ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY, 0);
+            Slog.v(TAG, "getAnrDelayMillis for " + packageName + ". " + delay + "ms");
             return delay;
         }
 
         @Override
         public void onAnrDelayStarted(String packageName, int uid) {
-            Log.d(TAG, "onAnrDelayStarted: " + packageName);
+            if (!isAppIoBlocked(uid)) {
+                return;
+            }
+
+            boolean notifyExternalStorageService = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                    ANR_DELAY_NOTIFY_EXTERNAL_STORAGE_SERVICE_DEVICE_CONFIG_KEY, true);
+            if (notifyExternalStorageService) {
+                Slog.d(TAG, "onAnrDelayStarted for " + packageName
+                        + ". Notifying external storage service");
+                try {
+                    mStorageSessionController.notifyAnrDelayStarted(packageName, uid, 0 /* tid */,
+                            StorageManager.APP_IO_BLOCKED_REASON_TRANSCODING);
+                } catch (ExternalStorageServiceException e) {
+                    Slog.e(TAG, "Failed to notify ANR delay started for " + packageName, e);
+                }
+            } else {
+                // TODO(b/170973510): Implement framework spinning dialog for ANR delay
+            }
         }
 
         @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;
+            if (isAppIoBlocked(uid)) {
+                Slog.d(TAG, "onAnrDelayCompleted for " + packageName + ". Showing ANR dialog...");
+                return true;
+            } else {
+                Slog.d(TAG, "onAnrDelayCompleted for " + packageName + ". Skipping ANR dialog...");
+                return false;
+            }
         }
     }
 
@@ -4690,5 +4722,19 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public List<String> getPrimaryVolumeIds() {
+            final List<String> primaryVolumeIds = new ArrayList<>();
+            synchronized (mLock) {
+                for (int i = 0; i < mVolumes.size(); i++) {
+                    final VolumeInfo vol = mVolumes.valueAt(i);
+                    if (vol.isPrimary()) {
+                        primaryVolumeIds.add(vol.getId());
+                    }
+                }
+            }
+            return primaryVolumeIds;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 5a5f1a3..978bd64 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -72,6 +72,7 @@
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
@@ -153,7 +154,7 @@
 
         int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
 
-        boolean matchPhoneStateListenerEvent(int event) {
+        boolean matchTelephonyCallbackEvent(int event) {
             return (callback != null) && (this.eventList.contains(event));
         }
 
@@ -198,8 +199,8 @@
         public int getRegistrationLimit() {
             return Binder.withCleanCallingIdentity(() ->
                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY,
-                            PhoneStateListener.FLAG_PER_PID_REGISTRATION_LIMIT,
-                            PhoneStateListener.DEFAULT_PER_PID_REGISTRATION_LIMIT));
+                            TelephonyCallback.FLAG_PER_PID_REGISTRATION_LIMIT,
+                            TelephonyCallback.DEFAULT_PER_PID_REGISTRATION_LIMIT));
         }
 
         /**
@@ -210,7 +211,7 @@
          */
         public boolean isRegistrationLimitEnabledInPlatformCompat(int uid) {
             return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
-                    PhoneStateListener.PHONE_STATE_LISTENER_LIMIT_CHANGE_ID, uid));
+                    TelephonyCallback.PHONE_STATE_LISTENER_LIMIT_CHANGE_ID, uid));
         }
     }
 
@@ -332,37 +333,37 @@
     static {
         REQUIRE_PRECISE_PHONE_STATE_PERMISSION = new HashSet<Integer>();
         REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+                TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
         REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
+                TelephonyCallback.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
         REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
+                TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED);
         REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+                TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
         REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
+                TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED);
         REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
-        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+                TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE);
+        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
         REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+                TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
         REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
-                PhoneStateListener.EVENT_DATA_ENABLED_CHANGED);
+                TelephonyCallback.EVENT_DATA_ENABLED_CHANGED);
     }
 
     private boolean isLocationPermissionRequired(Set<Integer> events) {
-        return events.contains(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_CELL_INFO_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_REGISTRATION_FAILURE)
-                || events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+        return events.contains(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_CELL_INFO_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_REGISTRATION_FAILURE)
+                || events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
     }
 
     private boolean isPhoneStatePermissionRequired(Set<Integer> events) {
-        return events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+        return events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
     }
 
     private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) {
@@ -375,14 +376,14 @@
     }
 
     private boolean isActiveEmergencySessionPermissionRequired(Set<Integer> events) {
-        return events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)
-                || events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
+        return events.contains(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL)
+                || events.contains(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS);
     }
 
     private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) {
-        return events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
-                || events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
+        return events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
     }
 
     private static final int MSG_USER_SWITCHED = 1;
@@ -903,7 +904,7 @@
                 log("listen:  Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
             }
             if (notifyNow && validatePhoneId(phoneId)) {
-                if (events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)){
                     try {
                         if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
                         ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
@@ -920,7 +921,7 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)) {
                     try {
                         if (mSignalStrength[phoneId] != null) {
                             int gsmSignalStrength = mSignalStrength[phoneId]
@@ -933,7 +934,7 @@
                     }
                 }
                 if (events.contains(
-                        PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
+                        TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
                     try {
                         r.callback.onMessageWaitingIndicatorChanged(
                                 mMessageWaiting[phoneId]);
@@ -942,7 +943,7 @@
                     }
                 }
                 if (events.contains(
-                        PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
+                        TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
                     try {
                         r.callback.onCallForwardingIndicatorChanged(
                                 mCallForwarding[phoneId]);
@@ -951,7 +952,7 @@
                     }
                 }
                 if (validateEventAndUserLocked(
-                        r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) {
+                        r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)) {
                     try {
                         if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]);
                         if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
@@ -963,7 +964,7 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_CALL_STATE_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) {
                     try {
                         r.callback.onCallStateChanged(mCallState[phoneId],
                                 getCallIncomingNumber(r, phoneId));
@@ -971,7 +972,7 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
                     try {
                         r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
                                 mDataConnectionNetworkType[phoneId]);
@@ -979,14 +980,14 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)) {
                     try {
                         r.callback.onDataActivity(mDataActivity[phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
                     try {
                         if (mSignalStrength[phoneId] != null) {
                             r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
@@ -996,7 +997,7 @@
                     }
                 }
                 if (events.contains(
-                        PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+                        TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
                     updateReportSignalStrengthDecision(r.subId);
                     try {
                         if (mSignalStrength[phoneId] != null) {
@@ -1007,7 +1008,7 @@
                     }
                 }
                 if (validateEventAndUserLocked(
-                        r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) {
+                        r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) {
                     try {
                         if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
                                 + mCellInfo.get(phoneId));
@@ -1019,14 +1020,14 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)) {
                     try {
                         r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) {
                     try {
                         r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
                                 mCallPreciseDisconnectCause[phoneId]);
@@ -1034,7 +1035,7 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) {
                     try {
                         r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId));
                     } catch (RemoteException ex) {
@@ -1042,7 +1043,7 @@
                     }
                 }
                 if (events.contains(
-                        PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) {
+                        TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) {
                     try {
                         for (PreciseDataConnectionState pdcs
                                 : mPreciseDataConnectionStates.get(phoneId).values()) {
@@ -1052,14 +1053,14 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)) {
                     try {
                         r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) {
                     try {
                         r.callback.onVoiceActivationStateChanged(
                                 mVoiceActivationState[phoneId]);
@@ -1067,21 +1068,21 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED)) {
                     try {
                         r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
                     try {
                         r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)) {
                     try {
                         if (mTelephonyDisplayInfos[phoneId] != null) {
                             r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]);
@@ -1090,14 +1091,14 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) {
                     try {
                         r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED)) {
                     try {
                         r.callback.onPhoneCapabilityChanged(mPhoneCapability);
                     } catch (RemoteException ex) {
@@ -1105,35 +1106,35 @@
                     }
                 }
                 if (events.contains(
-                        PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
+                        TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
                     try {
                         r.callback.onActiveDataSubIdChanged(mActiveDataSubId);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)) {
                     try {
                         r.callback.onRadioPowerStateChanged(mRadioPowerState);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)) {
                     try {
                         r.callback.onSrvccStateChanged(mSrvccState[phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)) {
                     try {
                         r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
-                if (events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED)) {
+                if (events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED)) {
                     BarringInfo barringInfo = mBarringInfo.get(phoneId);
                     BarringInfo biNoLocation = barringInfo != null
                             ? barringInfo.createLocationInfoSanitizedCopy() : null;
@@ -1147,7 +1148,7 @@
                     }
                 }
                 if (events.contains(
-                        PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) {
+                        TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) {
                     try {
                         r.callback.onPhysicalChannelConfigChanged(
                                 mPhysicalChannelConfigs);
@@ -1156,7 +1157,7 @@
                     }
                 }
                 if (events.contains(
-                        PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) {
+                        TelephonyCallback.EVENT_DATA_ENABLED_CHANGED)) {
                     try {
                         r.callback.onDataEnabledChanged(
                                 mIsDataEnabled[phoneId], mDataEnabledReason[phoneId]);
@@ -1165,7 +1166,7 @@
                     }
                 }
                 if (events.contains(
-                        PhoneStateListener.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) {
+                        TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) {
                     try {
                         r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
                     } catch (RemoteException ex) {
@@ -1183,8 +1184,8 @@
             for (Record r : mRecords) {
                 // If any of the system clients wants to always listen to signal strength,
                 // we need to set it on.
-                if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+                if (r.matchTelephonyCallbackEvent(
+                        TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
                     telephonyManager.createForSubscriptionId(subscriptionId)
                             .setAlwaysReportSignalStrength(true);
                     return;
@@ -1233,7 +1234,7 @@
                     throw new IllegalStateException(errorMsg);
                 }
             } else if (doesLimitApply && numRecordsForPid
-                    >= PhoneStateListener.DEFAULT_PER_PID_REGISTRATION_LIMIT / 2) {
+                    >= TelephonyCallback.DEFAULT_PER_PID_REGISTRATION_LIMIT / 2) {
                 // Log the warning independently of the dynamically set limit -- apps shouldn't be
                 // doing this regardless of whether we're throwing them an exception for it.
                 Rlog.w(TAG, "Pid " + callingPid + " has exceeded half the number of permissible"
@@ -1284,8 +1285,8 @@
                     // Every time a client that is registrating to always receive the signal
                     // strength is removed from registry records, we need to check if
                     // the signal strength decision needs to update on its slot.
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
                         updateReportSignalStrengthDecision(r.subId);
                     }
                     return;
@@ -1305,7 +1306,7 @@
 
         synchronized (mRecords) {
             for (Record r : mRecords) {
-                if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED)
+                if (r.matchTelephonyCallbackEvent(TelephonyCallback.EVENT_CALL_STATE_CHANGED)
                         && (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
                     try {
                         // Ensure the listener has read call log permission; if they do not return
@@ -1340,7 +1341,7 @@
                 mCallState[phoneId] = state;
                 mCallIncomingNumber[phoneId] = incomingNumber;
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(TelephonyCallback.EVENT_CALL_STATE_CHANGED)
                             && (r.subId == subId)
                             && (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
                         try {
@@ -1382,8 +1383,8 @@
                         log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
                                 + " phoneId=" + phoneId + " state=" + state);
                     }
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
 
                         try {
@@ -1444,8 +1445,8 @@
                     }
                     try {
                         if ((activationType == SIM_ACTIVATION_TYPE_VOICE)
-                                && r.matchPhoneStateListenerEvent(
-                                        PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
+                                && r.matchTelephonyCallbackEvent(
+                                        TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
                                 && idMatch(r.subId, subId, phoneId)) {
                             if (DBG) {
                                 log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r
@@ -1455,8 +1456,8 @@
                             r.callback.onVoiceActivationStateChanged(activationState);
                         }
                         if ((activationType == SIM_ACTIVATION_TYPE_DATA)
-                                && r.matchPhoneStateListenerEvent(
-                                        PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)
+                                && r.matchTelephonyCallbackEvent(
+                                        TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED)
                                 && idMatch(r.subId, subId, phoneId)) {
                             if (DBG) {
                                 log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r
@@ -1495,11 +1496,10 @@
                         log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
                                 + " phoneId=" + phoneId + " ss=" + signalStrength);
                     }
-                    if ((r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)
-                            || r.matchPhoneStateListenerEvent(
-                                    PhoneStateListener.
-                                            EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
+                    if ((r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
+                            || r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG) {
@@ -1512,8 +1512,8 @@
                             mRemoveList.add(r.binder);
                         }
                     }
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             int gsmSignalStrength = signalStrength.getGsmSignalStrength();
@@ -1559,8 +1559,8 @@
                     log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
                 }
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onCarrierNetworkChange(active);
@@ -1592,7 +1592,7 @@
                 mCellInfo.set(phoneId, cellInfo);
                 for (Record r : mRecords) {
                     if (validateEventAndUserLocked(
-                            r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)
+                            r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)
                             && idMatch(r.subId, subId, phoneId)
                             && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
                                     && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
@@ -1625,8 +1625,8 @@
             if (validatePhoneId(phoneId)) {
                 mMessageWaiting[phoneId] = mwi;
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onMessageWaitingIndicatorChanged(mwi);
@@ -1652,8 +1652,8 @@
             if (validatePhoneId(phoneId)) {
                 mUserMobileDataState[phoneId] = state;
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onUserMobileDataStateChanged(state);
@@ -1691,8 +1691,8 @@
             if (validatePhoneId(phoneId)) {
                 mTelephonyDisplayInfos[phoneId] = telephonyDisplayInfo;
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)
                             && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) {
                         try {
                             r.callback.onDisplayInfoChanged(telephonyDisplayInfo);
@@ -1723,8 +1723,8 @@
             if (validatePhoneId(phoneId)) {
                 mCallForwarding[phoneId] = cfi;
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onCallForwardingIndicatorChanged(cfi);
@@ -1752,8 +1752,8 @@
                 mDataActivity[phoneId] = state;
                 for (Record r : mRecords) {
                     // Notify by correct subId.
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onDataActivity(state);
@@ -1800,8 +1800,8 @@
                     log(str);
                     mLocalLog.log(str);
                     for (Record r : mRecords) {
-                        if (r.matchPhoneStateListenerEvent(
-                                PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)
+                        if (r.matchTelephonyCallbackEvent(
+                                TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)
                                 && idMatch(r.subId, subId, phoneId)) {
                             try {
                                 if (DBG) {
@@ -1825,8 +1825,8 @@
                         .remove(key);
                 if (!Objects.equals(oldState, preciseState)) {
                     for (Record r : mRecords) {
-                        if (r.matchPhoneStateListenerEvent(
-                                PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)
+                        if (r.matchTelephonyCallbackEvent(
+                                TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)
                                 && idMatch(r.subId, subId, phoneId)) {
                             try {
                                 r.callback.onPreciseDataConnectionStateChanged(preciseState);
@@ -1872,7 +1872,7 @@
                 mCellIdentity[phoneId] = cellIdentity;
                 for (Record r : mRecords) {
                     if (validateEventAndUserLocked(
-                            r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)
+                            r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)
                             && idMatch(r.subId, subId, phoneId)
                             && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
                                     && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
@@ -1925,8 +1925,8 @@
                 }
 
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
@@ -1934,8 +1934,8 @@
                             mRemoveList.add(r.binder);
                         }
                     }
-                    if (notifyCallAttributes && r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)
+                    if (notifyCallAttributes && r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
@@ -1959,8 +1959,9 @@
                 mCallDisconnectCause[phoneId] = disconnectCause;
                 mCallPreciseDisconnectCause[phoneId] = preciseDisconnectCause;
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(PhoneStateListener
-                            .LISTEN_CALL_DISCONNECT_CAUSES) && idMatch(r.subId, subId, phoneId)) {
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)
+                            && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
                                     mCallPreciseDisconnectCause[phoneId]);
@@ -1983,8 +1984,8 @@
             if (validatePhoneId(phoneId)) {
                 mImsReasonInfo.set(phoneId, imsReasonInfo);
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
@@ -2015,8 +2016,8 @@
             if (validatePhoneId(phoneId)) {
                 mSrvccState[phoneId] = state;
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
@@ -2044,8 +2045,8 @@
                     if (VDBG) {
                         log("notifyOemHookRawEventForSubscriber:  r=" + r + " subId=" + subId);
                     }
-                    if ((r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_OEM_HOOK_RAW))
+                    if ((r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_OEM_HOOK_RAW))
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onOemHookRawEvent(rawData);
@@ -2072,8 +2073,8 @@
             mPhoneCapability = capability;
 
             for (Record r : mRecords) {
-                if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) {
+                if (r.matchTelephonyCallbackEvent(
+                        TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED)) {
                     try {
                         r.callback.onPhoneCapabilityChanged(capability);
                     } catch (RemoteException ex) {
@@ -2097,8 +2098,8 @@
         mActiveDataSubId = activeDataSubId;
         synchronized (mRecords) {
             for (Record r : mRecords) {
-                if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
+                if (r.matchTelephonyCallbackEvent(
+                        TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
                     try {
                         r.callback.onActiveDataSubIdChanged(activeDataSubId);
                     } catch (RemoteException ex) {
@@ -2124,8 +2125,8 @@
                 mRadioPowerState = state;
 
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onRadioPowerStateChanged(state);
@@ -2153,8 +2154,8 @@
                 mEmergencyNumberList = tm.getEmergencyNumberList();
 
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
@@ -2185,8 +2186,8 @@
             }
             for (Record r : mRecords) {
                 // Send to all listeners regardless of subscription
-                if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)) {
+                if (r.matchTelephonyCallbackEvent(
+                        TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL)) {
                     try {
                         r.callback.onOutgoingEmergencyCall(emergencyNumber, subId);
                     } catch (RemoteException ex) {
@@ -2204,13 +2205,14 @@
         if (!checkNotifyPermission("notifyOutgoingEmergencySms()")) {
             return;
         }
+
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
                 mOutgoingSmsEmergencyNumber[phoneId] = emergencyNumber;
                 for (Record r : mRecords) {
                     // Send to all listeners regardless of subscription
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS)) {
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS)) {
                         try {
                             r.callback.onOutgoingEmergencySms(emergencyNumber, subId);
                         } catch (RemoteException ex) {
@@ -2239,8 +2241,8 @@
                         callNetworkType, callQuality);
 
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
@@ -2270,8 +2272,8 @@
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_REGISTRATION_FAILURE)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_REGISTRATION_FAILURE)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onRegistrationFailed(
@@ -2313,8 +2315,8 @@
                 BarringInfo biNoLocation = barringInfo.createLocationInfoSanitizedCopy();
                 if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_BARRING_INFO_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_BARRING_INFO_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
@@ -2356,8 +2358,8 @@
             if (validatePhoneId(phoneId)) {
                 mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId));
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
@@ -2386,7 +2388,7 @@
      *                {@link TelephonyManager}.
      */
     public void notifyDataEnabled(int phoneId, int subId, boolean enabled,
-                                  @TelephonyManager.DataEnabledReason int reason) {
+            @TelephonyManager.DataEnabledReason int reason) {
         if (!checkNotifyPermission("notifyDataEnabled()")) {
             return;
         }
@@ -2401,8 +2403,8 @@
                 mIsDataEnabled[phoneId] = enabled;
                 mDataEnabledReason[phoneId] = reason;
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_DATA_ENABLED_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onDataEnabledChanged(enabled, reason);
@@ -2435,8 +2437,8 @@
                 mAllowedNetworkTypesList = allowedNetworkTypesList;
 
                 for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (VDBG) {
@@ -2817,7 +2819,7 @@
                     android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
         }
 
-        if ((events.contains(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) {
+        if ((events.contains(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null);
         }
@@ -2847,7 +2849,7 @@
         try {
             foregroundUser = ActivityManager.getCurrentUser();
             valid = UserHandle.getUserId(r.callerUid) == foregroundUser
-                    && r.matchPhoneStateListenerEvent(event);
+                    && r.matchTelephonyCallbackEvent(event);
             if (DBG | DBG_LOC) {
                 log("validateEventAndUserLocked: valid=" + valid
                         + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
@@ -2972,7 +2974,7 @@
             return;
         }
 
-        if ((events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED))) {
+        if ((events.contains(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED))) {
             try {
                 if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" +
                         mServiceState[phoneId]);
@@ -2991,9 +2993,9 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)
+        if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
                 || events.contains(
-                PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+                TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
             try {
                 if (mSignalStrength[phoneId] != null) {
                     SignalStrength signalStrength = mSignalStrength[phoneId];
@@ -3008,7 +3010,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) {
+        if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)) {
             try {
                 if (mSignalStrength[phoneId] != null) {
                     int gsmSignalStrength = mSignalStrength[phoneId]
@@ -3025,7 +3027,7 @@
             }
         }
 
-        if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) {
+        if (validateEventAndUserLocked(r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) {
             try {
                 if (DBG_LOC) {
                     log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
@@ -3040,7 +3042,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
+        if (events.contains(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
             try {
                 if (VDBG) {
                     log("checkPossibleMissNotify: onUserMobileDataStateChanged phoneId="
@@ -3052,7 +3054,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) {
+        if (events.contains(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)) {
             try {
                 if (VDBG) {
                     log("checkPossibleMissNotify: onDisplayInfoChanged phoneId="
@@ -3066,7 +3068,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
+        if (events.contains(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
             try {
                 if (VDBG) {
                     log("checkPossibleMissNotify: onMessageWaitingIndicatorChanged phoneId="
@@ -3079,7 +3081,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
+        if (events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
             try {
                 if (VDBG) {
                     log("checkPossibleMissNotify: onCallForwardingIndicatorChanged phoneId="
@@ -3092,7 +3094,7 @@
             }
         }
 
-        if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) {
+        if (validateEventAndUserLocked(r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)) {
             try {
                 if (DBG_LOC) {
                     log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = "
@@ -3108,7 +3110,7 @@
             }
         }
 
-        if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
+        if (events.contains(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
             try {
                 if (DBG) {
                     log("checkPossibleMissNotify: onDataConnectionStateChanged(mDataConnectionState"
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 8d5d3d9..ad2f524 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -35,6 +35,7 @@
 import android.net.vcn.IVcnStatusCallback;
 import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
 import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnManager;
 import android.net.vcn.VcnManager.VcnErrorCode;
 import android.net.vcn.VcnUnderlyingNetworkPolicy;
 import android.net.wifi.WifiInfo;
@@ -724,6 +725,26 @@
         }
     }
 
+    private boolean isCallbackPermissioned(
+            @NonNull VcnStatusCallbackInfo cbInfo, @NonNull ParcelUuid subgroup) {
+        if (!subgroup.equals(cbInfo.mSubGroup)) {
+            return false;
+        }
+
+        if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(subgroup, cbInfo.mPkgName)) {
+            return false;
+        }
+
+        if (!mLocationPermissionChecker.checkLocationPermission(
+                cbInfo.mPkgName,
+                "VcnStatusCallback" /* featureId */,
+                cbInfo.mUid,
+                null /* message */)) {
+            return false;
+        }
+        return true;
+    }
+
     /** Registers the provided callback for receiving VCN status updates. */
     @Override
     public void registerVcnStatusCallback(
@@ -758,6 +779,27 @@
                 }
 
                 mRegisteredStatusCallbacks.put(cbBinder, cbInfo);
+
+                // now that callback is registered, send it the VCN's current status
+                final VcnConfig vcnConfig = mConfigs.get(subGroup);
+                final Vcn vcn = mVcns.get(subGroup);
+                final int vcnStatus;
+                if (vcnConfig == null || !isCallbackPermissioned(cbInfo, subGroup)) {
+                    vcnStatus = VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
+                } else if (vcn == null) {
+                    vcnStatus = VcnManager.VCN_STATUS_CODE_INACTIVE;
+                } else if (vcn.isActive()) {
+                    vcnStatus = VcnManager.VCN_STATUS_CODE_ACTIVE;
+                } else {
+                    // TODO(b/181789060): create Vcn.getStatus() and Log.WTF() for unknown status
+                    vcnStatus = VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+                }
+
+                try {
+                    cbInfo.mCallback.onVcnStatusChanged(vcnStatus);
+                } catch (RemoteException e) {
+                    Slog.d(TAG, "VcnStatusCallback threw on VCN status change", e);
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -806,26 +848,6 @@
             mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
         }
 
-        private boolean isCallbackPermissioned(@NonNull VcnStatusCallbackInfo cbInfo) {
-            if (!mSubGroup.equals(cbInfo.mSubGroup)) {
-                return false;
-            }
-
-            if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(
-                    mSubGroup, cbInfo.mPkgName)) {
-                return false;
-            }
-
-            if (!mLocationPermissionChecker.checkLocationPermission(
-                    cbInfo.mPkgName,
-                    "VcnStatusCallback" /* featureId */,
-                    cbInfo.mUid,
-                    null /* message */)) {
-                return false;
-            }
-            return true;
-        }
-
         @Override
         public void onEnteredSafeMode() {
             synchronized (mLock) {
@@ -838,7 +860,7 @@
 
                 // Notify all registered StatusCallbacks for this subGroup
                 for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
-                    if (isCallbackPermissioned(cbInfo)) {
+                    if (isCallbackPermissioned(cbInfo, mSubGroup)) {
                         Binder.withCleanCallingIdentity(
                                 () ->
                                         cbInfo.mCallback.onVcnStatusChanged(
@@ -862,7 +884,7 @@
 
                 // Notify all registered StatusCallbacks for this subGroup
                 for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
-                    if (isCallbackPermissioned(cbInfo)) {
+                    if (isCallbackPermissioned(cbInfo, mSubGroup)) {
                         Binder.withCleanCallingIdentity(
                                 () ->
                                         cbInfo.mCallback.onGatewayConnectionError(
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d998ebb..277cb8c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -86,6 +86,7 @@
 import android.app.ServiceStartArgs;
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.compat.CompatChanges;
+import android.app.usage.UsageEvents;
 import android.appwidget.AppWidgetManagerInternal;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
@@ -2464,6 +2465,10 @@
                 s.setAllowedBgFgsStartsByBinding(true);
             }
 
+            if ((flags & Context.BIND_NOT_APP_COMPONENT_USAGE) != 0) {
+                s.isNotAppComponentUsage = true;
+            }
+
             if (s.app != null) {
                 updateServiceClientActivitiesLocked(s.app.mServices, c, true);
             }
@@ -3332,6 +3337,14 @@
             return msg;
         }
 
+        // Report usage if binding is from a different package except for explicitly exempted
+        // bindings
+        if (!r.appInfo.packageName.equals(r.mRecentCallingPackage)
+                && !r.isNotAppComponentUsage) {
+            mAm.mUsageStatsService.reportEvent(
+                    r.packageName, r.userId, UsageEvents.Event.APP_COMPONENT_USED);
+        }
+
         // Service is now being launched, its package can't be stopped.
         try {
             AppGlobals.getPackageManager().setPackageStoppedState(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e8a4fa2..9e5d7e4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -50,7 +50,6 @@
 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;
@@ -92,6 +91,7 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
@@ -104,7 +104,6 @@
 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_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;
@@ -1178,14 +1177,16 @@
         final String tag;
         final int type;
         final @ReasonCode int reasonCode;
+        final int callingUid;
 
         PendingTempAllowlist(int targetUid, long duration, @ReasonCode int reasonCode, String tag,
-                int type) {
+                int type, int callingUid) {
             this.targetUid = targetUid;
             this.duration = duration;
             this.tag = tag;
             this.type = type;
             this.reasonCode = reasonCode;
+            this.callingUid = callingUid;
         }
 
         void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -1198,6 +1199,8 @@
             proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TYPE, type);
             proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.REASON_CODE,
                     reasonCode);
+            proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.CALLING_UID,
+                    callingUid);
             proto.end(token);
         }
     }
@@ -2684,6 +2687,11 @@
         }
         if (mUsageStatsService != null) {
             mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode(), taskRoot);
+            if (event == Event.ACTIVITY_RESUMED) {
+                // Report component usage as an activity is an app component
+                mUsageStatsService.reportEvent(
+                        activity.getPackageName(), userId, Event.APP_COMPONENT_USED);
+            }
         }
         ContentCaptureManagerInternal contentCaptureService = mContentCaptureService;
         if (contentCaptureService != null && (event == Event.ACTIVITY_PAUSED
@@ -4963,7 +4971,7 @@
     }
 
     @Override
-    public List<ResolveInfo> queryIntentComponentsForIntentSender(
+    public ParceledListSlice<ResolveInfo> queryIntentComponentsForIntentSender(
             IIntentSender pendingResult, int matchFlags) {
         enforceCallingPermission(Manifest.permission.GET_INTENT_SENDER_INTENT,
                 "queryIntentComponentsForIntentSender()");
@@ -4981,15 +4989,15 @@
         final int userId = res.key.userId;
         switch (res.key.type) {
             case ActivityManager.INTENT_SENDER_ACTIVITY:
-                return mContext.getPackageManager().queryIntentActivitiesAsUser(
-                        intent, matchFlags, userId);
+                return new ParceledListSlice<>(mContext.getPackageManager()
+                        .queryIntentActivitiesAsUser(intent, matchFlags, userId));
             case ActivityManager.INTENT_SENDER_SERVICE:
             case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
-                return mContext.getPackageManager().queryIntentServicesAsUser(
-                        intent, matchFlags, userId);
+                return new ParceledListSlice<>(mContext.getPackageManager()
+                        .queryIntentServicesAsUser(intent, matchFlags, userId));
             case ActivityManager.INTENT_SENDER_BROADCAST:
-                return mContext.getPackageManager().queryBroadcastReceiversAsUser(
-                        intent, matchFlags, userId);
+                return new ParceledListSlice<>(mContext.getPackageManager()
+                        .queryBroadcastReceiversAsUser(intent, matchFlags, userId));
             default: // ActivityManager.INTENT_SENDER_ACTIVITY_RESULT
                 throw new IllegalStateException("Unsupported intent sender type: " + res.key.type);
         }
@@ -6099,6 +6107,10 @@
             updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
         }
 
+        // Report usage as process is persistent and being started.
+        mUsageStatsService.reportEvent(info.packageName, UserHandle.getUserId(app.uid),
+                Event.APP_COMPONENT_USED);
+
         // This package really, really can not be stopped.
         try {
             AppGlobals.getPackageManager().setPackageStoppedState(
@@ -9222,6 +9234,8 @@
                     pw.print(ptw.type);
                     pw.print(" ");
                     pw.print(ptw.reasonCode);
+                    pw.print(" ");
+                    pw.print(ptw.callingUid);
                 }
             }
         }
@@ -10776,9 +10790,12 @@
                         pw.print(" unmapped + ");
                         pw.print(stringifyKBSize(ionPool));
                         pw.println(" pools)");
+                kernelUsed += ionUnmapped;
                 // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
-                // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
-                kernelUsed += ionHeap;
+                // set on ION VMAs, however it might be included by the memtrack HAL.
+                // Replace memtrack HAL reported Graphics category with mapped dmabufs
+                ss[INDEX_TOTAL_PSS] -= ss[INDEX_TOTAL_MEMTRACK_GRAPHICS];
+                ss[INDEX_TOTAL_PSS] += dmabufMapped;
             } else {
                 final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
                 if (totalExportedDmabuf >= 0) {
@@ -14501,7 +14518,8 @@
             String reason, int type, int callingUid) {
         synchronized (mProcLock) {
             mPendingTempAllowlist.put(targetUid,
-                    new PendingTempAllowlist(targetUid, duration, reasonCode, reason, type));
+                    new PendingTempAllowlist(targetUid, duration, reasonCode, reason, type,
+                            callingUid));
             setUidTempAllowlistStateLSP(targetUid, true);
             mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget();
 
@@ -14532,7 +14550,8 @@
             for (int i = 0; i < N; i++) {
                 PendingTempAllowlist ptw = list[i];
                 mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
-                        ptw.duration, ptw.type, true, ptw.reasonCode, ptw.tag);
+                        ptw.duration, ptw.type, true, ptw.reasonCode, ptw.tag,
+                        ptw.callingUid);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index f8494d8..31ea14a 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1517,17 +1517,21 @@
         long kernelUsed = memInfo.getKernelUsedSizeKb();
         final long ionHeap = Debug.getIonHeapsSizeKb();
         final long ionPool = Debug.getIonPoolsSizeKb();
+        final long dmabufMapped = Debug.getDmabufMappedSizeKb();
         if (ionHeap >= 0 && ionPool >= 0) {
+            final long ionUnmapped = ionHeap - dmabufMapped;
             memInfoBuilder.append("       ION: ");
             memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
             memInfoBuilder.append("\n");
+            kernelUsed += ionUnmapped;
             // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
-            // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
-            kernelUsed += ionHeap;
+            // set on ION VMAs, however it might be included by the memtrack HAL.
+            // Replace memtrack HAL reported Graphics category with mapped dmabufs
+            totalPss -= totalMemtrackGraphics;
+            totalPss += dmabufMapped;
         } else {
             final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
             if (totalExportedDmabuf >= 0) {
-                final long dmabufMapped = Debug.getDmabufMappedSizeKb();
                 final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped;
                 memInfoBuilder.append("DMA-BUF: ");
                 memInfoBuilder.append(stringifyKBSize(totalExportedDmabuf));
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 167c2b1..360e1b4 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -16,6 +16,9 @@
 
 package com.android.server.am;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+
+import android.annotation.NonNull;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -25,7 +28,9 @@
 import android.hardware.power.stats.State;
 import android.hardware.power.stats.StateResidency;
 import android.hardware.power.stats.StateResidencyResult;
+import android.net.ConnectivityManager;
 import android.net.INetworkManagementEventObserver;
+import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
@@ -77,6 +82,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.ParseUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
 import com.android.server.net.BaseNetworkObserver;
@@ -291,6 +297,23 @@
         return builder.toString();
     }
 
+    private ConnectivityManager.NetworkCallback mNetworkCallback =
+            new ConnectivityManager.NetworkCallback() {
+        @Override
+        public void onCapabilitiesChanged(@NonNull Network network,
+                @NonNull NetworkCapabilities networkCapabilities) {
+            final String state = networkCapabilities.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+                    ? "CONNECTED" : "SUSPENDED";
+            noteConnectivityChanged(NetworkCapabilitiesUtils.getDisplayTransport(
+                    networkCapabilities.getTransportTypes()), state);
+        }
+
+        @Override
+        public void onLost(Network network) {
+            noteConnectivityChanged(-1, "DISCONNECTED");
+        }
+    };
+
     BatteryStatsService(Context context, File systemDir, Handler handler) {
         // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
         mContext = context;
@@ -330,8 +353,10 @@
         mWorker.systemServicesReady();
         final INetworkManagementService nms = INetworkManagementService.Stub.asInterface(
                 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+        final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
         try {
             nms.registerObserver(mActivityChangeObserver);
+            cm.registerDefaultNetworkCallback(mNetworkCallback);
         } catch (RemoteException e) {
             Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
         }
@@ -346,6 +371,9 @@
         }
 
         Watchdog.getInstance().addMonitor(this);
+
+        final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext, mHandler);
+        dataConnectionStats.startMonitoring();
     }
 
     private final class LocalService extends BatteryStatsInternal {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 2906193..81c4c86 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -29,6 +29,7 @@
 import android.app.BroadcastOptions;
 import android.app.IApplicationThread;
 import android.app.PendingIntent;
+import android.app.usage.UsageEvents.Event;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.IIntentReceiver;
@@ -52,6 +53,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.permission.IPermissionManager;
+import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseIntArray;
@@ -928,6 +930,8 @@
         } else if (r.intent.getData() != null) {
             b.append(r.intent.getData());
         }
+        b.append(",reason:");
+        b.append(reason);
         if (DEBUG_BROADCAST) {
             Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration
                     + " type=" + type + " : " + b.toString());
@@ -1634,6 +1638,13 @@
                     brOptions.getTemporaryAppAllowlistReason());
         }
 
+        // Report that a component is used for explicit broadcasts.
+        if (!r.intent.isExcludingStopped() && r.curComponent != null
+                && !TextUtils.equals(r.curComponent.getPackageName(), r.callerPackage)) {
+            mService.mUsageStatsService.reportEvent(
+                    r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED);
+        }
+
         // Broadcast is being executed, its package can't be stopped.
         try {
             AppGlobals.getPackageManager().setPackageStoppedState(
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index f43c7f6..2c8794d 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -32,6 +32,7 @@
 import android.app.ApplicationExitInfo;
 import android.app.ContentProviderHolder;
 import android.app.IApplicationThread;
+import android.app.usage.UsageEvents.Event;
 import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -57,6 +58,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
@@ -412,6 +414,12 @@
                     final long origId = Binder.clearCallingIdentity();
 
                     try {
+                        if (!TextUtils.equals(cpr.appInfo.packageName, callingPackage)) {
+                            // Report component used since a content provider is being bound.
+                            mService.mUsageStatsService.reportEvent(
+                                    cpr.appInfo.packageName, userId, Event.APP_COMPONENT_USED);
+                        }
+
                         // Content provider is now in use, its package can't be stopped.
                         try {
                             checkTime(startTime,
diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/am/DataConnectionStats.java
similarity index 90%
rename from services/core/java/com/android/server/connectivity/DataConnectionStats.java
rename to services/core/java/com/android/server/am/DataConnectionStats.java
index 15f43a0..6e39a4c 100644
--- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java
+++ b/services/core/java/com/android/server/am/DataConnectionStats.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity;
+package com.android.server.am;
 
 import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
 import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
@@ -34,11 +34,11 @@
 import android.util.Log;
 
 import com.android.internal.app.IBatteryStats;
-import com.android.server.am.BatteryStatsService;
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
 
+/** Class for receiving data connection state to report to {@link BatteryStatsService}. */
 public class DataConnectionStats extends BroadcastReceiver {
     private static final String TAG = "DataConnectionStats";
     private static final boolean DEBUG = false;
@@ -62,14 +62,14 @@
                 new PhoneStateListenerImpl(new PhoneStateListenerExecutor(listenerHandler));
     }
 
+    /** Start data connection state monitoring. */
     public void startMonitoring() {
-        TelephonyManager phone =
-                (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        TelephonyManager phone = mContext.getSystemService(TelephonyManager.class);
         phone.listen(mPhoneStateListener,
                 PhoneStateListener.LISTEN_SERVICE_STATE
-              | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
-              | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
-              | PhoneStateListener.LISTEN_DATA_ACTIVITY);
+                | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
+                | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+                | PhoneStateListener.LISTEN_DATA_ACTIVITY);
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
@@ -103,8 +103,10 @@
         if (mNrState == NetworkRegistrationInfo.NR_STATE_CONNECTED) {
             networkType = TelephonyManager.NETWORK_TYPE_NR;
         }
-        if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible",
-                networkType, visible ? "" : "not "));
+        if (DEBUG) {
+            Log.d(TAG, String.format("Noting data connection for network type %s: %svisible",
+                    networkType, visible ? "" : "not "));
+        }
         try {
             mBatteryStats.notePhoneDataConnectionState(networkType, visible,
                     mServiceState.getState());
@@ -113,7 +115,7 @@
         }
     }
 
-    private final void updateSimState(Intent intent) {
+    private void updateSimState(Intent intent) {
         String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
         if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) {
             mSimState = TelephonyManager.SIM_STATE_ABSENT;
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 3258f8a..d03a47a 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -329,6 +329,22 @@
             info.append("Package is ").append((int) (loadingProgress * 100)).append("% loaded.\n");
         }
 
+        // Retrieve controller with max ANR delay from AnrControllers
+        // Note that we retrieve the controller before dumping stacks because dumping stacks can
+        // take a few seconds, after which the cause of the ANR delay might have completed and
+        // there might no longer be a valid ANR controller to cancel the dialog in that case
+        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);
+        }
+
         StringBuilder report = new StringBuilder();
         report.append(MemoryPressureUtil.currentPsiState());
         ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
@@ -417,20 +433,6 @@
             return;
         }
 
-        // Retrieve max ANR delay from AnrControllers without the mService lock since the
-        // controllers might in turn call into apps
-        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) {
             // mBatteryStatsService can be null if the AMS is constructed with injector only. This
             // will only happen in tests.
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 3ab95d1..9cd9902 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -107,6 +107,7 @@
     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?
+    boolean isNotAppComponentUsage; // is service binding not considered component/package usage?
     boolean isForeground;   // is service currently in foreground mode?
     int foregroundId;       // Notification ID of last foreground req.
     Notification foregroundNoti; // Notification record of foreground state.
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationManagerInternal.java b/services/core/java/com/android/server/apphibernation/AppHibernationManagerInternal.java
new file mode 100644
index 0000000..b0335fe
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationManagerInternal.java
@@ -0,0 +1,46 @@
+/*
+ * 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.apphibernation;
+
+/**
+ * App hibernation manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class AppHibernationManagerInternal {
+
+    /**
+     * @see AppHibernationService#isHibernatingForUser
+     */
+    public abstract boolean isHibernatingForUser(String packageName, int userId);
+
+    /**
+     * @see AppHibernationService#setHibernatingForUser
+     */
+    public abstract void setHibernatingForUser(String packageName, int userId,
+            boolean isHibernating);
+
+    /**
+     * @see AppHibernationService#isHibernatingGlobally
+     */
+    public abstract boolean isHibernatingGlobally(String packageName);
+
+    /**
+     * @see AppHibernationService#setHibernatingGlobally
+     */
+    public abstract void setHibernatingGlobally(String packageName, boolean isHibernating);
+}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 32ae878..968cf5f 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -59,6 +59,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 import java.io.File;
@@ -134,6 +135,8 @@
         intentFilter.addAction(ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
         userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+        LocalServices.addService(AppHibernationManagerInternal.class, mLocalService);
     }
 
     @Override
@@ -545,6 +548,36 @@
         }
     }
 
+    private final AppHibernationManagerInternal mLocalService = new LocalService(this);
+
+    private static final class LocalService extends AppHibernationManagerInternal {
+        private final AppHibernationService mService;
+
+        LocalService(AppHibernationService service) {
+            mService = service;
+        }
+
+        @Override
+        public boolean isHibernatingForUser(String packageName, int userId) {
+            return mService.isHibernatingForUser(packageName, userId);
+        }
+
+        @Override
+        public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
+            mService.setHibernatingForUser(packageName, userId, isHibernating);
+        }
+
+        @Override
+        public void setHibernatingGlobally(String packageName, boolean isHibernating) {
+            mService.setHibernatingGlobally(packageName, isHibernating);
+        }
+
+        @Override
+        public boolean isHibernatingGlobally(String packageName) {
+            return mService.isHibernatingGlobally(packageName);
+        }
+    }
+
     private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this);
 
     static final class AppHibernationServiceStub extends IAppHibernationService.Stub {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 11125dd..4a12ff6 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -848,10 +848,7 @@
                     proxyAttributionTag, uidState, flags);
 
             mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
-                    tag, uidState, flags);
-
-            mHistoricalRegistry.mDiscreteRegistry.recordDiscreteAccess(parent.uid,
-                    parent.packageName, parent.op, tag, flags, uidState, accessTime, -1);
+                    tag, uidState, flags, accessTime);
         }
 
         /**
@@ -955,9 +952,10 @@
                 mInProgressEvents = new ArrayMap<>(1);
             }
 
+            long startTime = System.currentTimeMillis();
             InProgressStartOpEvent event = mInProgressEvents.get(clientId);
             if (event == null) {
-                event = mInProgressStartOpEventPool.acquire(System.currentTimeMillis(),
+                event = mInProgressStartOpEventPool.acquire(startTime,
                         SystemClock.elapsedRealtime(), clientId,
                         PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId),
                         proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags);
@@ -971,7 +969,7 @@
             event.numUnfinishedStarts++;
 
             mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
-                    tag, uidState, flags);
+                    tag, uidState, flags, startTime);
         }
 
         /**
@@ -1017,11 +1015,7 @@
 
                 mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
                         parent.packageName, tag, event.getUidState(),
-                        event.getFlags(), finishedEvent.getDuration());
-
-                mHistoricalRegistry.mDiscreteRegistry.recordDiscreteAccess(parent.uid,
-                        parent.packageName, parent.op, tag, event.getFlags(), event.getUidState(),
-                        event.getStartTime(), accessDurationMillis);
+                        event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration());
 
                 mInProgressStartOpEventPool.release(event);
 
@@ -4769,7 +4763,7 @@
                 mFile.failWrite(stream);
             }
         }
-        mHistoricalRegistry.mDiscreteRegistry.writeAndClearAccessHistory();
+        mHistoricalRegistry.writeAndClearDiscreteHistory();
     }
 
     static class Shell extends ShellCommand {
@@ -6125,8 +6119,7 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
                 "clearHistory");
         // Must not hold the appops lock
-        mHistoricalRegistry.clearHistory();
-        mHistoricalRegistry.mDiscreteRegistry.clearHistory();
+        mHistoricalRegistry.clearAllHistory();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 7699045..a99d908 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -495,6 +495,12 @@
             int nAttributedOps = attributedOps.size();
             for (int i = nAttributedOps - 1; i >= 0; i--) {
                 DiscreteOpEvent previousOp = attributedOps.get(i);
+                if (i == nAttributedOps - 1 && previousOp.mNoteTime == accessTime
+                        && accessDuration > -1) {
+                    // existing event with updated duration
+                    attributedOps.remove(i);
+                    break;
+                }
                 if (previousOp.mNoteTime < accessTime) {
                     break;
                 }
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 1c43fed..0fcf5ee 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -138,7 +138,7 @@
     private static final String PARAMETER_ASSIGNMENT = "=";
     private static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
 
-    volatile @NonNull DiscreteRegistry mDiscreteRegistry;
+    private volatile @NonNull DiscreteRegistry mDiscreteRegistry;
 
     @GuardedBy("mLock")
     private @NonNull LinkedList<HistoricalOps> mPendingWrites = new LinkedList<>();
@@ -477,7 +477,8 @@
     }
 
     void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
-            @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) {
+            @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
+            long accessTime) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -487,6 +488,9 @@
                 getUpdatedPendingHistoricalOpsMLocked(
                         System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
                         attributionTag, uidState, flags, 1);
+
+                mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
+                        flags, uidState, accessTime, -1);
             }
         }
     }
@@ -508,7 +512,7 @@
 
     void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
             @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
-            long increment) {
+            long eventStartTime, long increment) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -518,6 +522,8 @@
                 getUpdatedPendingHistoricalOpsMLocked(
                         System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
                         attributionTag, uidState, flags, increment);
+                mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
+                        flags, uidState, increment, eventStartTime);
             }
         }
     }
@@ -562,7 +568,7 @@
                     return;
                 }
                 final List<HistoricalOps> history = mPersistence.readHistoryDLocked();
-                clearHistory();
+                clearHistoricalRegistry();
                 if (history != null) {
                     final int historySize = history.size();
                     for (int i = 0; i < historySize; i++) {
@@ -631,7 +637,16 @@
         }
     }
 
-    void clearHistory() {
+    void writeAndClearDiscreteHistory() {
+        mDiscreteRegistry.writeAndClearAccessHistory();
+    }
+
+    void clearAllHistory() {
+        clearHistoricalRegistry();
+        mDiscreteRegistry.clearHistory();
+    }
+
+    void clearHistoricalRegistry() {
         synchronized (mOnDiskLock) {
             synchronized (mInMemoryLock) {
                 if (!isPersistenceInitializedMLocked()) {
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/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
new file mode 100644
index 0000000..9f0a2ba
--- /dev/null
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -0,0 +1,245 @@
+/*
+ * 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.audio;
+
+import android.annotation.NonNull;
+import android.media.AudioAttributes;
+import android.media.AudioPlaybackConfiguration;
+import android.media.VolumeShaper;
+import android.util.Log;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Class to handle fading out players
+ */
+public final class FadeOutManager {
+
+    public static final String TAG = "AudioService.FadeOutManager";
+
+    /*package*/ static final long FADE_OUT_DURATION_MS = 2500;
+
+    private static final boolean DEBUG = PlaybackActivityMonitor.DEBUG;
+
+    private static final VolumeShaper.Configuration FADEOUT_VSHAPE =
+            new VolumeShaper.Configuration.Builder()
+                    .setId(PlaybackActivityMonitor.VOLUME_SHAPER_SYSTEM_FADEOUT_ID)
+                    .setCurve(new float[]{0.f, 1.0f} /* times */,
+                            new float[]{1.f, 0.0f} /* volumes */)
+                    .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
+                    .setDuration(FADE_OUT_DURATION_MS)
+                    .build();
+    private static final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED =
+            new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY)
+                    .createIfNeeded()
+                    .build();
+
+    private static final int[] UNFADEABLE_PLAYER_TYPES = {
+            AudioPlaybackConfiguration.PLAYER_TYPE_AAUDIO,
+            AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL,
+    };
+
+    private static final int[] UNFADEABLE_CONTENT_TYPES = {
+            AudioAttributes.CONTENT_TYPE_SPEECH,
+    };
+
+    private static final int[] FADEABLE_USAGES = {
+            AudioAttributes.USAGE_GAME,
+            AudioAttributes.USAGE_MEDIA,
+    };
+
+    // like a PLAY_CREATE_IF_NEEDED operation but with a skip to the end of the ramp
+    private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
+            new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
+
+    /**
+     * Evaluates whether the player associated with this configuration can and should be faded out
+     * @param apc the configuration of the player
+     * @return true if player type and AudioAttributes are compatible with fade out
+     */
+    static boolean canBeFadedOut(@NonNull AudioPlaybackConfiguration apc) {
+        if (ArrayUtils.contains(UNFADEABLE_PLAYER_TYPES, apc.getPlayerType())) {
+            if (DEBUG) { Log.i(TAG, "not fading: player type:" + apc.getPlayerType()); }
+            return false;
+        }
+        if (ArrayUtils.contains(UNFADEABLE_CONTENT_TYPES,
+                apc.getAudioAttributes().getContentType())) {
+            if (DEBUG) {
+                Log.i(TAG, "not fading: content type:"
+                        + apc.getAudioAttributes().getContentType());
+            }
+            return false;
+        }
+        if (!ArrayUtils.contains(FADEABLE_USAGES, apc.getAudioAttributes().getUsage())) {
+            if (DEBUG) {
+                Log.i(TAG, "not fading: usage:" + apc.getAudioAttributes().getUsage());
+            }
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Map of uid (key) to faded out apps (value)
+     */
+    private final HashMap<Integer, FadedOutApp> mFadedApps = new HashMap<Integer, FadedOutApp>();
+
+    synchronized void fadeOutUid(int uid, ArrayList<AudioPlaybackConfiguration> players) {
+        Log.i(TAG, "fadeOutUid() uid:" + uid);
+        if (!mFadedApps.containsKey(uid)) {
+            mFadedApps.put(uid, new FadedOutApp(uid));
+        }
+        final FadedOutApp fa = mFadedApps.get(uid);
+        for (AudioPlaybackConfiguration apc : players) {
+            fa.addFade(apc, false /*skipRamp*/);
+        }
+    }
+
+    synchronized void unfadeOutUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
+        Log.i(TAG, "unfadeOutUid() uid:" + uid);
+        final FadedOutApp fa = mFadedApps.remove(uid);
+        if (fa == null) {
+            return;
+        }
+        fa.removeUnfadeAll(players);
+    }
+
+    synchronized void forgetUid(int uid) {
+        //Log.v(TAG, "forget() uid:" + uid);
+        //mFadedApps.remove(uid);
+        // TODO unfade all players later in case they are reused or the app continued to play
+    }
+
+    // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
+    //   see {@link PlaybackActivityMonitor#playerEvent}
+    synchronized void checkFade(@NonNull AudioPlaybackConfiguration apc) {
+        if (DEBUG) {
+            Log.v(TAG, "checkFade() player piid:"
+                    + apc.getPlayerInterfaceId() + " uid:" + apc.getClientUid());
+        }
+        final FadedOutApp fa = mFadedApps.get(apc.getClientUid());
+        if (fa == null) {
+            return;
+        }
+        fa.addFade(apc, true);
+    }
+
+    /**
+     * Remove the player from the list of faded out players because it has been released
+     * @param apc the released player
+     */
+    synchronized void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
+        final int uid = apc.getClientUid();
+        if (DEBUG) {
+            Log.v(TAG, "removedReleased() player piid: "
+                    + apc.getPlayerInterfaceId() + " uid:" + uid);
+        }
+        final FadedOutApp fa = mFadedApps.get(uid);
+        if (fa == null) {
+            return;
+        }
+        fa.removeReleased(apc);
+    }
+
+    synchronized void dump(PrintWriter pw) {
+        for (FadedOutApp da : mFadedApps.values()) {
+            da.dump(pw);
+        }
+    }
+
+    //=========================================================================
+    /**
+     * Class to group players from a common app, that are faded out.
+     */
+    private static final class FadedOutApp {
+        private final int mUid;
+        private final ArrayList<Integer> mFadedPlayers = new ArrayList<Integer>();
+
+        FadedOutApp(int uid) {
+            mUid = uid;
+        }
+
+        void dump(PrintWriter pw) {
+            pw.print("\t uid:" + mUid + " piids:");
+            for (int piid : mFadedPlayers) {
+                pw.print(" " + piid);
+            }
+            pw.println("");
+        }
+
+        /**
+         * Add this player to the list of faded out players and apply the fade
+         * @param apc a config that satisfies
+         *      apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
+         * @param skipRamp true if the player should be directly into the end of ramp state.
+         *      This value would for instance be false when adding players at the start of a fade.
+         */
+        void addFade(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+            final int piid = new Integer(apc.getPlayerInterfaceId());
+            if (mFadedPlayers.contains(piid)) {
+                if (DEBUG) {
+                    Log.v(TAG, "player piid:" + piid + " already faded out");
+                }
+                return;
+            }
+            try {
+                PlaybackActivityMonitor.sEventLogger.log(
+                        (new PlaybackActivityMonitor.FadeOutEvent(apc, skipRamp)).printLog(TAG));
+                apc.getPlayerProxy().applyVolumeShaper(
+                        FADEOUT_VSHAPE,
+                        skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
+                mFadedPlayers.add(piid);
+            } catch (Exception e) {
+                Log.e(TAG, "Error fading out player piid:" + piid
+                        + " uid:" + apc.getClientUid(), e);
+            }
+        }
+
+        void removeUnfadeAll(HashMap<Integer, AudioPlaybackConfiguration> players) {
+            for (int piid : mFadedPlayers) {
+                final AudioPlaybackConfiguration apc = players.get(piid);
+                if (apc != null) {
+                    try {
+                        PlaybackActivityMonitor.sEventLogger.log(
+                                (new AudioEventLogger.StringEvent("unfading out piid:"
+                                        + piid)).printLog(TAG));
+                        apc.getPlayerProxy().applyVolumeShaper(
+                                FADEOUT_VSHAPE,
+                                VolumeShaper.Operation.REVERSE);
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error unfading out player piid:" + piid + " uid:" + mUid, e);
+                    }
+                } else {
+                    // this piid was in the list of faded players, but wasn't found
+                    if (DEBUG) {
+                        Log.v(TAG, "Error unfading out player piid:" + piid
+                                + ", player not found for uid " + mUid);
+                    }
+                }
+            }
+            mFadedPlayers.clear();
+        }
+
+        void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
+            mFadedPlayers.remove(new Integer(apc.getPlayerInterfaceId()));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index b6d6472..cc60fe1 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -70,6 +70,11 @@
      */
     private boolean mFocusLossWasNotified;
     /**
+     * whether this focus owner has already lost focus, but is being faded out until focus loss
+     * dispatch occurs. It's in "limbo" mode: has lost focus but not released yet until notified
+     */
+    boolean mFocusLossFadeLimbo;
+    /**
      * the audio attributes associated with the focus request
      */
     private final @NonNull AudioAttributes mAttributes;
@@ -102,6 +107,7 @@
         mGrantFlags = grantFlags;
         mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
         mFocusLossWasNotified = true;
+        mFocusLossFadeLimbo = false;
         mFocusController = ctlr;
         mSdkTarget = sdk;
     }
@@ -115,6 +121,7 @@
         mFocusGainRequest = afi.getGainRequest();
         mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
         mFocusLossWasNotified = true;
+        mFocusLossFadeLimbo = false;
         mGrantFlags = afi.getFlags();
         mSdkTarget = afi.getSdkTarget();
 
@@ -132,6 +139,13 @@
         return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);
     }
 
+    /**
+     * @return true if the focus requester is scheduled to receive a focus loss
+     */
+    boolean isInFocusLossLimbo() {
+        return mFocusLossFadeLimbo;
+    }
+
     boolean hasSameBinder(IBinder ib) {
         return (mSourceRef != null) && mSourceRef.equals(ib);
     }
@@ -231,11 +245,21 @@
                 + " -- flags: " + flagsToString(mGrantFlags)
                 + " -- loss: " + focusLossToString()
                 + " -- notified: " + mFocusLossWasNotified
+                + " -- limbo" + mFocusLossFadeLimbo
                 + " -- uid: " + mCallingUid
                 + " -- attr: " + mAttributes
                 + " -- sdk:" + mSdkTarget);
     }
 
+    /**
+     * Clear all references, except for instances in "loss limbo" due to the current fade out
+     * for which there will be an attempt to be clear after the loss has been notified
+     */
+    void maybeRelease() {
+        if (!mFocusLossFadeLimbo) {
+            release();
+        }
+    }
 
     void release() {
         final IBinder srcRef = mSourceRef;
@@ -315,6 +339,7 @@
     void handleFocusGain(int focusGain) {
         try {
             mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
+            mFocusLossFadeLimbo = false;
             mFocusController.notifyExtPolicyFocusGrant_syncAf(toAudioFocusInfo(),
                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
             final IAudioFocusDispatcher fd = mFocusDispatcher;
@@ -327,7 +352,7 @@
                     fd.dispatchAudioFocusChange(focusGain, mClientId);
                 }
             }
-            mFocusController.unduckPlayers(this);
+            mFocusController.restoreVShapedPlayers(this);
         } catch (android.os.RemoteException e) {
             Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
         }
@@ -336,7 +361,7 @@
     @GuardedBy("MediaFocusControl.mAudioFocusLock")
     void handleFocusGainFromRequest(int focusRequestResult) {
         if (focusRequestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
-            mFocusController.unduckPlayers(this);
+            mFocusController.restoreVShapedPlayers(this);
         }
     }
 
@@ -375,7 +400,7 @@
                 if (handled) {
                     if (DEBUG) {
                         Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
-                            + " to " + mClientId + ", ducking implemented by framework");
+                                + " to " + mClientId + ", response handled by framework");
                     }
                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
                             toAudioFocusInfo(), false /* wasDispatched */);
@@ -435,8 +460,27 @@
                 return false;
             }
 
-            return mFocusController.duckPlayers(frWinner, this, forceDuck);
+            return mFocusController.duckPlayers(frWinner, /*loser*/ this, forceDuck);
         }
+
+        if (focusLoss == AudioManager.AUDIOFOCUS_LOSS) {
+            if (!MediaFocusControl.ENFORCE_FADEOUT_FOR_FOCUS_LOSS) {
+                return false;
+            }
+
+            // candidate for fade-out before a receiving a loss
+            boolean playersAreFaded =  mFocusController.fadeOutPlayers(frWinner, /* loser */ this);
+            if (playersAreFaded) {
+                // active players are being faded out, delay the dispatch of focus loss
+                // mark this instance as being faded so it's not released yet as the focus loss
+                // will be dispatched later, it is now in limbo mode
+                mFocusLossFadeLimbo = true;
+                mFocusController.postDelayedLossAfterFade(this,
+                        FadeOutManager.FADE_OUT_DURATION_MS);
+                return true;
+            }
+        }
+
         return false;
     }
 
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index b1633b0..1dcfdae 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -30,7 +30,10 @@
 import android.media.audiopolicy.IAudioPolicyCallback;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.Log;
@@ -80,6 +83,12 @@
      */
     static final boolean ENFORCE_MUTING_FOR_RING_OR_CALL = true;
 
+    /**
+     * set to true so the framework enforces fading out apps that lose audio focus in a
+     * non-transient way.
+     */
+    static final boolean ENFORCE_FADEOUT_FOR_FOCUS_LOSS = true;
+
     private final Context mContext;
     private final AppOpsManager mAppOps;
     private PlayerFocusEnforcer mFocusEnforcer; // never null
@@ -98,6 +107,7 @@
         final ContentResolver cr = mContext.getContentResolver();
         mMultiAudioFocusEnabled = Settings.System.getIntForUser(cr,
                 Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0, cr.getUserId()) != 0;
+        initFocusThreading();
     }
 
     protected void dump(PrintWriter pw) {
@@ -119,8 +129,8 @@
     }
 
     @Override
-    public void unduckPlayers(@NonNull FocusRequester winner) {
-        mFocusEnforcer.unduckPlayers(winner);
+    public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
+        mFocusEnforcer.restoreVShapedPlayers(winner);
     }
 
     @Override
@@ -133,6 +143,16 @@
         mFocusEnforcer.unmutePlayersForCall();
     }
 
+    @Override
+    public boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser) {
+        return mFocusEnforcer.fadeOutPlayers(winner, loser);
+    }
+
+    @Override
+    public void forgetUid(int uid) {
+        mFocusEnforcer.forgetUid(uid);
+    }
+
     //==========================================================================================
     // AudioFocus
     //==========================================================================================
@@ -294,7 +314,7 @@
         {
             //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
             FocusRequester fr = mFocusStack.pop();
-            fr.release();
+            fr.maybeRelease();
             if (notifyFocusFollowers) {
                 abandonSource = fr.toAudioFocusInfo();
             }
@@ -318,7 +338,7 @@
                         abandonSource = fr.toAudioFocusInfo();
                     }
                     // stack entry not used anymore, clear references
-                    fr.release();
+                    fr.maybeRelease();
                 }
             }
         }
@@ -1134,4 +1154,57 @@
             pw.println("------------------------------");
         }
     }
+
+    //=================================================================
+    // Async focus events
+    void postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs) {
+        if (DEBUG) {
+            Log.v(TAG, "postDelayedLossAfterFade loser=" + focusLoser.getPackageName());
+        }
+        mFocusHandler.sendMessageDelayed(
+                mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser),
+                FadeOutManager.FADE_OUT_DURATION_MS);
+    }
+    //=================================================================
+    // Message handling
+    private Handler mFocusHandler;
+    private HandlerThread mFocusThread;
+
+    /**
+     * dispatch a focus loss after an app has been faded out. Focus loser is to be released
+     * after dispatch as it has already left the stack
+     * args:
+     *     msg.obj: the audio focus loser
+     *         type:FocusRequester
+     */
+    private static final int MSG_L_FOCUS_LOSS_AFTER_FADE = 1;
+
+    private void initFocusThreading() {
+        mFocusThread = new HandlerThread(TAG);
+        mFocusThread.start();
+        mFocusHandler = new Handler(mFocusThread.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_L_FOCUS_LOSS_AFTER_FADE:
+                        if (DEBUG) {
+                            Log.d(TAG, "MSG_L_FOCUS_LOSS_AFTER_FADE loser="
+                                    + ((FocusRequester) msg.obj).getPackageName());
+                        }
+                        synchronized (mAudioFocusLock) {
+                            final FocusRequester loser = (FocusRequester) msg.obj;
+                            if (loser.isInFocusLossLimbo()) {
+                                loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
+                                loser.release();
+                                mFocusEnforcer.forgetUid(loser.getClientUid());
+                            }
+                        }
+                        break;
+                    default:
+                        break;
+                }
+            }
+        };
+
+    }
 }
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 8af1b5be..47c91e6 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -51,8 +51,9 @@
 
     public static final String TAG = "AudioService.PlaybackActivityMonitor";
 
-    private static final boolean DEBUG = false;
-    private static final int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;
+    /*package*/ static final boolean DEBUG = false;
+    /*package*/ static final int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;
+    /*package*/ static final int VOLUME_SHAPER_SYSTEM_FADEOUT_ID = 2;
 
     private static final VolumeShaper.Configuration DUCK_VSHAPE =
             new VolumeShaper.Configuration.Builder()
@@ -298,6 +299,7 @@
             }
             if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
                 mDuckingManager.checkDuck(apc);
+                mFadingManager.checkFade(apc);
             }
         }
         if (change) {
@@ -320,6 +322,7 @@
                         "releasing player piid:" + piid));
                 mPlayers.remove(new Integer(piid));
                 mDuckingManager.removeReleased(apc);
+                mFadingManager.removeReleased(apc);
                 checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
                 change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED,
                         AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID);
@@ -442,6 +445,9 @@
             // ducked players
             pw.println("\n  ducked players piids:");
             mDuckingManager.dump(pw);
+            // faded out players
+            pw.println("\n  faded out players piids:");
+            mFadingManager.dump(pw);
             // players muted due to the device ringing or being in a call
             pw.print("\n  muted player piids:");
             for (int piid : mMutedPlayers) {
@@ -606,10 +612,11 @@
     }
 
     @Override
-    public void unduckPlayers(@NonNull FocusRequester winner) {
+    public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
         if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); }
         synchronized (mPlayerLock) {
             mDuckingManager.unduckUid(winner.getClientUid(), mPlayers);
+            mFadingManager.unfadeOutUid(winner.getClientUid(), mPlayers);
         }
     }
 
@@ -678,6 +685,67 @@
         }
     }
 
+    private final FadeOutManager mFadingManager = new FadeOutManager();
+
+    /**
+     *
+     * @param winner the new non-transient focus owner
+     * @param loser the previous focus owner
+     * @return true if there are players being faded out
+     */
+    @Override
+    public boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser) {
+        if (DEBUG) {
+            Log.v(TAG, "fadeOutPlayers: winner=" + winner.getPackageName()
+                    +  " loser=" + loser.getPackageName());
+        }
+        boolean loserHasActivePlayers = false;
+
+        // find which players to fade out
+        synchronized (mPlayerLock) {
+            if (mPlayers.isEmpty()) {
+                return false;
+            }
+            // check if this UID needs to be faded out (return false if not), and gather list of
+            // eligible players to fade out
+            final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator();
+            final ArrayList<AudioPlaybackConfiguration> apcsToFadeOut =
+                    new ArrayList<AudioPlaybackConfiguration>();
+            while (apcIterator.hasNext()) {
+                final AudioPlaybackConfiguration apc = apcIterator.next();
+                if (!winner.hasSameUid(apc.getClientUid())
+                        && loser.hasSameUid(apc.getClientUid())
+                        && apc.getPlayerState()
+                        == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+                    if (!FadeOutManager.canBeFadedOut(apc)) {
+                        // the player is not eligible to be faded out, bail
+                        Log.v(TAG, "not fading out player " + apc.getPlayerInterfaceId()
+                                + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
+                                + " type:"
+                                + AudioPlaybackConfiguration.toLogFriendlyPlayerType(
+                                        apc.getPlayerType())
+                                + " attr:" + apc.getAudioAttributes());
+                        return false;
+                    }
+                    loserHasActivePlayers = true;
+                    apcsToFadeOut.add(apc);
+                }
+            }
+            //###
+            //mDuckingManager.duckUid(loser.getClientUid(), apcsToFadeOut);
+            if (loserHasActivePlayers) {
+                mFadingManager.fadeOutUid(loser.getClientUid(), apcsToFadeOut);
+            }
+        }
+
+        return loserHasActivePlayers;
+    }
+
+    @Override
+    public void forgetUid(int uid) {
+        mFadingManager.forgetUid(uid);
+    }
+
     //=================================================================
     // Track playback activity listeners
 
@@ -964,13 +1032,15 @@
         }
     }
 
-    private static final class DuckEvent extends AudioEventLogger.Event {
+    private abstract static class VolumeShaperEvent extends AudioEventLogger.Event {
         private final int mPlayerIId;
         private final boolean mSkipRamp;
         private final int mClientUid;
         private final int mClientPid;
 
-        DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+        abstract String getVSAction();
+
+        VolumeShaperEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
             mPlayerIId = apc.getPlayerInterfaceId();
             mSkipRamp = skipRamp;
             mClientUid = apc.getClientUid();
@@ -979,12 +1049,34 @@
 
         @Override
         public String eventToString() {
-            return new StringBuilder("ducking player piid:").append(mPlayerIId)
+            return new StringBuilder(getVSAction()).append(" player piid:").append(mPlayerIId)
                     .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid)
                     .append(" skip ramp:").append(mSkipRamp).toString();
         }
     }
 
+    static final class DuckEvent extends VolumeShaperEvent {
+        @Override
+        String getVSAction() {
+            return "ducking";
+        }
+
+        DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+            super(apc, skipRamp);
+        }
+    }
+
+    static final class FadeOutEvent extends VolumeShaperEvent {
+        @Override
+        String getVSAction() {
+            return "fading out";
+        }
+
+        FadeOutEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+            super(apc, skipRamp);
+        }
+    }
+
     private static final class AudioAttrEvent extends AudioEventLogger.Event {
         private final int mPlayerIId;
         private final AudioAttributes mPlayerAttr;
@@ -1000,6 +1092,6 @@
         }
     }
 
-    private static final AudioEventLogger sEventLogger = new AudioEventLogger(100,
+    static final AudioEventLogger sEventLogger = new AudioEventLogger(100,
             "playback activity as reported through PlayerBase");
 }
diff --git a/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java b/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
index 89e7b782..fb72ac2 100644
--- a/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
+++ b/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
@@ -31,11 +31,12 @@
                                boolean forceDuck);
 
     /**
-     * Unduck the players that had been ducked with
-     * {@link #duckPlayers(FocusRequester, FocusRequester, boolean)}
+     * Restore the initial state of any players that had had a volume ramp applied as the result
+     * of a duck or fade out through {@link #duckPlayers(FocusRequester, FocusRequester, boolean)}
+     * or {@link #fadeOutPlayers(FocusRequester, FocusRequester)}
      * @param winner
      */
-    void unduckPlayers(@NonNull FocusRequester winner);
+    void restoreVShapedPlayers(@NonNull FocusRequester winner);
 
     /**
      * Mute players at the beginning of a call
@@ -47,4 +48,20 @@
      * Unmute players at the end of a call
      */
     void unmutePlayersForCall();
+
+    /**
+     * Fade out whatever is still playing after the non-transient focus change
+     * @param winner the new non-transient focus owner
+     * @param loser the previous focus owner
+     * @return true if there were any active players for the loser that qualified for being
+     *         faded out (because of audio attributes, or player types), and as such were faded
+     *         out.
+     */
+    boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser);
+
+    /**
+     * Mark this UID as no longer playing a role in focus enforcement
+     * @param uid
+     */
+    void forgetUid(int uid);
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index e19745e..050b28b 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -33,6 +33,7 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.IAuthService;
 import android.hardware.biometrics.IBiometricAuthenticator;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -337,6 +338,168 @@
                 Binder.restoreCallingIdentity(identity);
             }
         }
+
+        @Override
+        public CharSequence getButtonLabel(
+                int userId,
+                String opPackageName,
+                @Authenticators.Types int authenticators) throws RemoteException {
+
+            // Only allow internal clients to call getButtonLabel with a different userId.
+            final int callingUserId = UserHandle.getCallingUserId();
+
+            if (userId != callingUserId) {
+                checkInternalPermission();
+            } else {
+                checkPermission();
+            }
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                @BiometricAuthenticator.Modality final int modality =
+                        mBiometricService.getCurrentModality(
+                                opPackageName, userId, callingUserId, authenticators);
+
+                final String result;
+                switch (getCredentialBackupModality(modality)) {
+                    case BiometricAuthenticator.TYPE_NONE:
+                        result = null;
+                        break;
+                    case BiometricAuthenticator.TYPE_CREDENTIAL:
+                        result = getContext().getString(R.string.screen_lock_app_setting_name);
+                        break;
+                    case BiometricAuthenticator.TYPE_FINGERPRINT:
+                        result = getContext().getString(R.string.fingerprint_app_setting_name);
+                        break;
+                    case BiometricAuthenticator.TYPE_FACE:
+                        result = getContext().getString(R.string.face_app_setting_name);
+                        break;
+                    default:
+                        result = getContext().getString(R.string.biometric_app_setting_name);
+                        break;
+                }
+
+                return result;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public CharSequence getPromptMessage(
+                int userId,
+                String opPackageName,
+                @Authenticators.Types int authenticators) throws RemoteException {
+
+            // Only allow internal clients to call getButtonLabel with a different userId.
+            final int callingUserId = UserHandle.getCallingUserId();
+
+            if (userId != callingUserId) {
+                checkInternalPermission();
+            } else {
+                checkPermission();
+            }
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                @BiometricAuthenticator.Modality final int modality =
+                        mBiometricService.getCurrentModality(
+                                opPackageName, userId, callingUserId, authenticators);
+
+                final String result;
+                switch (getCredentialBackupModality(modality)) {
+                    case BiometricAuthenticator.TYPE_NONE:
+                        result = null;
+                        break;
+                    case BiometricAuthenticator.TYPE_CREDENTIAL:
+                        result = getContext().getString(
+                                R.string.screen_lock_dialog_default_subtitle);
+                        break;
+                    case BiometricAuthenticator.TYPE_FINGERPRINT:
+                        result = getContext().getString(
+                                R.string.fingerprint_dialog_default_subtitle);
+                        break;
+                    case BiometricAuthenticator.TYPE_FACE:
+                        result = getContext().getString(R.string.face_dialog_default_subtitle);
+                        break;
+                    default:
+                        result = getContext().getString(R.string.biometric_dialog_default_subtitle);
+                        break;
+                }
+                return result;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public CharSequence getSettingName(
+                int userId,
+                String opPackageName,
+                @Authenticators.Types int authenticators) throws RemoteException {
+
+            // Only allow internal clients to call getButtonLabel with a different userId.
+            final int callingUserId = UserHandle.getCallingUserId();
+
+            if (userId != callingUserId) {
+                checkInternalPermission();
+            } else {
+                checkPermission();
+            }
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                @BiometricAuthenticator.Modality final int modality =
+                        mBiometricService.getSupportedModalities(authenticators);
+
+                final String result;
+                switch (modality) {
+                    // Handle the case of a single supported modality.
+                    case BiometricAuthenticator.TYPE_NONE:
+                        result = null;
+                        break;
+                    case BiometricAuthenticator.TYPE_CREDENTIAL:
+                        result = getContext().getString(R.string.screen_lock_app_setting_name);
+                        break;
+                    case BiometricAuthenticator.TYPE_IRIS:
+                        result = getContext().getString(R.string.biometric_app_setting_name);
+                        break;
+                    case BiometricAuthenticator.TYPE_FINGERPRINT:
+                        result = getContext().getString(R.string.fingerprint_app_setting_name);
+                        break;
+                    case BiometricAuthenticator.TYPE_FACE:
+                        result = getContext().getString(R.string.face_app_setting_name);
+                        break;
+
+                    // Handle other possible modality combinations.
+                    default:
+                        if ((modality & BiometricAuthenticator.TYPE_CREDENTIAL) == 0) {
+                            // 2+ biometric modalities are supported (but not device credential).
+                            result = getContext().getString(R.string.biometric_app_setting_name);
+                        } else {
+                            @BiometricAuthenticator.Modality final int biometricModality =
+                                    modality & ~BiometricAuthenticator.TYPE_CREDENTIAL;
+                            if (biometricModality == BiometricAuthenticator.TYPE_FINGERPRINT) {
+                                // Only device credential and fingerprint are supported.
+                                result = getContext().getString(
+                                        R.string.fingerprint_or_screen_lock_app_setting_name);
+                            } else if (biometricModality == BiometricAuthenticator.TYPE_FACE) {
+                                // Only device credential and face are supported.
+                                result = getContext().getString(
+                                        R.string.face_or_screen_lock_app_setting_name);
+                            } else {
+                                // Device credential and 1+ other biometric(s) are supported.
+                                result = getContext().getString(
+                                        R.string.biometric_or_screen_lock_app_setting_name);
+                            }
+                        }
+                        break;
+                }
+                return result;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
     }
 
     public AuthService(Context context) {
@@ -442,4 +605,10 @@
         return mInjector.getAppOps(getContext()).noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid,
                 opPackageName, null /* attributionTag */, reason) == AppOpsManager.MODE_ALLOWED;
     }
+
+    @BiometricAuthenticator.Modality
+    private static int getCredentialBackupModality(@BiometricAuthenticator.Modality int modality) {
+        return modality == BiometricAuthenticator.TYPE_CREDENTIAL
+                ? modality : (modality & ~BiometricAuthenticator.TYPE_CREDENTIAL);
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 00a4e43..a888209 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -666,14 +666,9 @@
                 throw new SecurityException("Invalid authenticator configuration");
             }
 
-            final PromptInfo promptInfo = new PromptInfo();
-            promptInfo.setAuthenticators(authenticators);
-
             try {
-                PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager,
-                        mDevicePolicyManager, mSettingObserver, mSensors, userId, promptInfo,
-                        opPackageName,
-                        false /* checkDevicePolicyManager */);
+                final PreAuthInfo preAuthInfo =
+                        createPreAuthInfo(opPackageName, userId, authenticators);
                 return preAuthInfo.getCanAuthenticateResult();
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote exception", e);
@@ -807,6 +802,64 @@
             return Authenticators.EMPTY_SET;
         }
 
+        @Override // Binder call
+        public int getCurrentModality(
+                String opPackageName,
+                int userId,
+                int callingUserId,
+                @Authenticators.Types int authenticators) {
+
+            checkInternalPermission();
+
+            Slog.d(TAG, "getCurrentModality: User=" + userId
+                    + ", Caller=" + callingUserId
+                    + ", Authenticators=" + authenticators);
+
+            if (!Utils.isValidAuthenticatorConfig(authenticators)) {
+                throw new SecurityException("Invalid authenticator configuration");
+            }
+
+            try {
+                final PreAuthInfo preAuthInfo =
+                        createPreAuthInfo(opPackageName, userId, authenticators);
+                return preAuthInfo.getPreAuthenticateStatus().first;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception", e);
+                return BiometricAuthenticator.TYPE_NONE;
+            }
+        }
+
+        @Override // Binder call
+        public int getSupportedModalities(@Authenticators.Types int authenticators) {
+            checkInternalPermission();
+
+            Slog.d(TAG, "getSupportedModalities: Authenticators=" + authenticators);
+
+            if (!Utils.isValidAuthenticatorConfig(authenticators)) {
+                throw new SecurityException("Invalid authenticator configuration");
+            }
+
+            @BiometricAuthenticator.Modality int modality =
+                    Utils.isCredentialRequested(authenticators)
+                            ? BiometricAuthenticator.TYPE_CREDENTIAL
+                            : BiometricAuthenticator.TYPE_NONE;
+
+            if (Utils.isBiometricRequested(authenticators)) {
+                @Authenticators.Types final int requestedStrength =
+                        Utils.getPublicBiometricStrength(authenticators);
+
+                // Add modalities of all biometric sensors that meet the authenticator requirements.
+                for (final BiometricSensor sensor : mSensors) {
+                    @Authenticators.Types final int sensorStrength = sensor.getCurrentStrength();
+                    if (Utils.isAtLeastStrength(sensorStrength, requestedStrength)) {
+                        modality |= sensor.modality;
+                    }
+                }
+            }
+
+            return modality;
+        }
+
         @Override
         protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
@@ -845,6 +898,19 @@
                 "Must have USE_BIOMETRIC_INTERNAL permission");
     }
 
+    @NonNull
+    private PreAuthInfo createPreAuthInfo(
+            @NonNull String opPackageName,
+            int userId,
+            @Authenticators.Types int authenticators) throws RemoteException {
+
+        final PromptInfo promptInfo = new PromptInfo();
+        promptInfo.setAuthenticators(authenticators);
+
+        return PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, mSensors,
+                userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */);
+    }
+
     /**
      * Class for injecting dependencies into BiometricService.
      * TODO(b/141025588): Replace with a dependency injection framework (e.g. Guice, Dagger).
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 5cd0bbf..d9e21a8 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -153,6 +153,16 @@
     /**
      * Checks if any of the publicly defined strengths are set.
      *
+     * @param authenticators composed of one or more values from {@link Authenticators}
+     * @return true if biometric authentication is allowed.
+     */
+    static boolean isBiometricRequested(@Authenticators.Types int authenticators) {
+        return getPublicBiometricStrength(authenticators) != 0;
+    }
+
+    /**
+     * Checks if any of the publicly defined strengths are set.
+     *
      * @param promptInfo should be first processed by
      * {@link #combineAuthenticatorBundles(PromptInfo)}
      * @return true if biometric authentication is allowed.
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index d7e08e4..7a846f5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -32,7 +32,6 @@
  */
 public class FaceGenerateChallengeClient extends GenerateChallengeClient<ISession> {
     private static final String TAG = "FaceGenerateChallengeClient";
-    private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
 
     FaceGenerateChallengeClient(@NonNull Context context,
             @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
@@ -43,7 +42,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().generateChallenge(mSequentialId, CHALLENGE_TIMEOUT_SEC);
+            getFreshDaemon().generateChallenge(mSequentialId);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to generateChallenge", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index afa5bd2..b4c9b29 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -45,7 +45,7 @@
 
         return new ISession.Stub() {
             @Override
-            public void generateChallenge(int cookie, int timeoutSec) throws RemoteException {
+            public void generateChallenge(int cookie) throws RemoteException {
                 Slog.w(TAG, "generateChallenge, cookie: " + cookie);
                 cb.onChallengeGenerated(0L);
             }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index 402886b..3c9cced 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -32,7 +32,6 @@
  */
 class FingerprintGenerateChallengeClient extends GenerateChallengeClient<ISession> {
     private static final String TAG = "FingerprintGenerateChallengeClient";
-    private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
 
     FingerprintGenerateChallengeClient(@NonNull Context context,
             @NonNull LazyDaemon<ISession> lazyDaemon,
@@ -45,7 +44,7 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().generateChallenge(mSequentialId, CHALLENGE_TIMEOUT_SEC);
+            getFreshDaemon().generateChallenge(mSequentialId);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to generateChallenge", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
index 9db2fcf..8547a68 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
@@ -45,7 +45,7 @@
 
         return new ISession.Stub() {
             @Override
-            public void generateChallenge(int cookie, int timeoutSec) throws RemoteException {
+            public void generateChallenge(int cookie) throws RemoteException {
                 Slog.w(TAG, "generateChallenge, cookie: " + cookie);
                 cb.onChallengeGenerated(0L);
             }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index cac6cab..1d0e115 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -35,7 +35,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkMonitorManager;
 import android.net.NetworkRequest;
-import android.net.NetworkState;
+import android.net.NetworkStateSnapshot;
 import android.net.QosCallbackException;
 import android.net.QosFilter;
 import android.net.QosFilterParcelable;
@@ -890,15 +890,18 @@
         mScore = score;
     }
 
-    public NetworkState getNetworkState() {
+    /**
+     * Return a {@link NetworkStateSnapshot} for this network.
+     */
+    @NonNull
+    public NetworkStateSnapshot getNetworkStateSnapshot() {
         synchronized (this) {
             // Network objects are outwardly immutable so there is no point in duplicating.
             // Duplicating also precludes sharing socket factories and connection pools.
             final String subscriberId = (networkAgentConfig != null)
                     ? networkAgentConfig.subscriberId : null;
-            return new NetworkState(new NetworkInfo(networkInfo),
-                    new LinkProperties(linkProperties),
-                    new NetworkCapabilities(networkCapabilities), network, subscriberId);
+            return new NetworkStateSnapshot(network, new NetworkCapabilities(networkCapabilities),
+                    new LinkProperties(linkProperties), subscriberId, networkInfo.getType());
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
index 816bf2b..0f5400d 100644
--- a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
+++ b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
@@ -27,7 +27,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.telephony.data.EpsBearerQosSessionAttributes;
-import android.util.Slog;
+import android.util.Log;
 
 import java.util.Objects;
 
@@ -175,18 +175,14 @@
     }
 
     private static void log(@NonNull final String msg) {
-        Slog.d(TAG, msg);
+        Log.d(TAG, msg);
     }
 
     private static void logw(@NonNull final String msg) {
-        Slog.w(TAG, msg);
+        Log.w(TAG, msg);
     }
 
     private static void loge(@NonNull final String msg, final Throwable t) {
-        Slog.e(TAG, msg, t);
-    }
-
-    private static void logwtf(@NonNull final String msg) {
-        Slog.wtf(TAG, msg);
+        Log.e(TAG, msg, t);
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
index 7ef315c..8bda532 100644
--- a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
+++ b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
@@ -29,7 +29,7 @@
 import android.telephony.data.EpsBearerQosSessionAttributes;
 import android.util.Log;
 
-import com.android.internal.util.CollectionUtils;
+import com.android.net.module.util.CollectionUtils;
 import com.android.server.ConnectivityService;
 
 import java.util.ArrayList;
@@ -156,12 +156,13 @@
 
     private void handleUnregisterCallback(@NonNull final IBinder binder,
             final boolean sendToNetworkAgent) {
-        final QosCallbackAgentConnection agentConnection =
-                CollectionUtils.find(mConnections, c -> c.getBinder().equals(binder));
-        if (agentConnection == null) {
-            logw("handleUnregisterCallback: agentConnection is null");
+        final int connIndex =
+                CollectionUtils.indexOf(mConnections, c -> c.getBinder().equals(binder));
+        if (connIndex < 0) {
+            logw("handleUnregisterCallback: no matching agentConnection");
             return;
         }
+        final QosCallbackAgentConnection agentConnection = mConnections.get(connIndex);
 
         if (DBG) {
             log("handleUnregisterCallback: unregister "
@@ -226,10 +227,10 @@
      * @param network the network that was released
      */
     public void handleNetworkReleased(@Nullable final Network network) {
-        final List<QosCallbackAgentConnection> connections =
-                CollectionUtils.filter(mConnections, ac -> ac.getNetwork().equals(network));
-
-        for (final QosCallbackAgentConnection agentConnection : connections) {
+        // Iterate in reverse order as agent connections will be removed when unregistering
+        for (int i = mConnections.size() - 1; i >= 0; i--) {
+            final QosCallbackAgentConnection agentConnection = mConnections.get(i);
+            if (!agentConnection.getNetwork().equals(network)) continue;
             agentConnection.sendEventQosCallbackError(
                     QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED);
 
@@ -247,15 +248,14 @@
             @NonNull final String logPrefix,
             @NonNull final AgentConnectionAction action) {
         mConnectivityServiceHandler.post(() -> {
-            final QosCallbackAgentConnection ac =
-                    CollectionUtils.find(mConnections,
+            final int acIndex = CollectionUtils.indexOf(mConnections,
                             c -> c.getAgentCallbackId() == qosCallbackId);
-            if (ac == null) {
+            if (acIndex == -1) {
                 loge(logPrefix + ": " + qosCallbackId + " missing callback id");
                 return;
             }
 
-            action.execute(ac);
+            action.execute(mConnections.get(acIndex));
         });
     }
 
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 3709963..a0d9365 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -767,6 +767,12 @@
                     }
 
                     private void setDisplayBrightness(float brightness) {
+                        // Ensure brightnessState is valid, before processing and sending to
+                        // surface control
+                        if (Float.isNaN(brightness)) {
+                            return;
+                        }
+
                         if (DEBUG) {
                             Slog.d(TAG, "setDisplayBrightness("
                                     + "id=" + physicalDisplayId
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 8d6bcad..7235a92 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -666,8 +666,19 @@
                         mSelectRequestBuffer.process();
                         resetSelectRequestBuffer();
 
-                        addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
-                        addAndStartAction(new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));
+                        List<HotplugDetectionAction> hotplugActions
+                                = getActions(HotplugDetectionAction.class);
+                        if (hotplugActions.isEmpty()) {
+                            addAndStartAction(
+                                    new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
+                        }
+
+                        List<PowerStatusMonitorAction> powerStatusActions
+                                = getActions(PowerStatusMonitorAction.class);
+                        if (powerStatusActions.isEmpty()) {
+                            addAndStartAction(
+                                    new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));
+                        }
 
                         HdmiDeviceInfo avr = getAvrDeviceInfo();
                         if (avr != null) {
@@ -1062,7 +1073,21 @@
             // Ignore this message.
             return true;
         }
-        setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
+        boolean tvSystemAudioMode = isSystemAudioControlFeatureEnabled();
+        boolean avrSystemAudioMode = HdmiUtils.parseCommandParamSystemAudioStatus(message);
+        // Set System Audio Mode according to TV's settings.
+        // Handle <System Audio Mode Status> here only when
+        // SystemAudioAutoInitiationAction timeout
+        HdmiDeviceInfo avr = getAvrDeviceInfo();
+        if (avr == null) {
+            setSystemAudioMode(false);
+        } else if (avrSystemAudioMode != tvSystemAudioMode) {
+            addAndStartAction(new SystemAudioActionFromTv(this, avr.getLogicalAddress(),
+                    tvSystemAudioMode, null));
+        } else {
+            setSystemAudioMode(tvSystemAudioMode);
+        }
+
         return true;
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
index 8c40424..6f7473d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
@@ -106,9 +106,7 @@
         addHandler(Constants.MESSAGE_SET_STREAM_PATH, mBystander);
         addHandler(Constants.MESSAGE_STANDBY, mBystander);
         addHandler(Constants.MESSAGE_SET_MENU_LANGUAGE, mBystander);
-        addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBystander);
         addHandler(Constants.MESSAGE_USER_CONTROL_RELEASED, mBystander);
-        addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBystander);
         addHandler(Constants.MESSAGE_FEATURE_ABORT, mBystander);
         addHandler(Constants.MESSAGE_INACTIVE_SOURCE, mBystander);
         addHandler(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS, mBystander);
@@ -133,6 +131,8 @@
         addHandler(Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID, mBypasser);
         addHandler(Constants.MESSAGE_GIVE_OSD_NAME, mBypasser);
         addHandler(Constants.MESSAGE_SET_OSD_NAME, mBypasser);
+        addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBypasser);
+        addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser);
 
         addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index c0d577c..4e8fcf7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -159,6 +159,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.inputmethod.CallbackUtils;
 import com.android.internal.inputmethod.IBooleanResultCallback;
+import com.android.internal.inputmethod.IIInputContentUriTokenResultCallback;
 import com.android.internal.inputmethod.IInputBindResultResultCallback;
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodInfoListResultCallback;
@@ -5883,87 +5884,102 @@
 
         @BinderThread
         @Override
-        public void setImeWindowStatus(int vis, int backDisposition) {
-            mImms.setImeWindowStatus(mToken, vis, backDisposition);
+        public void setImeWindowStatus(int vis, int backDisposition,
+                IVoidResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback,
+                    () -> mImms.setImeWindowStatus(mToken, vis, backDisposition));
         }
 
         @BinderThread
         @Override
-        public void reportStartInput(IBinder startInputToken) {
-            mImms.reportStartInput(mToken, startInputToken);
+        public void reportStartInput(IBinder startInputToken, IVoidResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback,
+                    () -> mImms.reportStartInput(mToken, startInputToken));
         }
 
         @BinderThread
         @Override
-        public IInputContentUriToken createInputContentUriToken(Uri contentUri,
-                String packageName) {
-            return mImms.createInputContentUriToken(mToken, contentUri, packageName);
+        public void createInputContentUriToken(Uri contentUri, String packageName,
+                IIInputContentUriTokenResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback,
+                    () -> mImms.createInputContentUriToken(mToken, contentUri, packageName));
         }
 
         @BinderThread
         @Override
-        public void reportFullscreenMode(boolean fullscreen) {
-            mImms.reportFullscreenMode(mToken, fullscreen);
+        public void reportFullscreenMode(boolean fullscreen, IVoidResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback,
+                    () -> mImms.reportFullscreenMode(mToken, fullscreen));
         }
 
         @BinderThread
         @Override
-        public void setInputMethod(String id) {
-            mImms.setInputMethod(mToken, id);
+        public void setInputMethod(String id, IVoidResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback, () -> mImms.setInputMethod(mToken, id));
         }
 
         @BinderThread
         @Override
-        public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype) {
-            mImms.setInputMethodAndSubtype(mToken, id, subtype);
+        public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype,
+                IVoidResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback,
+                    () -> mImms.setInputMethodAndSubtype(mToken, id, subtype));
         }
 
         @BinderThread
         @Override
-        public void hideMySoftInput(int flags) {
-            mImms.hideMySoftInput(mToken, flags);
+        public void hideMySoftInput(int flags, IVoidResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback, () -> mImms.hideMySoftInput(mToken, flags));
         }
 
         @BinderThread
         @Override
-        public void showMySoftInput(int flags) {
-            mImms.showMySoftInput(mToken, flags);
+        public void showMySoftInput(int flags, IVoidResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback, () -> mImms.showMySoftInput(mToken, flags));
         }
 
         @BinderThread
         @Override
-        public void updateStatusIcon(String packageName, @DrawableRes int iconId) {
-            mImms.updateStatusIcon(mToken, packageName, iconId);
+        public void updateStatusIcon(String packageName, @DrawableRes int iconId,
+                IVoidResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback,
+                    () -> mImms.updateStatusIcon(mToken, packageName, iconId));
         }
 
         @BinderThread
         @Override
-        public boolean switchToPreviousInputMethod() {
-            return mImms.switchToPreviousInputMethod(mToken);
+        public void switchToPreviousInputMethod(IBooleanResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback, () -> mImms.switchToPreviousInputMethod(mToken));
         }
 
         @BinderThread
         @Override
-        public boolean switchToNextInputMethod(boolean onlyCurrentIme) {
-            return mImms.switchToNextInputMethod(mToken, onlyCurrentIme);
+        public void switchToNextInputMethod(boolean onlyCurrentIme,
+                IBooleanResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback,
+                    () -> mImms.switchToNextInputMethod(mToken, onlyCurrentIme));
         }
 
         @BinderThread
         @Override
-        public boolean shouldOfferSwitchingToNextInputMethod() {
-            return mImms.shouldOfferSwitchingToNextInputMethod(mToken);
+        public void shouldOfferSwitchingToNextInputMethod(
+                IBooleanResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback,
+                    () -> mImms.shouldOfferSwitchingToNextInputMethod(mToken));
         }
 
         @BinderThread
         @Override
-        public void notifyUserAction() {
-            mImms.notifyUserAction(mToken);
+        public void notifyUserAction(IVoidResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback, () -> mImms.notifyUserAction(mToken));
         }
 
         @BinderThread
         @Override
-        public void applyImeVisibility(IBinder windowToken, boolean setVisible) {
-            mImms.applyImeVisibility(mToken, windowToken, setVisible);
+        public void applyImeVisibility(IBinder windowToken, boolean setVisible,
+                IVoidResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback,
+                    () -> mImms.applyImeVisibility(mToken, windowToken, setVisible));
         }
     }
 }
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index 05d0aef..dbd8dd9 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -21,7 +21,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.SystemClock;
-import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 
 import com.android.server.FgThread;
@@ -55,8 +55,8 @@
 
         // TODO: this doesn't account for multisim phones
 
-        mTelephonyManager.registerPhoneStateListener(FgThread.getExecutor(),
-                new EmergencyCallPhoneStateListener());
+        mTelephonyManager.registerTelephonyCallback(FgThread.getExecutor(),
+                new EmergencyCallTelephonyCallback());
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -78,8 +78,8 @@
                 || mTelephonyManager.isInEmergencySmsMode();
     }
 
-    private class EmergencyCallPhoneStateListener extends PhoneStateListener implements
-            PhoneStateListener.CallStateChangedListener {
+    private class EmergencyCallTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.CallStateListener{
 
         @Override
         public void onCallStateChanged(int state, String incomingNumber) {
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 7e00fd6..364aa2c 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -193,6 +193,17 @@
                     0);
         }
 
+        public int getLoadEscrowDataRetryLimit() {
+            return DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
+                    "load_escrow_data_retry_count", DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT);
+        }
+
+        public int getLoadEscrowDataRetryIntervalSeconds() {
+            return DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
+                    "load_escrow_data_retry_interval_seconds",
+                    DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
+        }
+
         public void reportMetric(boolean success) {
             // TODO(b/179105110) design error code; and report the true value for other fields.
             FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, 0, 1, 1,
@@ -251,11 +262,8 @@
             List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
         Objects.requireNonNull(retryHandler);
 
-        final int retryLimit = DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
-                "load_escrow_data_retry_count", DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT);
-        final int retryIntervalInSeconds = DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
-                "load_escrow_data_retry_interval_seconds",
-                DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
+        final int retryLimit = mInjector.getLoadEscrowDataRetryLimit();
+        final int retryIntervalInSeconds = mInjector.getLoadEscrowDataRetryIntervalSeconds();
 
         if (attemptNumber < retryLimit) {
             Slog.i(TAG, "Scheduling loadRebootEscrowData retry number: " + attemptNumber);
diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
index 9c471b8..ec80521 100644
--- a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
+++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
@@ -136,7 +136,7 @@
         }
 
         /** Bind to the service */
-        public void bindToService(long timeOut) throws TimeoutException {
+        public void bindToService(long timeOut) throws RemoteException, TimeoutException {
             if (mBinder == null || !mBinder.asBinder().isBinderAlive()) {
                 CountDownLatch connectionLatch = new CountDownLatch(1);
                 Intent intent = new Intent();
@@ -210,27 +210,25 @@
 
         private void throwTypedException(
                 ParcelableException exception)
-                throws IOException {
-            if (exception.getCause() instanceof IOException) {
+                throws IOException, RemoteException {
+            if (exception != null && exception.getCause() instanceof IOException) {
                 exception.maybeRethrow(IOException.class);
-            } else if (exception.getCause() instanceof IllegalStateException) {
-                exception.maybeRethrow(IllegalStateException.class);
             } else {
-                // This should not happen. Wrap the cause in IllegalStateException so that it
-                // doesn't disrupt the exception handling
-                throw new IllegalStateException(exception.getCause());
+                // Wrap the exception and throw it as a RemoteException.
+                throw new RemoteException(TAG + " wrap/unwrap failed", exception,
+                        true /* enableSuppression */, true /* writableStackTrace */);
             }
         }
 
         private void waitForLatch(CountDownLatch latch, String reason, long timeOut)
-                throws TimeoutException {
+                throws RemoteException, TimeoutException {
             try {
                 if (!latch.await(timeOut, TimeUnit.SECONDS)) {
                     throw new TimeoutException("Latch wait for " + reason + " elapsed");
                 }
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
-                throw new IllegalStateException("Latch wait for " + reason + " interrupted");
+                throw new RemoteException("Latch wait for " + reason + " interrupted");
             }
         }
     }
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 7ab8b27..a5763ae 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -35,6 +35,7 @@
 import android.service.gatekeeper.GateKeeperResponse;
 import android.service.gatekeeper.IGateKeeperService;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -906,7 +907,7 @@
         if (!tokenMap.containsKey(userId)) {
             return Collections.emptySet();
         }
-        return tokenMap.get(userId).keySet();
+        return new ArraySet<>(tokenMap.get(userId).keySet());
     }
 
     public boolean removePendingToken(long handle, int userId) {
diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
index 639dda6..23195bb 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -93,8 +93,7 @@
             StatsLog.write(statsEvent);
         }
 
-        @Override
-        public String getSessionId(int userId) {
+        private String getSessionIdInternal(int userId) {
             byte[] byteId = new byte[16]; // 128 bits
             mSecureRandom.nextBytes(byteId);
             String id = Base64.encodeToString(byteId, Base64.DEFAULT);
@@ -102,6 +101,16 @@
         }
 
         @Override
+        public String getPlaybackSessionId(int userId) {
+            return getSessionIdInternal(userId);
+        }
+
+        @Override
+        public String getRecordingSessionId(int userId) {
+            return getSessionIdInternal(userId);
+        }
+
+        @Override
         public void reportPlaybackErrorEvent(
                 String sessionId, PlaybackErrorEvent event, int userId) {
             StatsEvent statsEvent = StatsEvent.newBuilder()
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 067c5c0e..da62aca 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -169,11 +169,10 @@
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkPolicyManager.UidState;
-import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
-import android.net.NetworkState;
+import android.net.NetworkStateSnapshot;
 import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
@@ -431,7 +430,7 @@
     private final CarrierConfigManager mCarrierConfigManager;
     private final MultipathPolicyTracker mMultipathPolicyTracker;
 
-    private IConnectivityManager mConnManager;
+    private ConnectivityManager mConnManager;
     private PowerManagerInternal mPowerManagerInternal;
     private PowerWhitelistManager mPowerWhitelistManager;
 
@@ -711,8 +710,9 @@
                 new NetworkPolicyManagerInternalImpl());
     }
 
-    public void bindConnectivityManager(IConnectivityManager connManager) {
-        mConnManager = Objects.requireNonNull(connManager, "missing IConnectivityManager");
+    public void bindConnectivityManager() {
+        mConnManager = Objects.requireNonNull(mContext.getSystemService(ConnectivityManager.class),
+                "missing ConnectivityManager");
     }
 
     @GuardedBy("mUidRulesFirstLock")
@@ -943,7 +943,7 @@
             mContext.registerReceiver(mCarrierConfigReceiver, carrierConfigFilter, null, mHandler);
 
             // listen for meteredness changes
-            mContext.getSystemService(ConnectivityManager.class).registerNetworkCallback(
+            mConnManager.registerNetworkCallback(
                     new NetworkRequest.Builder().build(), mNetworkCallback);
 
             mAppStandby.addListener(new NetPolicyAppIdleStateChangeListener());
@@ -1887,14 +1887,14 @@
     }
 
     /**
-     * Collect all ifaces from a {@link NetworkState} into the given set.
+     * Collect all ifaces from a {@link NetworkStateSnapshot} into the given set.
      */
-    private static void collectIfaces(ArraySet<String> ifaces, NetworkState state) {
-        final String baseIface = state.linkProperties.getInterfaceName();
+    private static void collectIfaces(ArraySet<String> ifaces, NetworkStateSnapshot snapshot) {
+        final String baseIface = snapshot.linkProperties.getInterfaceName();
         if (baseIface != null) {
             ifaces.add(baseIface);
         }
-        for (LinkProperties stackedLink : state.linkProperties.getStackedLinks()) {
+        for (LinkProperties stackedLink : snapshot.linkProperties.getStackedLinks()) {
             final String stackedIface = stackedLink.getInterfaceName();
             if (stackedIface != null) {
                 ifaces.add(stackedIface);
@@ -1964,7 +1964,7 @@
     }
 
     /**
-     * Examine all connected {@link NetworkState}, looking for
+     * Examine all connected {@link NetworkStateSnapshot}, looking for
      * {@link NetworkPolicy} that need to be enforced. When matches found, set
      * remaining quota based on usage cycle and historical stats.
      */
@@ -1973,29 +1973,21 @@
         if (LOGV) Slog.v(TAG, "updateNetworkRulesNL()");
         Trace.traceBegin(TRACE_TAG_NETWORK, "updateNetworkRulesNL");
 
-        final NetworkState[] states;
-        try {
-            states = defeatNullable(mConnManager.getAllNetworkState());
-        } catch (RemoteException e) {
-            // ignored; service lives in system_server
-            return;
-        }
+        final List<NetworkStateSnapshot> snapshots = mConnManager.getAllNetworkStateSnapshot();
 
         // First, generate identities of all connected networks so we can
         // quickly compare them against all defined policies below.
         mNetIdToSubId.clear();
-        final ArrayMap<NetworkState, NetworkIdentity> identified = new ArrayMap<>();
-        for (NetworkState state : states) {
-            if (state.network != null) {
-                mNetIdToSubId.put(state.network.netId, parseSubId(state));
-            }
+        final ArrayMap<NetworkStateSnapshot, NetworkIdentity> identified = new ArrayMap<>();
+        for (final NetworkStateSnapshot snapshot : snapshots) {
+            mNetIdToSubId.put(snapshot.network.netId, parseSubId(snapshot));
 
             // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
             // in the object created here is never used and its value doesn't matter, so use
             // NETWORK_TYPE_UNKNOWN.
-            final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+            final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
                     true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
-            identified.put(state, ident);
+            identified.put(snapshot, ident);
         }
 
         final ArraySet<String> newMeteredIfaces = new ArraySet<>();
@@ -2069,10 +2061,10 @@
 
         // One final pass to catch any metered ifaces that don't have explicitly
         // defined policies; typically Wi-Fi networks.
-        for (NetworkState state : states) {
-            if (!state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+        for (final NetworkStateSnapshot snapshot : snapshots) {
+            if (!snapshot.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
                 matchingIfaces.clear();
-                collectIfaces(matchingIfaces, state);
+                collectIfaces(matchingIfaces, snapshot);
                 for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
                     final String iface = matchingIfaces.valueAt(j);
                     if (!newMeteredIfaces.contains(iface)) {
@@ -2104,16 +2096,16 @@
 
         // Finally, calculate our opportunistic quotas
         mSubscriptionOpportunisticQuota.clear();
-        for (NetworkState state : states) {
+        for (final NetworkStateSnapshot snapshot : snapshots) {
             if (!quotaEnabled) continue;
-            if (state.network == null) continue;
-            final int subId = getSubIdLocked(state.network);
+            if (snapshot.network == null) continue;
+            final int subId = getSubIdLocked(snapshot.network);
             final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId);
             if (plan == null) continue;
 
             final long quotaBytes;
             final long limitBytes = plan.getDataLimitBytes();
-            if (!state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
+            if (!snapshot.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
                 // Clamp to 0 when roaming
                 quotaBytes = 0;
             } else if (limitBytes == SubscriptionPlan.BYTES_UNKNOWN) {
@@ -2131,7 +2123,7 @@
                         .truncatedTo(ChronoUnit.DAYS)
                         .toInstant().toEpochMilli();
                 final long totalBytes = getTotalBytes(
-                        NetworkTemplate.buildTemplateMobileAll(state.subscriberId),
+                        NetworkTemplate.buildTemplateMobileAll(snapshot.subscriberId),
                         start, startOfDay);
                 final long remainingBytes = limitBytes - totalBytes;
                 // Number of remaining days including current day
@@ -3164,14 +3156,6 @@
         }
     }
 
-    @Override
-    @Deprecated
-    public NetworkQuotaInfo getNetworkQuotaInfo(NetworkState state) {
-        Log.w(TAG, "Shame on UID " + Binder.getCallingUid()
-                + " for calling the hidden API getNetworkQuotaInfo(). Shame!");
-        return new NetworkQuotaInfo();
-    }
-
     private void enforceSubscriptionPlanAccess(int subId, int callingUid, String callingPackage) {
         // Verify they're not lying about package name
         mAppOps.checkPackage(callingUid, callingPackage);
@@ -5635,11 +5619,10 @@
         }
     }
 
-    private int parseSubId(NetworkState state) {
+    private int parseSubId(@NonNull NetworkStateSnapshot snapshot) {
         int subId = INVALID_SUBSCRIPTION_ID;
-        if (state != null && state.networkCapabilities != null
-                && state.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
-            NetworkSpecifier spec = state.networkCapabilities.getNetworkSpecifier();
+        if (snapshot.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+            NetworkSpecifier spec = snapshot.networkCapabilities.getNetworkSpecifier();
             if (spec instanceof TelephonyNetworkSpecifier) {
                 subId = ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
             }
@@ -5716,10 +5699,6 @@
         return (uidRules & rule) != 0;
     }
 
-    private static @NonNull NetworkState[] defeatNullable(@Nullable NetworkState[] val) {
-        return (val != null) ? val : new NetworkState[0];
-    }
-
     private static boolean getBooleanDefeatingNullable(@Nullable PersistableBundle bundle,
             String key, boolean defaultValue) {
         return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index ebf1fe9..e0f5346 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -24,7 +24,6 @@
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_UID;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.ConnectivityManager.isNetworkTypeMobile;
 import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
 import static android.net.NetworkStack.checkNetworkStackPermission;
@@ -45,6 +44,7 @@
 import static android.net.NetworkStatsHistory.FIELD_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
 import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
+import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.net.TrafficStats.UNSUPPORTED;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 460b2f2..903652a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2086,15 +2086,16 @@
             try {
                 sealLocked();
 
-                // Session that are staged, ready and not multi package will be installed during
-                // this boot. As such, we need populate all the fields for successful installation.
-                if (isMultiPackage()) {
+                // Session that are staged, committed and not multi package will be installed or
+                // restart verification during this boot. As such, we need populate all the fields
+                // for successful installation.
+                if (isMultiPackage() || !isStaged() || !isCommitted()) {
                     return;
                 }
                 final PackageInstallerSession root = hasParentSessionId()
                         ? allSessions.get(getParentSessionId())
                         : this;
-                if (root != null && root.isStagedSessionReady()) {
+                if (root != null) {
                     if (isApexSession()) {
                         validateApexInstallLocked();
                     } else {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7da53b5..b751503 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -20368,6 +20368,11 @@
             return;
         }
 
+        if (isIncrementalPath(pkg.getPath()) && IncrementalManager.getVersion()
+                < IncrementalManager.MIN_VERSION_TO_SUPPORT_FSVERITY) {
+            return;
+        }
+
         // Collect files we care for fs-verity setup.
         ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
         if (legacyMode) {
diff --git a/services/core/java/com/android/server/pm/SettingsXml.java b/services/core/java/com/android/server/pm/SettingsXml.java
index ec643f5..c53fef7 100644
--- a/services/core/java/com/android/server/pm/SettingsXml.java
+++ b/services/core/java/com/android/server/pm/SettingsXml.java
@@ -83,7 +83,7 @@
         @Override
         public void close() throws IOException {
             mWriteSection.closeCompletely();
-            mXmlSerializer.endDocument();
+            mXmlSerializer.flush();
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a377f1c..d1cf55d 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -103,6 +103,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.CollectionUtils;
@@ -142,6 +143,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -1885,8 +1887,8 @@
     // === APIs ===
 
     @Override
-    public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
-            @UserIdInt int userId) {
+    public void setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+            @UserIdInt int userId, @NonNull AndroidFuture callback) {
         verifyCaller(packageName, userId);
 
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
@@ -1913,7 +1915,7 @@
 
             // Throttling.
             if (!ps.tryApiCall(unlimited)) {
-                return false;
+                callback.complete(false);
             }
 
             // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
@@ -1949,12 +1951,12 @@
 
         verifyStates();
 
-        return true;
+        callback.complete(true);
     }
 
     @Override
-    public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
-            @UserIdInt int userId) {
+    public void updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+            @UserIdInt int userId, AndroidFuture callback) {
         verifyCaller(packageName, userId);
 
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
@@ -1981,7 +1983,8 @@
 
             // Throttling.
             if (!ps.tryApiCall(unlimited)) {
-                return false;
+                callback.complete(false);
+                return;
             }
 
             // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
@@ -2046,12 +2049,12 @@
 
         verifyStates();
 
-        return true;
+        callback.complete(true);
     }
 
     @Override
-    public boolean  addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
-            @UserIdInt int userId) {
+    public void addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+            @UserIdInt int userId, AndroidFuture callback) {
         verifyCaller(packageName, userId);
 
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
@@ -2081,7 +2084,8 @@
 
             // Throttling.
             if (!ps.tryApiCall(unlimited)) {
-                return false;
+                callback.complete(false);
+                return;
             }
             for (int i = 0; i < size; i++) {
                 final ShortcutInfo newShortcut = newShortcuts.get(i);
@@ -2109,7 +2113,7 @@
 
         verifyStates();
 
-        return true;
+        callback.complete(true);
     }
 
     @Override
@@ -2174,15 +2178,17 @@
     }
 
     @Override
-    public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut,
-            IntentSender resultIntent, int userId) {
+    public void requestPinShortcut(String packageName, ShortcutInfo shortcut,
+            IntentSender resultIntent, int userId, AndroidFuture callback) {
         Objects.requireNonNull(shortcut);
+        Objects.requireNonNull(callback);
         Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
-        return requestPinItem(packageName, userId, shortcut, null, null, resultIntent);
+        callback.complete(requestPinItem(packageName, userId, shortcut, null, null, resultIntent));
     }
 
     @Override
-    public Intent createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId)
+    public void createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId,
+            AndroidFuture callback)
             throws RemoteException {
         Objects.requireNonNull(shortcut);
         Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
@@ -2198,7 +2204,7 @@
         }
 
         verifyStates();
-        return ret;
+        callback.complete(ret);
     }
 
     /**
@@ -2455,8 +2461,9 @@
     }
 
     @Override
-    public ParceledListSlice<ShortcutInfo> getShortcuts(String packageName,
-            @ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId) {
+    public void getShortcuts(String packageName,
+            @ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId,
+            AndroidFuture<ParceledListSlice<ShortcutInfo>> callback) {
         verifyCaller(packageName, userId);
 
         synchronized (mLock) {
@@ -2472,16 +2479,16 @@
                     | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
                     | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
 
-            return getShortcutsWithQueryLocked(
+            callback.complete(getShortcutsWithQueryLocked(
                     packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
                     (ShortcutInfo si) ->
-                            si.isVisibleToPublisher() && (si.getFlags() & shortcutFlags) != 0);
+                            si.isVisibleToPublisher() && (si.getFlags() & shortcutFlags) != 0));
         }
     }
 
     @Override
-    public ParceledListSlice<ShortcutManager.ShareShortcutInfo> getShareTargets(String packageName,
-            IntentFilter filter, @UserIdInt int userId) {
+    public void getShareTargets(String packageName, IntentFilter filter, @UserIdInt int userId,
+            AndroidFuture<ParceledListSlice> callback) {
         Preconditions.checkStringNotEmpty(packageName, "packageName");
         Objects.requireNonNull(filter, "intentFilter");
 
@@ -2497,7 +2504,7 @@
             final ShortcutUser user = getUserShortcutsLocked(userId);
             user.forAllPackages(p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter)));
 
-            return new ParceledListSlice<>(shortcutInfoList);
+            callback.complete(new ParceledListSlice<>(shortcutInfoList));
         }
     }
 
@@ -3081,8 +3088,14 @@
         @Override
         public List<ShortcutManager.ShareShortcutInfo> getShareTargets(
                 @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId) {
-            return ShortcutService.this.getShareTargets(
-                    callingPackage, intentFilter, userId).getList();
+            final AndroidFuture<ParceledListSlice> future = new AndroidFuture<>();
+            ShortcutService.this.getShareTargets(
+                    callingPackage, intentFilter, userId, future);
+            try {
+                return future.get().getList();
+            } catch (InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 871576e..8283ac6 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1544,7 +1544,7 @@
     public String getUserName() {
         final int callingUid = Binder.getCallingUid();
         if (!hasManageOrCreateUsersPermission()
-                || hasPermissionGranted(
+                && !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");
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index c9067a3..b61fd8d 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -33,9 +33,9 @@
 import com.android.internal.util.CollectionUtils;
 import com.android.server.pm.PackageSetting;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
 import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
 import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
-import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
 
 import java.util.Arrays;
 import java.util.function.Function;
@@ -169,8 +169,8 @@
         }
 
         ArraySet<String> allWebDomains = mCollector.collectAllWebDomains(pkg);
-        SparseArray<DomainVerificationUserState> userStates =
-                pkgState.getUserSelectionStates();
+        SparseArray<DomainVerificationInternalUserState> userStates =
+                pkgState.getUserStates();
         if (userId == UserHandle.USER_ALL) {
             int size = userStates.size();
             if (size == 0) {
@@ -178,13 +178,13 @@
                         wasHeaderPrinted);
             } else {
                 for (int index = 0; index < size; index++) {
-                    DomainVerificationUserState userState = userStates.valueAt(index);
+                    DomainVerificationInternalUserState userState = userStates.valueAt(index);
                     printState(writer, pkgState, userState.getUserId(), userState, reusedSet,
                             allWebDomains, wasHeaderPrinted);
                 }
             }
         } else {
-            DomainVerificationUserState userState = userStates.get(userId);
+            DomainVerificationInternalUserState userState = userStates.get(userId);
             printState(writer, pkgState, userId, userState, reusedSet, allWebDomains,
                     wasHeaderPrinted);
         }
@@ -192,8 +192,9 @@
 
     boolean printState(@NonNull IndentingPrintWriter writer,
             @NonNull DomainVerificationPkgState pkgState, @UserIdInt int userId,
-            @Nullable DomainVerificationUserState userState, @NonNull ArraySet<String> reusedSet,
-            @NonNull ArraySet<String> allWebDomains, boolean wasHeaderPrinted) {
+            @Nullable DomainVerificationInternalUserState userState,
+            @NonNull ArraySet<String> reusedSet, @NonNull ArraySet<String> allWebDomains,
+            boolean wasHeaderPrinted) {
         reusedSet.clear();
         reusedSet.addAll(allWebDomains);
         if (userState != null) {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
index ed37fa0..1721a18 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -132,6 +132,21 @@
     /**
      * Enforced when mutating user selection state inside an exposed API method.
      */
+    public boolean assertApprovedUserStateQuerent(int callingUid, @UserIdInt int callingUserId,
+            @NonNull String packageName, @UserIdInt int targetUserId) throws SecurityException {
+        if (callingUserId != targetUserId) {
+            mContext.enforcePermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS,
+                    Binder.getCallingPid(), callingUid,
+                    "Caller is not allowed to edit other users");
+        }
+
+        return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
+    }
+
+    /**
+     * Enforced when mutating user selection state inside an exposed API method.
+     */
     public boolean assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
             @Nullable String packageName, @UserIdInt int targetUserId) throws SecurityException {
         if (callingUserId != targetUserId) {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java
index c787356..4bad102 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java
@@ -32,7 +32,6 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
-import java.util.Map;
 
 /**
  * Reads and writes the old {@link android.content.pm.IntentFilterVerificationInfo} so that it can
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 a68b3da..0c2b4c5 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
@@ -48,7 +48,7 @@
 import java.util.UUID;
 import java.util.function.Function;
 
-public interface DomainVerificationManagerInternal extends DomainVerificationManager {
+public interface DomainVerificationManagerInternal {
 
     UUID DISABLED_ID = new UUID(0, 0);
 
@@ -69,8 +69,8 @@
      * during the legacy transition period.
      *
      * TODO(b/177923646): The legacy values can be removed once the Settings API changes are
-     *  shipped. These values are not stable, so just deleting the constant and shifting others is
-     *  fine.
+     * shipped. These values are not stable, so just deleting the constant and shifting others is
+     * fine.
      */
     int APPROVAL_LEVEL_LEGACY_ASK = 1;
 
@@ -84,14 +84,15 @@
 
     /**
      * The app has been chosen by the user through
-     * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)}, indictag an explicit
-     * choice to use this app to open an unverified domain.
+     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)},
+     * indicating an explicit choice to use this app to open an unverified domain.
      */
     int APPROVAL_LEVEL_SELECTION = 2;
 
     /**
      * The app is approved through the digital asset link statement being hosted at the domain
-     * it is capturing. This is set through {@link #setDomainVerificationStatus(UUID, Set, int)} by
+     * it is capturing. This is set through
+     * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)} by
      * the domain verification agent on device.
      */
     int APPROVAL_LEVEL_VERIFIED = 3;
@@ -102,7 +103,7 @@
      * declares against the digital asset link statements before allowing it to be installed.
      *
      * The user is still able to disable instant app link handling through
-     * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}.
+     * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, boolean)}.
      */
     int APPROVAL_LEVEL_INSTANT_APP = 4;
 
@@ -122,7 +123,17 @@
             APPROVAL_LEVEL_VERIFIED,
             APPROVAL_LEVEL_INSTANT_APP
     })
-    @interface ApprovalLevel{}
+    @interface ApprovalLevel {
+    }
+
+    /** @see DomainVerificationManager#getDomainVerificationInfo(String) */
+    @Nullable
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
+            android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+    })
+    DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
+            throws NameNotFoundException;
 
     /**
      * Generate a new domain set ID to be used for attaching new packages.
@@ -173,9 +184,9 @@
     /**
      * Migrates verification state from a previous install to a new one. It is expected that the
      * {@link PackageSetting#getDomainSetId()} already be set to the correct value, usually from
-     * {@link #generateNewId()}. This will preserve {@link #STATE_SUCCESS} domains under the
-     * assumption that the new package will pass the same server side config as the previous
-     * package, as they have matching signatures.
+     * {@link #generateNewId()}. This will preserve {@link DomainVerificationManager#STATE_SUCCESS}
+     * domains under the assumption that the new package will pass the same server side config as
+     * the previous package, as they have matching signatures.
      * <p>
      * This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
      * lock. This should never be called from within the domain verification classes themselves.
@@ -229,8 +240,10 @@
      * tag has already been entered.
      * <p>
      * This is <b>only</b> for restore, and will override package states, ignoring if their {@link
-     * DomainVerificationInfo#getIdentifier()}s match. It's expected that any restored domains marked
-     * as success verify against the server correctly, although the verification agent may decide to
+     * DomainVerificationInfo#getIdentifier()}s match. It's expected that any restored domains
+     * marked
+     * as success verify against the server correctly, although the verification agent may decide
+     * to
      * re-verify them when it gets the chance.
      */
     /*
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
index 6f28107..a7a52e0 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
@@ -23,29 +23,29 @@
 import android.content.pm.verify.domain.DomainOwner;
 import android.content.pm.verify.domain.DomainSet;
 import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationManager;
 import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
-import android.content.pm.verify.domain.DomainVerificationManagerImpl;
-import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.DomainVerificationUserState;
 import android.content.pm.verify.domain.IDomainVerificationManager;
 import android.os.ServiceSpecificException;
 
 import java.util.List;
 import java.util.UUID;
 
-class DomainVerificationManagerStub extends IDomainVerificationManager.Stub {
+public class DomainVerificationManagerStub extends IDomainVerificationManager.Stub {
 
     @NonNull
-    private DomainVerificationService mService;
+    private final DomainVerificationService mService;
 
-    DomainVerificationManagerStub(DomainVerificationService service) {
+    public DomainVerificationManagerStub(DomainVerificationService service) {
         mService = service;
     }
 
     @NonNull
     @Override
-    public List<String> getValidVerificationPackageNames() {
+    public List<String> queryValidVerificationPackageNames() {
         try {
-            return mService.getValidVerificationPackageNames();
+            return mService.queryValidVerificationPackageNames();
         } catch (Exception e) {
             throw rethrow(e);
         }
@@ -95,10 +95,10 @@
 
     @Nullable
     @Override
-    public DomainVerificationUserSelection getDomainVerificationUserSelection(
+    public DomainVerificationUserState getDomainVerificationUserState(
             String packageName, @UserIdInt int userId) {
         try {
-            return mService.getDomainVerificationUserSelection(packageName, userId);
+            return mService.getDomainVerificationUserState(packageName, userId);
         } catch (Exception e) {
             throw rethrow(e);
         }
@@ -117,13 +117,13 @@
 
     private RuntimeException rethrow(Exception exception) throws RuntimeException {
         if (exception instanceof InvalidDomainSetException) {
-            int packedErrorCode = DomainVerificationManagerImpl.ERROR_INVALID_DOMAIN_SET;
+            int packedErrorCode = DomainVerificationManager.ERROR_INVALID_DOMAIN_SET;
             packedErrorCode |= ((InvalidDomainSetException) exception).getReason() << 16;
             return new ServiceSpecificException(packedErrorCode,
                     ((InvalidDomainSetException) exception).getPackageName());
         } else if (exception instanceof NameNotFoundException) {
             return new ServiceSpecificException(
-                    DomainVerificationManagerImpl.ERROR_NAME_NOT_FOUND);
+                    DomainVerificationManager.ERROR_NAME_NOT_FOUND);
         } else if (exception instanceof RuntimeException) {
             return (RuntimeException) exception;
         } else {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
index c864b29..abb8d2f 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
@@ -27,9 +27,9 @@
 import android.util.TypedXmlSerializer;
 
 import com.android.server.pm.SettingsXml;
+import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
 import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
 import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
-import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -157,7 +157,7 @@
         UUID id = UUID.fromString(idString);
 
         final ArrayMap<String, Integer> stateMap = new ArrayMap<>();
-        final SparseArray<DomainVerificationUserState> userStates = new SparseArray<>();
+        final SparseArray<DomainVerificationInternalUserState> userStates = new SparseArray<>();
 
         SettingsXml.ChildSection child = section.children();
         while (child.moveToNext()) {
@@ -176,10 +176,10 @@
     }
 
     private static void readUserStates(@NonNull SettingsXml.ReadSection section,
-            @NonNull SparseArray<DomainVerificationUserState> userStates) {
+            @NonNull SparseArray<DomainVerificationInternalUserState> userStates) {
         SettingsXml.ChildSection child = section.children();
         while (child.moveToNext(TAG_USER_STATE)) {
-            DomainVerificationUserState userState = createUserStateFromXml(child);
+            DomainVerificationInternalUserState userState = createUserStateFromXml(child);
             if (userState != null) {
                 userStates.put(userState.getUserId(), userState);
             }
@@ -205,12 +205,12 @@
                              .attribute(ATTR_HAS_AUTO_VERIFY_DOMAINS,
                                      pkgState.isHasAutoVerifyDomains())) {
             writeStateMap(parentSection, pkgState.getStateMap());
-            writeUserStates(parentSection, pkgState.getUserSelectionStates());
+            writeUserStates(parentSection, pkgState.getUserStates());
         }
     }
 
     private static void writeUserStates(@NonNull SettingsXml.WriteSection parentSection,
-            @NonNull SparseArray<DomainVerificationUserState> states) throws IOException {
+            @NonNull SparseArray<DomainVerificationInternalUserState> states) throws IOException {
         int size = states.size();
         if (size == 0) {
             return;
@@ -245,7 +245,7 @@
      * entered.
      */
     @Nullable
-    public static DomainVerificationUserState createUserStateFromXml(
+    public static DomainVerificationInternalUserState createUserStateFromXml(
             @NonNull SettingsXml.ReadSection section) {
         int userId = section.getInt(ATTR_USER_ID);
         if (userId == -1) {
@@ -260,7 +260,7 @@
             readEnabledHosts(child, enabledHosts);
         }
 
-        return new DomainVerificationUserState(userId, enabledHosts, allowLinkHandling);
+        return new DomainVerificationInternalUserState(userId, enabledHosts, allowLinkHandling);
     }
 
     private static void readEnabledHosts(@NonNull SettingsXml.ReadSection section,
@@ -275,7 +275,7 @@
     }
 
     public static void writeUserStateToXml(@NonNull SettingsXml.WriteSection parentSection,
-            @NonNull DomainVerificationUserState userState) throws IOException {
+            @NonNull DomainVerificationInternalUserState userState) throws IOException {
         try (SettingsXml.WriteSection section =
                      parentSection.startSection(TAG_USER_STATE)
                              .attribute(ATTR_USER_ID, userState.getUserId())
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 dbd7f96..e85bbe4 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
@@ -34,8 +34,9 @@
 import android.content.pm.verify.domain.DomainOwner;
 import android.content.pm.verify.domain.DomainVerificationInfo;
 import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
 import android.content.pm.verify.domain.DomainVerificationState;
-import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.DomainVerificationUserState;
 import android.content.pm.verify.domain.IDomainVerificationManager;
 import android.os.UserHandle;
 import android.util.ArrayMap;
@@ -55,9 +56,9 @@
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.PackageSetting;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
 import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
 import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
-import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyUnavailable;
 
@@ -208,8 +209,7 @@
     }
 
     @NonNull
-    @Override
-    public List<String> getValidVerificationPackageNames() {
+    public List<String> queryValidVerificationPackageNames() {
         mEnforcer.assertApprovedVerifier(mConnection.getCallingUid(), mProxy);
         List<String> packageNames = new ArrayList<>();
         synchronized (mLock) {
@@ -272,7 +272,6 @@
         }
     }
 
-    @Override
     public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
             int state) throws InvalidDomainSetException, NameNotFoundException {
         if (state < DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED) {
@@ -314,7 +313,7 @@
 
             int size = verifiedDomains.size();
             for (int index = 0; index < size; index++) {
-                removeUserSelectionsForDomain(verifiedDomains.get(index));
+                removeUserStatesForDomain(verifiedDomains.get(index));
             }
         }
 
@@ -401,12 +400,12 @@
         }
     }
 
-    private void removeUserSelectionsForDomain(@NonNull String domain) {
+    private void removeUserStatesForDomain(@NonNull String domain) {
         synchronized (mLock) {
             final int size = mAttachedPkgStates.size();
             for (int index = 0; index < size; index++) {
                 DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
-                SparseArray<DomainVerificationUserState> array = pkgState.getUserSelectionStates();
+                SparseArray<DomainVerificationInternalUserState> array = pkgState.getUserStates();
                 int arraySize = array.size();
                 for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) {
                     array.valueAt(arrayIndex).removeHost(domain);
@@ -415,13 +414,6 @@
         }
     }
 
-    @Override
-    public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
-            boolean allowed) throws NameNotFoundException {
-        setDomainVerificationLinkHandlingAllowed(packageName, allowed,
-                mConnection.getCallingUserId());
-    }
-
     public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
             boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
         if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
@@ -434,7 +426,7 @@
                 throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
 
-            pkgState.getOrCreateUserSelectionState(userId)
+            pkgState.getOrCreateUserState(userId)
                     .setLinkHandlingAllowed(allowed);
         }
 
@@ -452,11 +444,11 @@
                     DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(pkgStateIndex);
                     if (userId == UserHandle.USER_ALL) {
                         for (int aUserId : mConnection.getAllUserIds()) {
-                            pkgState.getOrCreateUserSelectionState(aUserId)
+                            pkgState.getOrCreateUserState(aUserId)
                                     .setLinkHandlingAllowed(allowed);
                         }
                     } else {
-                        pkgState.getOrCreateUserSelectionState(userId)
+                        pkgState.getOrCreateUserState(userId)
                                 .setLinkHandlingAllowed(allowed);
                     }
                 }
@@ -468,7 +460,7 @@
                     throw DomainVerificationUtils.throwPackageUnavailable(packageName);
                 }
 
-                pkgState.getOrCreateUserSelectionState(userId)
+                pkgState.getOrCreateUserState(userId)
                         .setLinkHandlingAllowed(allowed);
             }
         }
@@ -476,14 +468,6 @@
         mConnection.scheduleWriteSettings();
     }
 
-    @Override
-    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
-            @NonNull Set<String> domains, boolean enabled)
-            throws InvalidDomainSetException, NameNotFoundException {
-        setDomainVerificationUserSelection(domainSetId, domains, enabled,
-                mConnection.getCallingUserId());
-    }
-
     public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
             @NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
             throws InvalidDomainSetException, NameNotFoundException {
@@ -500,7 +484,8 @@
 
             DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
                     false /* forAutoVerify */, callingUid, userId);
-            DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
+            DomainVerificationInternalUserState userState =
+                    pkgState.getOrCreateUserState(userId);
 
             // Disable other packages if approving this one. Note that this check is only done for
             // enabling. This allows an escape hatch in case multiple packages somehow get selected.
@@ -540,8 +525,8 @@
                             continue;
                         }
 
-                        DomainVerificationUserState approvedUserState =
-                                approvedPkgState.getUserSelectionState(userId);
+                        DomainVerificationInternalUserState approvedUserState =
+                                approvedPkgState.getUserState(userId);
                         if (approvedUserState == null) {
                             continue;
                         }
@@ -623,8 +608,8 @@
 
         if (userId == UserHandle.USER_ALL) {
             for (int aUserId : mConnection.getAllUserIds()) {
-                DomainVerificationUserState userState =
-                        pkgState.getOrCreateUserSelectionState(aUserId);
+                DomainVerificationInternalUserState userState =
+                        pkgState.getOrCreateUserState(aUserId);
                 if (enabled) {
                     userState.addHosts(domains);
                 } else {
@@ -632,7 +617,8 @@
                 }
             }
         } else {
-            DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
+            DomainVerificationInternalUserState userState =
+                    pkgState.getOrCreateUserState(userId);
             if (enabled) {
                 userState.addHosts(domains);
             } else {
@@ -643,17 +629,9 @@
 
     @Nullable
     @Override
-    public DomainVerificationUserSelection getDomainVerificationUserSelection(
-            @NonNull String packageName) throws NameNotFoundException {
-        return getDomainVerificationUserSelection(packageName,
-                mConnection.getCallingUserId());
-    }
-
-    @Nullable
-    @Override
-    public DomainVerificationUserSelection getDomainVerificationUserSelection(
+    public DomainVerificationUserState getDomainVerificationUserState(
             @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException {
-        if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+        if (!mEnforcer.assertApprovedUserStateQuerent(mConnection.getCallingUid(),
                 mConnection.getCallingUserId(), packageName, userId)) {
             throw DomainVerificationUtils.throwPackageUnavailable(packageName);
         }
@@ -673,7 +651,7 @@
 
             Map<String, Integer> domains = new ArrayMap<>(webDomainsSize);
             ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
-            DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);
+            DomainVerificationInternalUserState userState = pkgState.getUserState(userId);
             Set<String> enabledHosts = userState == null ? emptySet() : userState.getEnabledHosts();
 
             for (int index = 0; index < webDomainsSize; index++) {
@@ -682,11 +660,11 @@
 
                 int domainState;
                 if (state != null && DomainVerificationManager.isStateVerified(state)) {
-                    domainState = DomainVerificationUserSelection.DOMAIN_STATE_VERIFIED;
+                    domainState = DomainVerificationUserState.DOMAIN_STATE_VERIFIED;
                 } else if (enabledHosts.contains(host)) {
-                    domainState = DomainVerificationUserSelection.DOMAIN_STATE_SELECTED;
+                    domainState = DomainVerificationUserState.DOMAIN_STATE_SELECTED;
                 } else {
-                    domainState = DomainVerificationUserSelection.DOMAIN_STATE_NONE;
+                    domainState = DomainVerificationUserState.DOMAIN_STATE_NONE;
                 }
 
                 domains.put(host, domainState);
@@ -694,17 +672,11 @@
 
             boolean linkHandlingAllowed = userState == null || userState.isLinkHandlingAllowed();
 
-            return new DomainVerificationUserSelection(pkgState.getId(), packageName,
+            return new DomainVerificationUserState(pkgState.getId(), packageName,
                     UserHandle.of(userId), linkHandlingAllowed, domains);
         }
     }
 
-    @NonNull
-    @Override
-    public List<DomainOwner> getOwnersForDomain(@NonNull String domain) {
-        return getOwnersForDomain(domain, mConnection.getCallingUserId());
-    }
-
     public List<DomainOwner> getOwnersForDomain(@NonNull String domain, @UserIdInt int userId) {
         mEnforcer.assertOwnerQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(),
                 userId);
@@ -795,7 +767,7 @@
             AndroidPackage newPkg = newPkgSetting.getPkg();
 
             ArrayMap<String, Integer> newStateMap = new ArrayMap<>();
-            SparseArray<DomainVerificationUserState> newUserStates = new SparseArray<>();
+            SparseArray<DomainVerificationInternalUserState> newUserStates = new SparseArray<>();
 
             if (oldPkgState == null || oldPkg == null || newPkg == null) {
                 // Should be impossible, but to be safe, continue with a new blank state instead
@@ -838,21 +810,22 @@
                 }
             }
 
-            SparseArray<DomainVerificationUserState> oldUserStates =
-                    oldPkgState.getUserSelectionStates();
+            SparseArray<DomainVerificationInternalUserState> oldUserStates =
+                    oldPkgState.getUserStates();
             int oldUserStatesSize = oldUserStates.size();
             if (oldUserStatesSize > 0) {
                 ArraySet<String> newWebDomains = mCollector.collectValidAutoVerifyDomains(newPkg);
                 for (int oldUserStatesIndex = 0; oldUserStatesIndex < oldUserStatesSize;
                         oldUserStatesIndex++) {
                     int userId = oldUserStates.keyAt(oldUserStatesIndex);
-                    DomainVerificationUserState oldUserState = oldUserStates.valueAt(
+                    DomainVerificationInternalUserState oldUserState = oldUserStates.valueAt(
                             oldUserStatesIndex);
                     ArraySet<String> oldEnabledHosts = oldUserState.getEnabledHosts();
                     ArraySet<String> newEnabledHosts = new ArraySet<>(oldEnabledHosts);
                     newEnabledHosts.retainAll(newWebDomains);
-                    DomainVerificationUserState newUserState = new DomainVerificationUserState(
-                            userId, newEnabledHosts, oldUserState.isLinkHandlingAllowed());
+                    DomainVerificationInternalUserState newUserState =
+                            new DomainVerificationInternalUserState(userId, newEnabledHosts,
+                                    oldUserState.isLinkHandlingAllowed());
                     newUserStates.put(userId, newUserState);
                 }
             }
@@ -926,7 +899,7 @@
                         webDomains = mCollector.collectAllWebDomains(pkg);
                     }
 
-                    pkgState.getOrCreateUserSelectionState(userId).addHosts(webDomains);
+                    pkgState.getOrCreateUserState(userId).addHosts(webDomains);
                 }
             }
 
@@ -1295,7 +1268,7 @@
     }
 
     @Override
-    public void clearUserSelections(@Nullable List<String> packageNames, @UserIdInt int userId) {
+    public void clearUserStates(@Nullable List<String> packageNames, @UserIdInt int userId) {
         mEnforcer.assertInternal(mConnection.getCallingUid());
         synchronized (mLock) {
             if (packageNames == null) {
@@ -1545,7 +1518,7 @@
                 return APPROVAL_LEVEL_NONE;
             }
 
-            DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);
+            DomainVerificationInternalUserState userState = pkgState.getUserState(userId);
 
             if (userState != null && !userState.isLinkHandlingAllowed()) {
                 if (DEBUG_APPROVAL) {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
index a8e937c..f3d1dbb 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
@@ -29,9 +29,9 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
 import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
 import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
-import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -216,21 +216,22 @@
             }
         }
 
-        SparseArray<DomainVerificationUserState> oldSelectionStates =
-                oldState.getUserSelectionStates();
+        SparseArray<DomainVerificationInternalUserState> oldSelectionStates =
+                oldState.getUserStates();
 
-        SparseArray<DomainVerificationUserState> newSelectionStates =
-                newState.getUserSelectionStates();
+        SparseArray<DomainVerificationInternalUserState> newSelectionStates =
+                newState.getUserStates();
 
-        DomainVerificationUserState newUserState = newSelectionStates.get(UserHandle.USER_SYSTEM);
+        DomainVerificationInternalUserState newUserState =
+                newSelectionStates.get(UserHandle.USER_SYSTEM);
         if (newUserState != null) {
             ArraySet<String> newEnabledHosts = newUserState.getEnabledHosts();
-            DomainVerificationUserState oldUserState =
+            DomainVerificationInternalUserState oldUserState =
                     oldSelectionStates.get(UserHandle.USER_SYSTEM);
 
             boolean linkHandlingAllowed = newUserState.isLinkHandlingAllowed();
             if (oldUserState == null) {
-                oldUserState = new DomainVerificationUserState(UserHandle.USER_SYSTEM,
+                oldUserState = new DomainVerificationInternalUserState(UserHandle.USER_SYSTEM,
                         newEnabledHosts, linkHandlingAllowed);
                 oldSelectionStates.put(UserHandle.USER_SYSTEM, oldUserState);
             } else {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
index d083d11..94767f5 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
@@ -24,7 +24,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.verify.domain.DomainVerificationManager;
 import android.content.pm.verify.domain.DomainVerificationState;
-import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.DomainVerificationUserState;
 import android.os.Binder;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -118,7 +118,7 @@
             case "set-app-links":
                 return runSetAppLinks(commandHandler);
             case "set-app-links-user-selection":
-                return runSetAppLinksUserSelection(commandHandler);
+                return runSetAppLinksUserState(commandHandler);
             case "set-app-links-allowed":
                 return runSetAppLinksAllowed(commandHandler);
         }
@@ -193,7 +193,7 @@
     }
 
     // pm set-app-links-user-selection --user <USER_ID> [--package <PACKAGE>] <ENABLED> <DOMAINS>...
-    private boolean runSetAppLinksUserSelection(@NonNull BasicShellCommandHandler commandHandler) {
+    private boolean runSetAppLinksUserState(@NonNull BasicShellCommandHandler commandHandler) {
         Integer userId = null;
         String packageName = null;
 
@@ -224,7 +224,7 @@
             return false;
         }
 
-        userId = translateUserId(userId, "runSetAppLinksUserSelection");
+        userId = translateUserId(userId, "runSetAppLinksUserState");
 
         String enabledString = commandHandler.getNextArgRequired();
 
@@ -326,7 +326,7 @@
         }
 
         if (userId != null) {
-            mCallback.clearUserSelections(packageNames, userId);
+            mCallback.clearUserStates(packageNames, userId);
         } else {
             mCallback.clearDomainVerificationState(packageNames);
         }
@@ -457,10 +457,10 @@
                 throws PackageManager.NameNotFoundException;
 
         /**
-         * @see DomainVerificationManager#getDomainVerificationUserSelection(String)
+         * @see DomainVerificationManager#getDomainVerificationUserState(String)
          */
         @Nullable
-        DomainVerificationUserSelection getDomainVerificationUserSelection(
+        DomainVerificationUserState getDomainVerificationUserState(
                 @NonNull String packageName, @UserIdInt int userId)
                 throws PackageManager.NameNotFoundException;
 
@@ -486,7 +486,7 @@
          * Reset all the user selections for the given package names, or all package names if null
          * is provided.
          */
-        void clearUserSelections(@Nullable List<String> packageNames, @UserIdInt int userId);
+        void clearUserStates(@Nullable List<String> packageNames, @UserIdInt int userId);
 
         /**
          * Broadcast a verification request for the given package names, or all package names if
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationInternalUserState.java
similarity index 74%
rename from services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
rename to services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationInternalUserState.java
index 8fbb33a..aa7407c 100644
--- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationInternalUserState.java
@@ -29,7 +29,7 @@
  * when a web URL Intent is sent and the application is the highest priority for that domain.
  */
 @DataClass(genSetters = true, genEqualsHashCode = true, genToString = true, genBuilder = false)
-public class DomainVerificationUserState {
+public class DomainVerificationInternalUserState {
 
     @UserIdInt
     private final int mUserId;
@@ -43,32 +43,32 @@
      */
     private boolean mLinkHandlingAllowed = true;
 
-    public DomainVerificationUserState(@UserIdInt int userId) {
+    public DomainVerificationInternalUserState(@UserIdInt int userId) {
         mUserId = userId;
         mEnabledHosts = new ArraySet<>();
     }
 
-    public DomainVerificationUserState addHosts(@NonNull ArraySet<String> newHosts) {
+    public DomainVerificationInternalUserState addHosts(@NonNull ArraySet<String> newHosts) {
         mEnabledHosts.addAll(newHosts);
         return this;
     }
 
-    public DomainVerificationUserState addHosts(@NonNull Set<String> newHosts) {
+    public DomainVerificationInternalUserState addHosts(@NonNull Set<String> newHosts) {
         mEnabledHosts.addAll(newHosts);
         return this;
     }
 
-    public DomainVerificationUserState removeHost(String host) {
+    public DomainVerificationInternalUserState removeHost(String host) {
         mEnabledHosts.remove(host);
         return this;
     }
 
-    public DomainVerificationUserState removeHosts(@NonNull ArraySet<String> newHosts) {
+    public DomainVerificationInternalUserState removeHosts(@NonNull ArraySet<String> newHosts) {
         mEnabledHosts.removeAll(newHosts);
         return this;
     }
 
-    public DomainVerificationUserState removeHosts(@NonNull Set<String> newHosts) {
+    public DomainVerificationInternalUserState removeHosts(@NonNull Set<String> newHosts) {
         mEnabledHosts.removeAll(newHosts);
         return this;
     }
@@ -81,8 +81,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm
-    // /verify/domain/models/DomainVerificationUserState.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationInternalUserState.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -90,7 +89,7 @@
 
 
     /**
-     * Creates a new DomainVerificationUserState.
+     * Creates a new DomainVerificationInternalUserState.
      *
      * @param enabledHosts
      *   List of domains which have been enabled by the user. *
@@ -98,7 +97,7 @@
      *   Whether to allow this package to automatically open links by auto verification.
      */
     @DataClass.Generated.Member
-    public DomainVerificationUserState(
+    public DomainVerificationInternalUserState(
             @UserIdInt int userId,
             @NonNull ArraySet<String> enabledHosts,
             boolean linkHandlingAllowed) {
@@ -138,7 +137,7 @@
      * Whether to allow this package to automatically open links by auto verification.
      */
     @DataClass.Generated.Member
-    public @NonNull DomainVerificationUserState setLinkHandlingAllowed( boolean value) {
+    public @NonNull DomainVerificationInternalUserState setLinkHandlingAllowed( boolean value) {
         mLinkHandlingAllowed = value;
         return this;
     }
@@ -149,7 +148,7 @@
         // You can override field toString logic by defining methods like:
         // String fieldNameToString() { ... }
 
-        return "DomainVerificationUserState { " +
+        return "DomainVerificationInternalUserState { " +
                 "userId = " + mUserId + ", " +
                 "enabledHosts = " + mEnabledHosts + ", " +
                 "linkHandlingAllowed = " + mLinkHandlingAllowed +
@@ -160,13 +159,13 @@
     @DataClass.Generated.Member
     public boolean equals(@android.annotation.Nullable Object o) {
         // You can override field equality logic by defining either of the methods like:
-        // boolean fieldNameEquals(DomainVerificationUserState other) { ... }
+        // boolean fieldNameEquals(DomainVerificationInternalUserState other) { ... }
         // boolean fieldNameEquals(FieldType otherValue) { ... }
 
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         @SuppressWarnings("unchecked")
-        DomainVerificationUserState that = (DomainVerificationUserState) o;
+        DomainVerificationInternalUserState that = (DomainVerificationInternalUserState) o;
         //noinspection PointlessBooleanExpression
         return true
                 && mUserId == that.mUserId
@@ -188,10 +187,10 @@
     }
 
     @DataClass.Generated(
-            time = 1612894390039L,
+            time = 1614714563905L,
             codegenVersion = "1.0.22",
-            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java",
-            inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledHosts\nprivate  boolean mLinkHandlingAllowed\npublic  com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(java.util.Set<java.lang.String>)\nclass DomainVerificationUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true, genBuilder=false)")
+            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationInternalUserState.java",
+            inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledHosts\nprivate  boolean mLinkHandlingAllowed\npublic  com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState addHosts(android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState addHosts(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState removeHost(java.lang.String)\npublic  com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState removeHosts(android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState removeHosts(java.util.Set<java.lang.String>)\nclass DomainVerificationInternalUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
index 48099aa..a089a60 100644
--- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.content.pm.verify.domain.DomainVerificationManager;
 import android.content.pm.verify.domain.DomainVerificationState;
 import android.util.ArrayMap;
 import android.util.SparseArray;
@@ -44,9 +43,9 @@
 
     /**
      * Whether or not the package declares any autoVerify domains. This is separate from an empty
-     * check on the map itself, because an empty map means no response recorded, not necessarily no
-     * domains declared. When this is false, {@link #mStateMap} will be empty, but
-     * {@link #mUserSelectionStates} may contain any domains the user has explicitly chosen to
+     * check on the map itself, because an empty map means no response recorded, not necessarily
+     * no domains declared. When this is false, {@link #mStateMap} will be empty, but
+     * {@link #mUserStates} may contain any domains the user has explicitly chosen to
      * allow this package to open, which may or may not be marked autoVerify.
      */
     private final boolean mHasAutoVerifyDomains;
@@ -62,7 +61,7 @@
     private final ArrayMap<String, Integer> mStateMap;
 
     @NonNull
-    private final SparseArray<DomainVerificationUserState> mUserSelectionStates;
+    private final SparseArray<DomainVerificationInternalUserState> mUserStates;
 
     public DomainVerificationPkgState(@NonNull String packageName, @NonNull UUID id,
             boolean hasAutoVerifyDomains) {
@@ -70,16 +69,17 @@
     }
 
     @Nullable
-    public DomainVerificationUserState getUserSelectionState(@UserIdInt int userId) {
-        return mUserSelectionStates.get(userId);
+    public DomainVerificationInternalUserState getUserState(@UserIdInt int userId) {
+        return mUserStates.get(userId);
     }
 
     @Nullable
-    public DomainVerificationUserState getOrCreateUserSelectionState(@UserIdInt int userId) {
-        DomainVerificationUserState userState = mUserSelectionStates.get(userId);
+    public DomainVerificationInternalUserState getOrCreateUserState(
+            @UserIdInt int userId) {
+        DomainVerificationInternalUserState userState = mUserStates.get(userId);
         if (userState == null) {
-            userState = new DomainVerificationUserState(userId);
-            mUserSelectionStates.put(userId, userState);
+            userState = new DomainVerificationInternalUserState(userId);
+            mUserStates.put(userId, userState);
         }
         return userState;
     }
@@ -89,20 +89,20 @@
     }
 
     public void removeUser(@UserIdInt int userId) {
-        mUserSelectionStates.remove(userId);
+        mUserStates.remove(userId);
     }
 
     public void removeAllUsers() {
-        mUserSelectionStates.clear();
+        mUserStates.clear();
     }
 
-    private int userSelectionStatesHashCode() {
-        return mUserSelectionStates.contentHashCode();
+    private int userStatesHashCode() {
+        return mUserStates.contentHashCode();
     }
 
-    private boolean userSelectionStatesEquals(
-            @NonNull SparseArray<DomainVerificationUserState> other) {
-        return mUserSelectionStates.contentEquals(other);
+    private boolean userStatesEquals(
+            @NonNull SparseArray<DomainVerificationInternalUserState> other) {
+        return mUserStates.contentEquals(other);
     }
 
 
@@ -113,7 +113,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -123,9 +123,15 @@
     /**
      * Creates a new DomainVerificationPkgState.
      *
+     * @param hasAutoVerifyDomains
+     *   Whether or not the package declares any autoVerify domains. This is separate from an empty
+     *   check on the map itself, because an empty map means no response recorded, not necessarily
+     *   no domains declared. When this is false, {@link #mStateMap} will be empty, but
+     *   {@link #mUserStates} may contain any domains the user has explicitly chosen to
+     *   allow this package to open, which may or may not be marked autoVerify.
      * @param stateMap
      *   Map of domains to state integers. Only domains that are not set to the default value of
-     *   {@link DomainVerificationManager#STATE_NO_RESPONSE} are included.
+     *   {@link DomainVerificationState#STATE_NO_RESPONSE} are included.
      *
      *   TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
      *    such as storing no state when the package is marked as a linked app in SystemConfig.
@@ -136,7 +142,7 @@
             @NonNull UUID id,
             boolean hasAutoVerifyDomains,
             @NonNull ArrayMap<String,Integer> stateMap,
-            @NonNull SparseArray<DomainVerificationUserState> userSelectionStates) {
+            @NonNull SparseArray<DomainVerificationInternalUserState> userStates) {
         this.mPackageName = packageName;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPackageName);
@@ -147,9 +153,9 @@
         this.mStateMap = stateMap;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mStateMap);
-        this.mUserSelectionStates = userSelectionStates;
+        this.mUserStates = userStates;
         com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mUserSelectionStates);
+                NonNull.class, null, mUserStates);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -164,6 +170,13 @@
         return mId;
     }
 
+    /**
+     * Whether or not the package declares any autoVerify domains. This is separate from an empty
+     * check on the map itself, because an empty map means no response recorded, not necessarily
+     * no domains declared. When this is false, {@link #mStateMap} will be empty, but
+     * {@link #mUserStates} may contain any domains the user has explicitly chosen to
+     * allow this package to open, which may or may not be marked autoVerify.
+     */
     @DataClass.Generated.Member
     public boolean isHasAutoVerifyDomains() {
         return mHasAutoVerifyDomains;
@@ -171,7 +184,7 @@
 
     /**
      * Map of domains to state integers. Only domains that are not set to the default value of
-     * {@link DomainVerificationManager#STATE_NO_RESPONSE} are included.
+     * {@link DomainVerificationState#STATE_NO_RESPONSE} are included.
      *
      * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
      *  such as storing no state when the package is marked as a linked app in SystemConfig.
@@ -182,8 +195,8 @@
     }
 
     @DataClass.Generated.Member
-    public @NonNull SparseArray<DomainVerificationUserState> getUserSelectionStates() {
-        return mUserSelectionStates;
+    public @NonNull SparseArray<DomainVerificationInternalUserState> getUserStates() {
+        return mUserStates;
     }
 
     @Override
@@ -197,7 +210,7 @@
                 "id = " + mId + ", " +
                 "hasAutoVerifyDomains = " + mHasAutoVerifyDomains + ", " +
                 "stateMap = " + mStateMap + ", " +
-                "userSelectionStates = " + mUserSelectionStates +
+                "userStates = " + mUserStates +
         " }";
     }
 
@@ -218,7 +231,7 @@
                 && Objects.equals(mId, that.mId)
                 && mHasAutoVerifyDomains == that.mHasAutoVerifyDomains
                 && Objects.equals(mStateMap, that.mStateMap)
-                && userSelectionStatesEquals(that.mUserSelectionStates);
+                && userStatesEquals(that.mUserStates);
     }
 
     @Override
@@ -232,15 +245,15 @@
         _hash = 31 * _hash + Objects.hashCode(mId);
         _hash = 31 * _hash + Boolean.hashCode(mHasAutoVerifyDomains);
         _hash = 31 * _hash + Objects.hashCode(mStateMap);
-        _hash = 31 * _hash + userSelectionStatesHashCode();
+        _hash = 31 * _hash + userStatesHashCode();
         return _hash;
     }
 
     @DataClass.Generated(
-            time = 1608234185474L,
+            time = 1614818362549L,
             codegenVersion = "1.0.22",
-            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java",
-            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final  boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.Integer> mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationUserState> mUserSelectionStates\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationUserState getUserSelectionState(int)\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationUserState getOrCreateUserSelectionState(int)\npublic  void setId(java.util.UUID)\npublic  void removeUser(int)\npublic  void removeAllUsers()\nprivate  int userSelectionStatesHashCode()\nprivate  boolean userSelectionStatesEquals(android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationUserState>)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java",
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final  boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.Integer> mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState> mUserStates\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getUserState(int)\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getOrCreateUserState(int)\npublic  void setId(java.util.UUID)\npublic  void removeUser(int)\npublic  void removeAllUsers()\nprivate  int userStatesHashCode()\nprivate  boolean userStatesEquals(android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState>)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
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 bce218f..047e3b3 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;
@@ -430,7 +432,6 @@
     volatile boolean mPowerKeyHandled;
     volatile boolean mBackKeyHandled;
     volatile boolean mBeganFromNonInteractive;
-    volatile int mPowerKeyPressCounter;
     volatile boolean mEndCallKeyHandled;
     volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
     volatile boolean mGoingToSleep;
@@ -471,7 +472,6 @@
     boolean mHasSoftInput = false;
     boolean mHapticTextHandleEnabled;
     boolean mUseTvRouting;
-    int mVeryLongPressTimeout;
     boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
     MetricsLogger mLogger;
     boolean mWakeOnDpadKeyPress;
@@ -567,14 +567,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;
@@ -585,10 +584,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;
@@ -596,8 +592,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
@@ -638,22 +633,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;
@@ -764,13 +746,6 @@
         }
     };
 
-    private Runnable mPossibleVeryLongPressReboot = new Runnable() {
-        @Override
-        public void run() {
-            mActivityManagerInternal.prepareForPossibleShutdown();
-        }
-    };
-
     private void handleRingerChordGesture() {
         if (mRingerToggleChord == VOLUME_HUSH_OFF) {
             return;
@@ -810,28 +785,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();
 
@@ -853,10 +813,9 @@
             }
         }
 
-        if (mAutofillManagerInternal != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+        if (mAutofillManagerInternal != null) {
             mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_BACK_KEY_TO_AUTOFILL));
         }
-
         return handled;
     }
 
@@ -866,11 +825,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.
@@ -892,71 +846,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 {
@@ -964,68 +867,37 @@
                     }
                 }
             }
+        } 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 "
@@ -1176,6 +1048,7 @@
 
     private void powerLongPress(long eventTime) {
         final int behavior = getResolvedLongPressOnPowerBehavior();
+
         switch (behavior) {
             case LONG_PRESS_POWER_NOTHING:
                 break;
@@ -1183,7 +1056,7 @@
                 mPowerKeyHandled = true;
                 performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                         "Power - Long Press - Global Actions");
-                showGlobalActionsInternal();
+                showGlobalActions();
                 break;
             case LONG_PRESS_POWER_SHUT_OFF:
             case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
@@ -1214,14 +1087,14 @@
 
     private void powerVeryLongPress() {
         switch (mVeryLongPressOnPowerBehavior) {
-        case VERY_LONG_PRESS_POWER_NOTHING:
-            break;
-        case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
-            mPowerKeyHandled = true;
-            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
-                    "Power - Very Long Press - Show Global Actions");
-            showGlobalActionsInternal();
-            break;
+            case VERY_LONG_PRESS_POWER_NOTHING:
+                break;
+            case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
+                mPowerKeyHandled = true;
+                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+                        "Power - Very Long Press - Show Global Actions");
+                showGlobalActions();
+                break;
         }
     }
 
@@ -1814,8 +1687,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);
 
@@ -1909,6 +1780,7 @@
                     }
                 });
         initKeyCombinationRules();
+        initSingleKeyGestureRules();
     }
 
     private void initKeyCombinationRules() {
@@ -1921,7 +1793,7 @@
                     new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
                         @Override
                         void execute() {
-                            cancelPendingPowerKeyAction();
+                            mPowerKeyHandled = true;
                             interceptScreenshotChord();
                         }
                         @Override
@@ -1956,7 +1828,7 @@
                     }
                     @Override
                     void execute() {
-                        cancelPendingPowerKeyAction();
+                        mPowerKeyHandled = true;
                         interceptRingerToggleChord();
                     }
                     @Override
@@ -1970,7 +1842,7 @@
                     new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) {
                         @Override
                         void execute() {
-                            cancelPendingBackKeyAction();
+                            mBackKeyHandled = true;
                             interceptAccessibilityGestureTv();
                         }
 
@@ -1984,7 +1856,7 @@
                     new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) {
                         @Override
                         void execute() {
-                            cancelPendingBackKeyAction();
+                            mBackKeyHandled = true;
                             interceptBugreportGestureTv();
                         }
 
@@ -1997,6 +1869,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 eventTime) {
+            powerLongPress(eventTime);
+        }
+
+        @Override
+        void onVeryLongPress(long eventTime) {
+            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.
@@ -3427,8 +3377,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
@@ -3443,12 +3406,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;
                     }
                 }
@@ -3560,33 +3524,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
@@ -3767,6 +3715,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
@@ -4766,15 +4751,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..cae2093
--- /dev/null
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -0,0 +1,362 @@
+/*
+ * 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 eventTime) { // long press behavior. }
+     *           void onVeryLongPress(long eventTime) { // 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 eventTime) {}
+        /**
+         *  Callback when very long press has been detected.
+         */
+        void onVeryLongPress(long eventTime) {}
+
+        @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
+                    && mActiveRule.supportLongPress() && !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)) {
+                    if (DEBUG) {
+                        Log.i(TAG, "Intercept key by rule " + rule);
+                    }
+                    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;
+            mKeyPressCounter = 0;
+            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) {
+                if (DEBUG) {
+                    Log.i(TAG, "press key " + KeyEvent.keyCodeToString(event.getKeyCode()));
+                }
+                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 bc11709..29adde3 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -5073,7 +5073,7 @@
         }
 
         @Override // Binder call
-        public void userActivity(long eventTime, int event, int flags) {
+        public void userActivity(int displayId, long eventTime, int event, int flags) {
             final long now = mClock.uptimeMillis();
             if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
                     != PackageManager.PERMISSION_GRANTED
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 6aa7c8a..7ed7a59 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -3759,13 +3759,15 @@
             ConnectivityManager.NetworkCallback {
         @Override
         public void onAvailable(Network network) {
-            FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+            FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED,
+                    network.getNetId(),
                     FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED__STATE__CONNECTED);
         }
 
         @Override
         public void onLost(Network network) {
-            FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+            FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED,
+                    network.getNetId(),
                     FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED__STATE__DISCONNECTED);
         }
     }
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index d2b05c0..a0e2286 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -42,8 +42,8 @@
 import android.os.storage.StorageVolume;
 import android.service.storage.ExternalStorageService;
 import android.service.storage.IExternalStorageService;
-import android.util.ArraySet;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -53,11 +53,13 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 
 /**
  * Controls the lifecycle of the {@link ActiveConnection} to an {@link ExternalStorageService}
@@ -72,15 +74,17 @@
     private final Context mContext;
     private final int mUserId;
     private final StorageSessionController mSessionController;
+    private final StorageManagerInternal mSmInternal;
     private final ActiveConnection mActiveConnection = new ActiveConnection();
-    @GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
-    @GuardedBy("mLock") private final Set<Integer> mUidsBlockedOnIo = new ArraySet<>();
+    @GuardedBy("mSessionsLock") private final Map<String, Session> mSessions = new HashMap<>();
+    @GuardedBy("mSessionsLock") private final SparseArray<Integer> mUidsBlockedOnIo = new SparseArray<>();
     private final HandlerThread mHandlerThread;
 
     public StorageUserConnection(Context context, int userId, StorageSessionController controller) {
         mContext = Objects.requireNonNull(context);
         mUserId = Preconditions.checkArgumentNonnegative(userId);
         mSessionController = controller;
+        mSmInternal = LocalServices.getService(StorageManagerInternal.class);
         mHandlerThread = new HandlerThread("StorageUserConnectionThread-" + mUserId);
         mHandlerThread.start();
     }
@@ -152,9 +156,13 @@
      */
     public void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason)
             throws ExternalStorageServiceException {
+        List<String> primarySessionIds = mSmInternal.getPrimaryVolumeIds();
         synchronized (mSessionsLock) {
             for (String sessionId : mSessions.keySet()) {
-                mActiveConnection.notifyAnrDelayStarted(packageName, uid, tid, reason);
+                if (primarySessionIds.contains(sessionId)) {
+                    mActiveConnection.notifyAnrDelayStarted(packageName, uid, tid, reason);
+                    return;
+                }
             }
         }
     }
@@ -201,8 +209,7 @@
                 return;
             }
         }
-        StorageManagerInternal sm = LocalServices.getService(StorageManagerInternal.class);
-        sm.resetUser(mUserId);
+        mSmInternal.resetUser(mUserId);
     }
 
     /**
@@ -242,7 +249,8 @@
     public void notifyAppIoBlocked(String volumeUuid, int uid, int tid,
             @StorageManager.AppIoBlockedReason int reason) {
         synchronized (mSessionsLock) {
-            mUidsBlockedOnIo.add(uid);
+            int ioBlockedCounter = mUidsBlockedOnIo.get(uid, 0);
+            mUidsBlockedOnIo.put(uid, ++ioBlockedCounter);
         }
     }
 
@@ -255,7 +263,12 @@
     public void notifyAppIoResumed(String volumeUuid, int uid, int tid,
             @StorageManager.AppIoBlockedReason int reason) {
         synchronized (mSessionsLock) {
-            mUidsBlockedOnIo.remove(uid);
+            int ioBlockedCounter = mUidsBlockedOnIo.get(uid, 0);
+            if (ioBlockedCounter == 0) {
+                mUidsBlockedOnIo.remove(uid);
+            } else {
+                mUidsBlockedOnIo.put(uid, --ioBlockedCounter);
+            }
         }
     }
 
@@ -317,6 +330,23 @@
             }
         }
 
+        private void asyncBestEffort(Consumer<IExternalStorageService> consumer) {
+            synchronized (mLock) {
+                if (mRemoteFuture == null) {
+                    Slog.w(TAG, "Dropping async request service is not bound");
+                    return;
+                }
+
+                IExternalStorageService service = mRemoteFuture.getNow(null);
+                if (service == null) {
+                    Slog.w(TAG, "Dropping async request service is not connected");
+                    return;
+                }
+
+                consumer.accept(service);
+            }
+        }
+
         private void waitForAsyncVoid(AsyncStorageServiceCall asyncCall) throws Exception {
             CompletableFuture<Void> opFuture = new CompletableFuture<>();
             RemoteCallback callback = new RemoteCallback(result -> setResult(result, opFuture));
@@ -401,13 +431,13 @@
 
         public void notifyAnrDelayStarted(String packgeName, int uid, int tid, int reason)
                 throws ExternalStorageServiceException {
-            try {
-                waitForAsyncVoid((service, callback) ->
-                        service.notifyAnrDelayStarted(packgeName, uid, tid, reason, callback));
-            } catch (Exception e) {
-                throw new ExternalStorageServiceException("Failed to notify ANR delay started: "
-                        + packgeName, e);
-            }
+            asyncBestEffort(service -> {
+                try {
+                    service.notifyAnrDelayStarted(packgeName, uid, tid, reason);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to notify ANR delay started", e);
+                }
+            });
         }
 
         private void setResult(Bundle result, CompletableFuture<Void> future) {
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
index 5772dea..e54b40e 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
@@ -16,6 +16,8 @@
 
 package com.android.server.uri;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Intent;
 import android.content.pm.ProviderInfo;
 import android.net.Uri;
@@ -58,6 +60,19 @@
     void grantUriPermissionUncheckedFromIntent(
             NeededUriGrants needed, UriPermissionOwner owner);
 
+    /**
+     * Creates a new stateful object to track uri permission grants. This is needed to maintain
+     * state when managing grants via {@link UriGrantsManagerService#grantUriPermissionFromOwner},
+     * {@link #revokeUriPermissionFromOwner}, etc.
+     *
+     * @param name A name for the object. This is only used for logcat/dumpsys logging, so there
+     *             are no uniqueness or other requirements, but it is recommended to make the
+     *             name sufficiently readable so that the relevant code area can be determined
+     *             easily when this name shows up in a bug report.
+     * @return An opaque owner token for tracking uri permission grants.
+     * @see UriPermissionOwner
+     * @see UriGrantsManagerService
+     */
     IBinder newUriPermissionOwner(String name);
 
     /**
@@ -74,33 +89,39 @@
      */
     void removeUriPermissionsForPackage(
             String packageName, int userHandle, boolean persistable, boolean targetOnly);
+
     /**
-     * Remove any {@link UriPermission} associated with the owner whose values match the given
-     * filtering parameters.
-     *
-     * @param token An opaque owner token as returned by {@link #newUriPermissionOwner(String)}.
-     * @param uri This uri must NOT contain an embedded userId. {@code null} to apply to all Uris.
-     * @param mode The modes (as a bitmask) to revoke.
-     * @param userId The userId in which the uri is to be resolved.
+     * Like {@link #revokeUriPermissionFromOwner(IBinder, Uri, int, int, String, int)} but applies
+     * to all target packages and all target users.
      */
-    void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId);
+    void revokeUriPermissionFromOwner(@NonNull IBinder token, @Nullable Uri uri, int mode,
+            int userId);
 
     /**
      * Remove any {@link UriPermission} associated with the owner whose values match the given
      * filtering parameters.
      *
      * @param token An opaque owner token as returned by {@link #newUriPermissionOwner(String)}.
-     * @param uri This uri must NOT contain an embedded userId. {@code null} to apply to all Uris.
-     * @param mode The modes (as a bitmask) to revoke.
-     * @param userId The userId in which the uri is to be resolved.
-     * @param targetPkg Calling package name to match, or {@code null} to apply to all packages.
-     * @param targetUserId Calling user to match, or {@link UserHandle#USER_ALL} to apply to all
-     *                     users.
+     * @param uri The content uri for which the permission grant should be revoked. This uri
+     *            must NOT contain an embedded userId; use
+     *            {@link android.content.ContentProvider#getUriWithoutUserId(Uri)} if needed.
+     *            This param may be {@code null} to revoke grants for all uris tracked by the
+     *            provided owner token.
+     * @param mode The modes (as a bitmask) to revoke. See
+     *             {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc.
+     * @param userId The userId in which the given uri is to be resolved. If the {@code uri}
+     *               param is {@code null}, this param is ignored since permissions for all
+     *               uris will be revoked.
+     * @param targetPkg Target package name to match (app that received the grant), or
+     *                  {@code null} to apply to all packages.
+     * @param targetUserId Target user to match (userId of the app that received the grant), or
+     *                     {@link UserHandle#USER_ALL} to apply to all users.
      */
-    void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId,
-            String targetPkg, int targetUserId);
+    void revokeUriPermissionFromOwner(@NonNull IBinder token, @Nullable Uri uri, int mode,
+            int userId, @Nullable String targetPkg, int targetUserId);
 
     boolean checkAuthorityGrants(
             int callingUid, ProviderInfo cpi, int userId, boolean checkUser);
+
     void dump(PrintWriter pw, boolean dumpAll, String dumpPackage);
 }
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index dcc1599..44545ed 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -33,17 +33,13 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.myUid;
 
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readLongAttribute;
 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.server.uri.UriGrantsManagerService.H.PERSIST_URI_GRANTS_MSG;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -82,7 +78,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
@@ -94,9 +89,7 @@
 
 import libcore.io.IoUtils;
 
-import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -104,7 +97,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -211,6 +203,21 @@
         }
     }
 
+    /**
+     * Grant uri permissions to the specified app.
+     *
+     * @param token An opaque owner token for tracking the permissions. See
+     *              {@link UriGrantsManagerInternal#newUriPermissionOwner}.
+     * @param fromUid The uid of the grantor app that has permissions to the uri. Permissions
+     *                will be granted on behalf of this app.
+     * @param targetPkg The package name of the grantor app that has permissions to the uri.
+     *                  Permissions will be granted on behalf of this app.
+     * @param uri The uri for which permissions should be granted. This uri must NOT contain an
+     *            embedded userId; use {@link ContentProvider#getUriWithoutUserId(Uri)} if needed.
+     * @param modeFlags The modes to grant. See {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc.
+     * @param sourceUserId The userId in which the uri is to be resolved.
+     * @param targetUserId The userId of the target app to receive the grant.
+     */
     @Override
     public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg,
             Uri uri, final int modeFlags, int sourceUserId, int targetUserId) {
@@ -219,12 +226,11 @@
     }
 
     /**
-     * @param uri This uri must NOT contain an embedded userId.
-     * @param sourceUserId The userId in which the uri is to be resolved.
-     * @param targetUserId The userId of the app that receives the grant.
+     * See {@link #grantUriPermissionFromOwner(IBinder, int, String, Uri, int, int, int)}.
      */
-    private void grantUriPermissionFromOwnerUnlocked(IBinder token, int fromUid, String targetPkg,
-            Uri uri, final int modeFlags, int sourceUserId, int targetUserId) {
+    private void grantUriPermissionFromOwnerUnlocked(@NonNull IBinder token, int fromUid,
+            @NonNull String targetPkg, @NonNull Uri uri, final int modeFlags,
+            int sourceUserId, int targetUserId) {
         targetUserId = mAmInternal.handleIncomingUser(Binder.getCallingPid(),
                 Binder.getCallingUid(), targetUserId, false, ALLOW_FULL_ONLY,
                 "grantUriPermissionFromOwner", null);
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 607da3c..e84ee672 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -120,7 +120,9 @@
         if (newEffect.equals(mEffect)) {
             return;
         }
-        mOriginalEffect = mEffect;
+        if (mOriginalEffect == null) {
+            mOriginalEffect = mEffect;
+        }
         mEffect = newEffect;
     }
 
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 95f6059..eaba083 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -87,8 +87,8 @@
     static native long vibratorPerformEffect(
             long nativePtr, long effect, long strength, long vibrationId);
 
-    static native void vibratorPerformComposedEffect(long nativePtr,
-            VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
+    static native long vibratorPerformComposedEffect(
+            long nativePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
 
     static native void vibratorSetExternalControl(long nativePtr, boolean enabled);
 
@@ -269,13 +269,9 @@
             VibrationEffect.Composition.PrimitiveEffect[] primitives =
                     effect.getPrimitiveEffects().toArray(
                             new VibrationEffect.Composition.PrimitiveEffect[0]);
-            mNativeWrapper.compose(primitives, vibrationId);
-            notifyVibratorOnLocked();
-            // Compose don't actually give us an estimated duration, so we just guess here.
-            long duration = 0;
-            for (VibrationEffect.Composition.PrimitiveEffect primitive : primitives) {
-                // TODO(b/177807015): use exposed durations from IVibrator here instead
-                duration += 20 + primitive.delay;
+            long duration = mNativeWrapper.compose(primitives, vibrationId);
+            if (duration > 0) {
+                notifyVibratorOnLocked();
             }
             return duration;
         }
@@ -393,9 +389,9 @@
         }
 
         /** Turns vibrator on to perform one of the supported composed effects. */
-        public void compose(
+        public long compose(
                 VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) {
-            VibratorController.vibratorPerformComposedEffect(mNativePtr, effect,
+            return VibratorController.vibratorPerformComposedEffect(mNativePtr, effect,
                     vibrationId);
         }
 
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 90a763c..c9751bb 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -344,10 +344,11 @@
             if (!isEffectValid(effect)) {
                 return;
             }
-            effect = fixupVibrationEffect(effect);
             attrs = fixupVibrationAttributes(attrs);
             Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), effect, attrs,
                     uid, opPkg, reason);
+            // Update with fixed up effect to keep the original effect in Vibration for debugging.
+            vib.updateEffect(fixupVibrationEffect(effect));
 
             synchronized (mLock) {
                 Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib);
@@ -1138,6 +1139,9 @@
         void dumpText(PrintWriter pw) {
             pw.println("Vibrator Manager Service:");
             synchronized (mLock) {
+                pw.println("  mVibrationSettings:");
+                pw.println("    " + mVibrationSettings);
+                pw.println();
                 pw.println("  mVibratorControllers:");
                 for (int i = 0; i < mVibrators.size(); i++) {
                     pw.println("    " + mVibrators.valueAt(i));
@@ -1146,14 +1150,15 @@
                 pw.println("  mCurrentVibration:");
                 pw.println("    " + (mCurrentVibration == null
                         ? null : mCurrentVibration.getVibration().getDebugInfo()));
+                pw.println();
                 pw.println("  mNextVibration:");
                 pw.println("    " + (mNextVibration == null
                         ? null : mNextVibration.getVibration().getDebugInfo()));
+                pw.println();
                 pw.println("  mCurrentExternalVibration:");
                 pw.println("    " + (mCurrentExternalVibration == null
                         ? null : mCurrentExternalVibration.getDebugInfo()));
                 pw.println();
-                pw.println("  mVibrationSettings=" + mVibrationSettings);
                 for (int i = 0; i < mPreviousVibrations.size(); i++) {
                     pw.println();
                     pw.print("  Previous vibrations for usage ");
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5932388..298128a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -211,9 +211,10 @@
 import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
 import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
 import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO;
+import static com.android.server.wm.WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.letterboxBackgroundTypeToString;
 import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
@@ -580,9 +581,6 @@
 
     private Task mLastParent;
 
-    // Have we told the window clients to show themselves?
-    private boolean mClientVisible;
-
     boolean firstWindowDrawn;
     /** Whether the visible window(s) of this activity is drawn. */
     private boolean mReportedDrawn;
@@ -910,7 +908,7 @@
                         pw.print(Integer.toHexString(taskDescription.getStatusBarColor()));
                         pw.print(" navigationBarColor=");
                         pw.println(Integer.toHexString(taskDescription.getNavigationBarColor()));
-                        pw.print(" backgroundColorFloating=");
+                        pw.print(prefix); pw.print(" backgroundColorFloating=");
                         pw.println(Integer.toHexString(
                                 taskDescription.getBackgroundColorFloating()));
             }
@@ -998,7 +996,7 @@
         pw.print(prefix); pw.print("mOrientation=");
         pw.println(ActivityInfo.screenOrientationToString(mOrientation));
         pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
-                + " mVisible=" + mVisible + " mClientVisible=" + mClientVisible
+                + " mVisible=" + mVisible + " mClientVisible=" + isClientVisible()
                 + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
                 + " reportedDrawn=" + mReportedDrawn + " reportedVisible=" + reportedVisible);
         if (paused) {
@@ -1080,6 +1078,46 @@
                 pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
             }
         }
+
+        dumpLetterboxInfo(pw, prefix);
+    }
+
+    private void dumpLetterboxInfo(PrintWriter pw, String prefix) {
+        final WindowState mainWin = findMainWindow();
+        if (mainWin == null) {
+            return;
+        }
+
+        boolean isLetterboxed = isLetterboxed(mainWin);
+        pw.println(prefix + "isLetterboxed=" + isLetterboxed);
+        if (!isLetterboxed) {
+            return;
+        }
+
+        pw.println(prefix + "  letterboxReason=" + getLetterboxReasonString(mainWin));
+        pw.println(prefix + "  letterboxBackgroundColor=" + Integer.toHexString(
+                getLetterboxBackgroundColor().toArgb()));
+        pw.println(prefix + "  letterboxBackgroundType="
+                + letterboxBackgroundTypeToString(mWmService.getLetterboxBackgroundType()));
+        pw.println(prefix + "  letterboxAspectRatio="
+                + computeAspectRatio(getBounds()));
+    }
+
+    /**
+     * Returns a string representing the reason for letterboxing. This method assumes the activity
+     * is letterboxed.
+     */
+    private String getLetterboxReasonString(WindowState mainWin) {
+        if (inSizeCompatMode()) {
+            return "SIZE_COMPAT_MODE";
+        }
+        if (isLetterboxedForFixedOrientationAndAspectRatio()) {
+            return "FIXED_ORIENTATION";
+        }
+        if (mainWin.isLetterboxedForDisplayCutout()) {
+            return "DISPLAY_CUTOUT";
+        }
+        return "UNKNOWN_REASON";
     }
 
     void setAppTimeTracker(AppTimeTracker att) {
@@ -1695,7 +1733,7 @@
         keysPaused = false;
         inHistory = false;
         nowVisible = false;
-        mClientVisible = true;
+        super.setClientVisible(true);
         idle = false;
         hasBeenLaunched = false;
         mTaskSupervisor = supervisor;
@@ -3735,7 +3773,7 @@
                     setVisibleRequested(true);
                     mVisibleSetFromTransferredStartingWindow = true;
                 }
-                setClientVisible(fromActivity.mClientVisible);
+                setClientVisible(fromActivity.isClientVisible());
 
                 if (fromActivity.isAnimating()) {
                     transferAnimation(fromActivity);
@@ -5836,18 +5874,15 @@
         return mReportedDrawn;
     }
 
-    boolean isClientVisible() {
-        return mClientVisible;
-    }
-
+    @Override
     void setClientVisible(boolean clientVisible) {
-        if (mClientVisible == clientVisible || (!clientVisible && mDeferHidingClient)) {
-            return;
-        }
+        // TODO(shell-transitions): Remove mDeferHidingClient once everything is shell-transitions.
+        //                          pip activities should just remain in clientVisible.
+        if (!clientVisible && mDeferHidingClient) return;
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                 "setClientVisible: %s clientVisible=%b Callers=%s", this, clientVisible,
                 Debug.getCallers(5));
-        mClientVisible = clientVisible;
+        super.setClientVisible(clientVisible);
         sendAppVisibilityToClients();
     }
 
@@ -6959,6 +6994,13 @@
         float aspect = Math.max(parentWidth, parentHeight)
                 / (float) Math.min(parentWidth, parentHeight);
 
+        // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
+        // set-fixed-orientation-letterbox-aspect-ratio.
+        final float letterboxAspectRatioOverride =
+                mWmService.getFixedOrientationLetterboxAspectRatio();
+        aspect = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+                ? letterboxAspectRatioOverride : aspect;
+
         // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
         // order to use the extra available space.
         final float maxAspectRatio = info.maxAspectRatio;
@@ -6969,16 +7011,6 @@
             aspect = minAspectRatio;
         }
 
-        // Override from config_letterboxAspectRatio or via ADB with set-letterbox-aspect-ratio.
-        // TODO(b/175212232): Rename getTaskLetterboxAspectRatio and all related methods since fixed
-        // orientation letterbox is on the activity level now.
-        final float letterboxAspectRatioOverride = mWmService.getTaskLetterboxAspectRatio();
-        // Activity min/max aspect ratio restrictions will be respected by the activity-level
-        // letterboxing (size-compat mode). Therefore this override can control the maximum screen
-        // area that can be occupied by the app in the letterbox mode.
-        aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO
-                ? letterboxAspectRatioOverride : aspect;
-
         // Store the current bounds to be able to revert to size compat mode values below if needed.
         Rect mTmpFullBounds = new Rect(resolvedBounds);
         if (forcedOrientation == ORIENTATION_LANDSCAPE) {
@@ -7391,8 +7423,7 @@
 
         final int containingAppWidth = containingAppBounds.width();
         final int containingAppHeight = containingAppBounds.height();
-        final float containingRatio = Math.max(containingAppWidth, containingAppHeight)
-                / (float) Math.min(containingAppWidth, containingAppHeight);
+        final float containingRatio = computeAspectRatio(containingAppBounds);
 
         int activityWidth = containingAppWidth;
         int activityHeight = containingAppHeight;
@@ -7456,6 +7487,18 @@
     }
 
     /**
+     * Returns the aspect ratio of the given {@code rect}.
+     */
+    private static float computeAspectRatio(Rect rect) {
+        final int width = rect.width();
+        final int height = rect.height();
+        if (width == 0 || height == 0) {
+            return 0;
+        }
+        return Math.max(width, height) / (float) Math.min(width, height);
+    }
+
+    /**
      * @return {@code true} if this activity was reparented to another display but
      *         {@link #ensureActivityConfiguration} is not called.
      */
@@ -8134,7 +8177,7 @@
         proto.write(TRANSLUCENT, !occludesParent());
         proto.write(VISIBLE, mVisible);
         proto.write(VISIBLE_REQUESTED, mVisibleRequested);
-        proto.write(CLIENT_VISIBLE, mClientVisible);
+        proto.write(CLIENT_VISIBLE, isClientVisible());
         proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient);
         proto.write(REPORTED_DRAWN, mReportedDrawn);
         proto.write(REPORTED_VISIBLE, reportedVisible);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index bb0f1f0..db751e9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1504,7 +1504,13 @@
             // Prevent recursion.
             return;
         }
-        mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task);
+        if (task.isVisible()) {
+            mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task);
+        } else {
+            // Removing a non-visible task doesn't require a transition, but if there is one
+            // collecting, this should be a member just in case.
+            mService.getTransitionController().collect(task);
+        }
         task.mInRemoveTask = true;
         try {
             task.performClearTask(reason);
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 759b7fe..5ccf576 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -495,6 +495,21 @@
         return info;
     }
 
+    /**
+     * Gets the stable bounds of the DisplayArea, which is the bounds excluding insets for
+     * navigation bar, cutout, and status bar.
+     */
+    void getStableRect(Rect out) {
+        if (mDisplayContent == null) {
+            getBounds(out);
+            return;
+        }
+
+        // Intersect with the display stable bounds to get the DisplayArea stable bounds.
+        mDisplayContent.getStableRect(out);
+        out.intersect(getBounds());
+    }
+
     @Override
     public boolean providesMaxBounds() {
         return true;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1157307..119ffb7 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2677,6 +2677,7 @@
         mWmService.mDisplayWindowSettings.setForcedSize(this, width, height);
     }
 
+    @Override
     void getStableRect(Rect out) {
         final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
         out.set(state.getDisplayFrame());
@@ -4822,7 +4823,7 @@
         }
         mNoAnimationNotifyOnTransitionFinished.clear();
 
-        mWallpaperController.hideDeferredWallpapersIfNeeded();
+        mWallpaperController.hideDeferredWallpapersIfNeededLegacy();
 
         onAppTransitionDone();
 
@@ -5261,13 +5262,6 @@
                 || windowingMode == WINDOWING_MODE_MULTI_WINDOW);
     }
 
-    static boolean canReuseExistingTask(int windowingMode, int activityType) {
-        // Existing Tasks can be reused if a new root task will be created anyway, or for the
-        // Dream - because there can only ever be one DreamActivity.
-        return alwaysCreateRootTask(windowingMode, activityType)
-                || activityType == ACTIVITY_TYPE_DREAM;
-    }
-
     @Nullable
     Task getFocusedRootTask() {
         return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedRootTask);
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 1f7e152..316c20b 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -99,6 +99,9 @@
         mTask.forAllActivities(a -> {
             setActivityVisibilityState(a, starting, resumeTopActivity);
         });
+        if (mTask.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+            mTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
+        }
     }
 
     private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 27ef147..28c5a6d9 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -410,15 +410,19 @@
             return;
         }
 
+        requestFocus(focusToken, focus.getName());
+    }
+
+    private void requestFocus(IBinder focusToken, String windowName) {
         if (focusToken == mInputFocus) {
             return;
         }
 
         mInputFocus = focusToken;
-        mInputTransaction.setFocusedWindow(mInputFocus, focus.getName(), mDisplayId);
-        EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + focus,
+        mInputTransaction.setFocusedWindow(mInputFocus, windowName, mDisplayId);
+        EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + windowName,
                 "reason=UpdateInputWindows");
-        ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus);
+        ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", windowName);
     }
 
     void setFocusedAppLw(ActivityRecord newApp) {
@@ -470,6 +474,8 @@
 
         boolean mInDrag;
 
+        private boolean mRecentsAnimationFocusOverride;
+
         private void updateInputWindows(boolean inDrag) {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
 
@@ -485,10 +491,16 @@
             mInDrag = inDrag;
 
             resetInputConsumers(mInputTransaction);
-
+            mRecentsAnimationFocusOverride = false;
             mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
 
-            updateInputFocusRequest();
+            if (mRecentsAnimationFocusOverride) {
+                requestFocus(mRecentsAnimationInputConsumer.mWindowHandle.token,
+                        mRecentsAnimationInputConsumer.mName);
+            } else {
+                updateInputFocusRequest();
+            }
+
 
             if (!mUpdateInputWindowsImmediately) {
                 mDisplayContent.getPendingTransaction().merge(mInputTransaction);
@@ -526,6 +538,7 @@
                         mRecentsAnimationInputConsumer.mWindowHandle)) {
                     mRecentsAnimationInputConsumer.show(mInputTransaction, w.mActivityRecord);
                     mAddRecentsAnimationInputConsumerHandle = false;
+                    mRecentsAnimationFocusOverride = true;
                 }
             }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 5efbb09..194350d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -21,6 +21,7 @@
 import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -3290,11 +3291,22 @@
 
     @Override
     boolean handlesOrientationChangeFromDescendant() {
-        return super.handlesOrientationChangeFromDescendant()
-                // Display won't rotate for the orientation request if the Task/TaskDisplayArea
-                // can't specify orientation.
-                && canSpecifyOrientation()
-                && getDisplayArea().canSpecifyOrientation();
+        if (!super.handlesOrientationChangeFromDescendant()) {
+            return false;
+        }
+
+        // At task level, we want to check canSpecifyOrientation() based on the top activity type.
+        // Do this only on leaf Task, so that the result is not affecting by the sibling leaf Task.
+        // Otherwise, root Task will use the result from the top leaf Task, and all its child
+        // leaf Tasks will rely on that from super.handlesOrientationChangeFromDescendant().
+        if (!isLeafTask()) {
+            return true;
+        }
+
+        // Check for leaf Task.
+        // Display won't rotate for the orientation request if the Task/TaskDisplayArea
+        // can't specify orientation.
+        return canSpecifyOrientation() && getDisplayArea().canSpecifyOrientation();
     }
 
     void resize(boolean relayout, boolean forced) {
@@ -3625,7 +3637,6 @@
 
     @Override
     void resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t) {
-        if (isOrganized()) return;
         super.resetSurfacePositionForAnimationLeash(t);
     }
 
@@ -7349,6 +7360,7 @@
         return reuseOrCreateTask(info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
                 toTop, null /*activity*/, null /*source*/, null /*options*/);
     }
+
     // TODO: Can be removed once we change callpoints creating root tasks to be creating tasks.
     /** Either returns this current task to be re-used or creates a new child task. */
     Task reuseOrCreateTask(ActivityInfo info, Intent intent, IVoiceInteractionSession voiceSession,
@@ -7356,7 +7368,7 @@
             ActivityRecord source, ActivityOptions options) {
 
         Task task;
-        if (DisplayContent.canReuseExistingTask(getWindowingMode(), getActivityType())) {
+        if (canReuseAsLeafTask()) {
             // This root task will only contain one task, so just return itself since all root
             // tasks ara now tasks and all tasks are now root tasks.
             task = reuseAsLeafTask(voiceSession, voiceInteractor, intent, info, activity);
@@ -7391,10 +7403,24 @@
         return task;
     }
 
+    /** Return {@code true} if this task can be reused as leaf task. */
+    private boolean canReuseAsLeafTask() {
+        // Cannot be reused as leaf task if this task is created by organizer or having child tasks.
+        if (mCreatedByOrganizer || !isLeafTask()) {
+            return false;
+        }
+
+        // Existing Tasks can be reused if a new root task will be created anyway, or for the
+        // Dream - because there can only ever be one DreamActivity.
+        final int windowingMode = getWindowingMode();
+        final int activityType = getActivityType();
+        return DisplayContent.alwaysCreateRootTask(windowingMode, activityType)
+                || activityType == ACTIVITY_TYPE_DREAM;
+    }
+
     void addChild(WindowContainer child, final boolean toTop, boolean showForAllUsers) {
         Task task = child.asTask();
         try {
-
             if (task != null) {
                 task.setForceShowForAllUsers(showForAllUsers);
             }
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 0be43ab..ee5c1f01 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -165,6 +165,10 @@
         // hasInitialBounds is set if either activity options or layout has specified bounds. If
         // that's set we'll skip some adjustments later to avoid overriding the initial bounds.
         boolean hasInitialBounds = false;
+        // hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow is set if the outParams.mBounds
+        // is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
+        // different, we should recalculating the bounds.
+        boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow = false;
         final boolean canApplyFreeformPolicy = canApplyFreeformWindowPolicy(display, launchMode);
         if (mSupervisor.canUseActivityOptionsLaunchBounds(options)
                 && (canApplyFreeformPolicy || canApplyPipWindowPolicy(launchMode))) {
@@ -181,11 +185,12 @@
             if (DEBUG) appendLog("activity-options-fullscreen=" + outParams.mBounds);
         } else if (layout != null && canApplyFreeformPolicy) {
             mTmpBounds.set(currentParams.mBounds);
-            getLayoutBounds(display, root, layout, mTmpBounds);
+            getLayoutBounds(suggestedDisplayArea, root, layout, mTmpBounds);
             if (!mTmpBounds.isEmpty()) {
                 launchMode = WINDOWING_MODE_FREEFORM;
                 outParams.mBounds.set(mTmpBounds);
                 hasInitialBounds = true;
+                hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow = true;
                 if (DEBUG) appendLog("bounds-from-layout=" + outParams.mBounds);
             } else {
                 if (DEBUG) appendLog("empty-window-layout");
@@ -241,6 +246,11 @@
         // such as orientation. Otherwise, the app is forcefully launched in maximized. The rest of
         // this step is to define the default policy when there is no initial bounds or a fully
         // resolved current params from callers.
+
+        // hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay is set if the outParams.mBounds
+        // is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
+        // different, we should recalcuating the bounds.
+        boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay = false;
         if (display.inFreeformWindowingMode()) {
             if (launchMode == WINDOWING_MODE_PINNED) {
                 if (DEBUG) appendLog("picture-in-picture");
@@ -248,8 +258,9 @@
                 if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea)) {
                     launchMode = WINDOWING_MODE_FREEFORM;
                     if (outParams.mBounds.isEmpty()) {
-                        getTaskBounds(root, display, layout, launchMode, hasInitialBounds,
-                                outParams.mBounds);
+                        getTaskBounds(root, suggestedDisplayArea, layout, launchMode,
+                                hasInitialBounds, outParams.mBounds);
+                        hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay = true;
                     }
                     if (DEBUG) appendLog("unresizable-freeform");
                 } else {
@@ -288,6 +299,20 @@
                 mTmpDisplayArea = displayArea;
                 return true;
             });
+            // We may need to recalculate the bounds if the new TaskDisplayArea is different from
+            // the suggested one we used to calculate the bounds.
+            if (mTmpDisplayArea != null && mTmpDisplayArea != suggestedDisplayArea) {
+                if (hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow) {
+                    outParams.mBounds.setEmpty();
+                    getLayoutBounds(mTmpDisplayArea, root, layout, outParams.mBounds);
+                    hasInitialBounds = !outParams.mBounds.isEmpty();
+                } else if (hasInitialBoundsForSuggestedDisplayAreaInFreeformDisplay) {
+                    outParams.mBounds.setEmpty();
+                    getTaskBounds(root, mTmpDisplayArea, layout, launchMode,
+                            hasInitialBounds, outParams.mBounds);
+                }
+            }
+
             if (mTmpDisplayArea != null) {
                 taskDisplayArea = mTmpDisplayArea;
                 mTmpDisplayArea = null;
@@ -303,7 +328,6 @@
         if (phase == PHASE_DISPLAY_AREA) {
             return RESULT_CONTINUE;
         }
-        // TODO(b/152116619): Update the usages of display to use taskDisplayArea below.
 
         // STEP 4: Determine final launch bounds based on resolved windowing mode and activity
         // requested orientation. We set bounds to empty for fullscreen mode and keep bounds as is
@@ -313,13 +337,13 @@
         // We skip making adjustments if the params are fully resolved from previous results.
         if (fullyResolvedCurrentParam) {
             if (resolvedMode == WINDOWING_MODE_FREEFORM) {
-                // Make sure bounds are in the display if it's possibly in a different display/area.
+                // Make sure bounds are in the displayArea.
                 if (currentParams.mPreferredTaskDisplayArea != taskDisplayArea) {
-                    adjustBoundsToFitInDisplay(display, outParams.mBounds);
+                    adjustBoundsToFitInDisplayArea(taskDisplayArea, outParams.mBounds);
                 }
                 // Even though we want to keep original bounds, we still don't want it to stomp on
                 // an existing task.
-                adjustBoundsToAvoidConflictInDisplay(display, outParams.mBounds);
+                adjustBoundsToAvoidConflictInDisplayArea(taskDisplayArea, outParams.mBounds);
             }
         } else if (taskDisplayArea.inFreeformWindowingMode()) {
             if (source != null && source.inFreeformWindowingMode()
@@ -328,9 +352,10 @@
                     && source.getDisplayArea() == taskDisplayArea) {
                 // Set bounds to be not very far from source activity.
                 cascadeBounds(source.getConfiguration().windowConfiguration.getBounds(),
-                        display, outParams.mBounds);
+                        taskDisplayArea, outParams.mBounds);
             }
-            getTaskBounds(root, display, layout, resolvedMode, hasInitialBounds, outParams.mBounds);
+            getTaskBounds(root, taskDisplayArea, layout, resolvedMode, hasInitialBounds,
+                    outParams.mBounds);
         }
         return RESULT_CONTINUE;
     }
@@ -500,7 +525,7 @@
                 && launchMode == WINDOWING_MODE_PINNED;
     }
 
-    private void getLayoutBounds(@NonNull DisplayContent display, @NonNull ActivityRecord root,
+    private void getLayoutBounds(@NonNull TaskDisplayArea displayArea, @NonNull ActivityRecord root,
             @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect inOutBounds) {
         final int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
         final int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
@@ -511,10 +536,10 @@
 
         // Use stable frame instead of raw frame to avoid launching freeform windows on top of
         // stable insets, which usually are system widgets such as sysbar & navbar.
-        final Rect displayStableBounds = mTmpStableBounds;
-        display.getStableRect(displayStableBounds);
-        final int defaultWidth = displayStableBounds.width();
-        final int defaultHeight = displayStableBounds.height();
+        final Rect stableBounds = mTmpStableBounds;
+        displayArea.getStableRect(stableBounds);
+        final int defaultWidth = stableBounds.width();
+        final int defaultHeight = stableBounds.height();
 
         int width;
         int height;
@@ -525,7 +550,7 @@
                 width = inOutBounds.width();
                 height = inOutBounds.height();
             } else {
-                getTaskBounds(root, display, windowLayout, WINDOWING_MODE_FREEFORM,
+                getTaskBounds(root, displayArea, windowLayout, WINDOWING_MODE_FREEFORM,
                         /* hasInitialBounds */ false, inOutBounds);
                 width = inOutBounds.width();
                 height = inOutBounds.height();
@@ -571,7 +596,7 @@
         }
 
         inOutBounds.set(0, 0, width, height);
-        inOutBounds.offset(displayStableBounds.left, displayStableBounds.top);
+        inOutBounds.offset(stableBounds.left, stableBounds.top);
         final int xOffset = (int) (fractionOfHorizontalOffset * (defaultWidth - width));
         final int yOffset = (int) (fractionOfVerticalOffset * (defaultHeight - height));
         inOutBounds.offset(xOffset, yOffset);
@@ -582,13 +607,9 @@
         if (!mSupervisor.mService.mSupportsNonResizableMultiWindow || activity.isResizeable()) {
             return false;
         }
-        final DisplayContent display = displayArea.getDisplayContent();
-        if (display == null) {
-            return false;
-        }
 
         final int displayOrientation = orientationFromBounds(displayArea.getBounds());
-        final int activityOrientation = resolveOrientation(activity, display,
+        final int activityOrientation = resolveOrientation(activity, displayArea,
                 displayArea.getBounds());
         if (displayArea.getWindowingMode() == WINDOWING_MODE_FREEFORM
                 && displayOrientation != activityOrientation) {
@@ -638,19 +659,19 @@
         return orientation;
     }
 
-    private void cascadeBounds(@NonNull Rect srcBounds, @NonNull DisplayContent display,
+    private void cascadeBounds(@NonNull Rect srcBounds, @NonNull TaskDisplayArea displayArea,
             @NonNull Rect outBounds) {
         outBounds.set(srcBounds);
-        float density = (float) display.getConfiguration().densityDpi / DENSITY_DEFAULT;
+        float density = (float) displayArea.getConfiguration().densityDpi / DENSITY_DEFAULT;
         final int defaultOffset = (int) (CASCADING_OFFSET_DP * density + 0.5f);
 
-        display.getBounds(mTmpBounds);
+        displayArea.getBounds(mTmpBounds);
         final int dx = Math.min(defaultOffset, Math.max(0, mTmpBounds.right - srcBounds.right));
         final int dy = Math.min(defaultOffset, Math.max(0, mTmpBounds.bottom - srcBounds.bottom));
         outBounds.offset(dx, dy);
     }
 
-    private void getTaskBounds(@NonNull ActivityRecord root, @NonNull DisplayContent display,
+    private void getTaskBounds(@NonNull ActivityRecord root, @NonNull TaskDisplayArea displayArea,
             @NonNull ActivityInfo.WindowLayout layout, int resolvedMode, boolean hasInitialBounds,
             @NonNull Rect inOutBounds) {
         if (resolvedMode == WINDOWING_MODE_FULLSCREEN) {
@@ -669,7 +690,7 @@
             return;
         }
 
-        final int orientation = resolveOrientation(root, display, inOutBounds);
+        final int orientation = resolveOrientation(root, displayArea, inOutBounds);
         if (orientation != SCREEN_ORIENTATION_PORTRAIT
                 && orientation != SCREEN_ORIENTATION_LANDSCAPE) {
             throw new IllegalStateException(
@@ -678,7 +699,7 @@
         }
 
         // First we get the default size we want.
-        getDefaultFreeformSize(display, layout, orientation, mTmpBounds);
+        getDefaultFreeformSize(displayArea, layout, orientation, mTmpBounds);
         if (hasInitialBounds || sizeMatches(inOutBounds, mTmpBounds)) {
             // We're here because either input parameters specified initial bounds, or the suggested
             // bounds have the same size of the default freeform size. We should use the suggested
@@ -688,22 +709,24 @@
                 if (DEBUG) appendLog("freeform-size-orientation-match=" + inOutBounds);
             } else {
                 // Meh, orientation doesn't match. Let's rotate inOutBounds in-place.
-                centerBounds(display, inOutBounds.height(), inOutBounds.width(), inOutBounds);
+                centerBounds(displayArea, inOutBounds.height(), inOutBounds.width(),
+                        inOutBounds);
                 if (DEBUG) appendLog("freeform-orientation-mismatch=" + inOutBounds);
             }
         } else {
             // We are here either because there is no suggested bounds, or the suggested bounds is
             // a cascade from source activity. We should use the default freeform size and center it
-            // to the center of suggested bounds (or the display if no suggested bounds). The
-            // default size might be too big to center to source activity bounds in display, so we
-            // may need to move it back to the display.
-            centerBounds(display, mTmpBounds.width(), mTmpBounds.height(), inOutBounds);
-            adjustBoundsToFitInDisplay(display, inOutBounds);
+            // to the center of suggested bounds (or the displayArea if no suggested bounds). The
+            // default size might be too big to center to source activity bounds in displayArea, so
+            // we may need to move it back to the displayArea.
+            centerBounds(displayArea, mTmpBounds.width(), mTmpBounds.height(),
+                    inOutBounds);
+            adjustBoundsToFitInDisplayArea(displayArea, inOutBounds);
             if (DEBUG) appendLog("freeform-size-mismatch=" + inOutBounds);
         }
 
         // Lastly we adjust bounds to avoid conflicts with other tasks as much as possible.
-        adjustBoundsToAvoidConflictInDisplay(display, inOutBounds);
+        adjustBoundsToAvoidConflictInDisplayArea(displayArea, inOutBounds);
     }
 
     private int convertOrientationToScreenOrientation(int orientation) {
@@ -717,13 +740,14 @@
         }
     }
 
-    private int resolveOrientation(@NonNull ActivityRecord root, @NonNull DisplayContent display,
-            @NonNull Rect bounds) {
+    private int resolveOrientation(@NonNull ActivityRecord root,
+            @NonNull TaskDisplayArea displayArea, @NonNull Rect bounds) {
         int orientation = resolveOrientation(root);
 
         if (orientation == SCREEN_ORIENTATION_LOCKED) {
             orientation = bounds.isEmpty()
-                    ? convertOrientationToScreenOrientation(display.getConfiguration().orientation)
+                    ? convertOrientationToScreenOrientation(
+                            displayArea.getConfiguration().orientation)
                     : orientationFromBounds(bounds);
             if (DEBUG) {
                 appendLog(bounds.isEmpty() ? "locked-orientation-from-display=" + orientation
@@ -743,19 +767,17 @@
         return orientation;
     }
 
-    private void getDefaultFreeformSize(@NonNull DisplayContent display,
+    private void getDefaultFreeformSize(@NonNull TaskDisplayArea displayArea,
             @NonNull ActivityInfo.WindowLayout layout, int orientation, @NonNull Rect bounds) {
-        // Default size, which is letterboxing/pillarboxing in display. That's to say the large
-        // dimension of default size is the small dimension of display size, and the small dimension
-        // of default size is calculated to keep the same aspect ratio as the display's. Here we use
-        // stable bounds of displays because that indicates the area that isn't occupied by system
-        // widgets (e.g. sysbar and navbar).
-        final Rect displayStableBounds = mTmpStableBounds;
-        display.getStableRect(displayStableBounds);
-        final int portraitHeight =
-                Math.min(displayStableBounds.width(), displayStableBounds.height());
-        final int otherDimension =
-                Math.max(displayStableBounds.width(), displayStableBounds.height());
+        // Default size, which is letterboxing/pillarboxing in displayArea. That's to say the large
+        // dimension of default size is the small dimension of displayArea size, and the small
+        // dimension of default size is calculated to keep the same aspect ratio as the
+        // displayArea's. Here we use stable bounds of displayArea because that indicates the area
+        // that isn't occupied by system widgets (e.g. sysbar and navbar).
+        final Rect stableBounds = mTmpStableBounds;
+        displayArea.getStableRect(stableBounds);
+        final int portraitHeight = Math.min(stableBounds.width(), stableBounds.height());
+        final int otherDimension = Math.max(stableBounds.width(), stableBounds.height());
         final int portraitWidth = (portraitHeight * portraitHeight) / otherDimension;
         final int defaultWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitHeight
                 : portraitWidth;
@@ -764,7 +786,7 @@
 
         // Get window size based on Nexus 5x screen, we assume that this is enough to show content
         // of activities.
-        final float density = (float) display.getConfiguration().densityDpi / DENSITY_DEFAULT;
+        final float density = (float) displayArea.getConfiguration().densityDpi / DENSITY_DEFAULT;
         final int phonePortraitWidth = (int) (DEFAULT_PORTRAIT_PHONE_WIDTH_DP * density + 0.5f);
         final int phonePortraitHeight = (int) (DEFAULT_PORTRAIT_PHONE_HEIGHT_DP * density + 0.5f);
         final int phoneWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? phonePortraitHeight
@@ -781,83 +803,83 @@
         final int height = Math.min(defaultHeight, Math.max(phoneHeight, layoutMinHeight));
 
         bounds.set(0, 0, width, height);
-        bounds.offset(displayStableBounds.left, displayStableBounds.top);
+        bounds.offset(stableBounds.left, stableBounds.top);
     }
 
     /**
      * Gets centered bounds of width x height. If inOutBounds is not empty, the result bounds
-     * centers at its center or display's app bounds center if inOutBounds is empty.
+     * centers at its center or displayArea's app bounds center if inOutBounds is empty.
      */
-    private void centerBounds(@NonNull DisplayContent display, int width, int height,
+    private void centerBounds(@NonNull TaskDisplayArea displayArea, int width, int height,
             @NonNull Rect inOutBounds) {
         if (inOutBounds.isEmpty()) {
-            display.getStableRect(inOutBounds);
+            displayArea.getStableRect(inOutBounds);
         }
         final int left = inOutBounds.centerX() - width / 2;
         final int top = inOutBounds.centerY() - height / 2;
         inOutBounds.set(left, top, left + width, top + height);
     }
 
-    private void adjustBoundsToFitInDisplay(@NonNull DisplayContent display,
+    private void adjustBoundsToFitInDisplayArea(@NonNull TaskDisplayArea displayArea,
             @NonNull Rect inOutBounds) {
-        final Rect displayStableBounds = mTmpStableBounds;
-        display.getStableRect(displayStableBounds);
+        final Rect stableBounds = mTmpStableBounds;
+        displayArea.getStableRect(stableBounds);
 
-        if (displayStableBounds.width() < inOutBounds.width()
-                || displayStableBounds.height() < inOutBounds.height()) {
-            // There is no way for us to fit the bounds in the display without changing width
-            // or height. Just move the start to align with the display.
+        if (stableBounds.width() < inOutBounds.width()
+                || stableBounds.height() < inOutBounds.height()) {
+            // There is no way for us to fit the bounds in the displayArea without changing width
+            // or height. Just move the start to align with the displayArea.
             final int layoutDirection =
                     mSupervisor.mRootWindowContainer.getConfiguration().getLayoutDirection();
             final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL
-                    ? displayStableBounds.right - inOutBounds.right + inOutBounds.left
-                    : displayStableBounds.left;
-            inOutBounds.offsetTo(left, displayStableBounds.top);
+                    ? stableBounds.right - inOutBounds.right + inOutBounds.left
+                    : stableBounds.left;
+            inOutBounds.offsetTo(left, stableBounds.top);
             return;
         }
 
         final int dx;
-        if (inOutBounds.right > displayStableBounds.right) {
-            // Right edge is out of display.
-            dx = displayStableBounds.right - inOutBounds.right;
-        } else if (inOutBounds.left < displayStableBounds.left) {
-            // Left edge is out of display.
-            dx = displayStableBounds.left - inOutBounds.left;
+        if (inOutBounds.right > stableBounds.right) {
+            // Right edge is out of displayArea.
+            dx = stableBounds.right - inOutBounds.right;
+        } else if (inOutBounds.left < stableBounds.left) {
+            // Left edge is out of displayArea.
+            dx = stableBounds.left - inOutBounds.left;
         } else {
-            // Vertical edges are all in display.
+            // Vertical edges are all in displayArea.
             dx = 0;
         }
 
         final int dy;
-        if (inOutBounds.top < displayStableBounds.top) {
-            // Top edge is out of display.
-            dy = displayStableBounds.top - inOutBounds.top;
-        } else if (inOutBounds.bottom > displayStableBounds.bottom) {
-            // Bottom edge is out of display.
-            dy = displayStableBounds.bottom - inOutBounds.bottom;
+        if (inOutBounds.top < stableBounds.top) {
+            // Top edge is out of displayArea.
+            dy = stableBounds.top - inOutBounds.top;
+        } else if (inOutBounds.bottom > stableBounds.bottom) {
+            // Bottom edge is out of displayArea.
+            dy = stableBounds.bottom - inOutBounds.bottom;
         } else {
-            // Horizontal edges are all in display.
+            // Horizontal edges are all in displayArea.
             dy = 0;
         }
         inOutBounds.offset(dx, dy);
     }
 
     /**
-     * Adjusts input bounds to avoid conflict with existing tasks in the display.
+     * Adjusts input bounds to avoid conflict with existing tasks in the displayArea.
      *
      * If the input bounds conflict with existing tasks, this method scans the bounds in a series of
-     * directions to find a location where the we can put the bounds in display without conflict
+     * directions to find a location where the we can put the bounds in displayArea without conflict
      * with any other tasks.
      *
-     * It doesn't try to adjust bounds that's not fully in the given display.
+     * It doesn't try to adjust bounds that's not fully in the given displayArea.
      *
-     * @param display the display which tasks are to check
+     * @param displayArea the displayArea which tasks are to check
      * @param inOutBounds the bounds used to input initial bounds and output result bounds
      */
-    private void adjustBoundsToAvoidConflictInDisplay(@NonNull DisplayContent display,
+    private void adjustBoundsToAvoidConflictInDisplayArea(@NonNull TaskDisplayArea displayArea,
             @NonNull Rect inOutBounds) {
         final List<Rect> taskBoundsToCheck = new ArrayList<>();
-        display.forAllRootTasks(task -> {
+        displayArea.forAllRootTasks(task -> {
             if (!task.inFreeformWindowingMode()) {
                 return;
             }
@@ -866,28 +888,28 @@
                 taskBoundsToCheck.add(task.getChildAt(j).getBounds());
             }
         }, false /* traverseTopToBottom */);
-        adjustBoundsToAvoidConflict(display.getBounds(), taskBoundsToCheck, inOutBounds);
+        adjustBoundsToAvoidConflict(displayArea.getBounds(), taskBoundsToCheck, inOutBounds);
     }
 
     /**
-     * Adjusts input bounds to avoid conflict with provided display bounds and list of tasks bounds
-     * for the display.
+     * Adjusts input bounds to avoid conflict with provided displayArea bounds and list of tasks
+     * bounds for the displayArea.
      *
      * Scans the bounds in directions to find a candidate location that does not conflict with the
-     * provided list of task bounds. If starting bounds are outside the display bounds or if no
+     * provided list of task bounds. If starting bounds are outside the displayArea bounds or if no
      * suitable candidate bounds are found, the method returns the input bounds.
      *
-     * @param displayBounds display bounds used to restrict the candidate bounds
+     * @param displayAreaBounds displayArea bounds used to restrict the candidate bounds
      * @param taskBoundsToCheck list of task bounds to check for conflict
      * @param inOutBounds the bounds used to input initial bounds and output result bounds
      */
     @VisibleForTesting
-    void adjustBoundsToAvoidConflict(@NonNull Rect displayBounds,
+    void adjustBoundsToAvoidConflict(@NonNull Rect displayAreaBounds,
             @NonNull List<Rect> taskBoundsToCheck,
             @NonNull Rect inOutBounds) {
-        if (!displayBounds.contains(inOutBounds)) {
-            // The initial bounds are already out of display. The scanning algorithm below doesn't
-            // work so well with them.
+        if (!displayAreaBounds.contains(inOutBounds)) {
+            // The initial bounds are already out of displayArea. The scanning algorithm below
+            // doesn't work so well with them.
             return;
         }
 
@@ -897,7 +919,7 @@
             return;
         }
 
-        calculateCandidateShiftDirections(displayBounds, inOutBounds);
+        calculateCandidateShiftDirections(displayAreaBounds, inOutBounds);
         for (int direction : mTmpDirections) {
             if (direction == Gravity.NO_GRAVITY) {
                 // We exhausted candidate directions, give up.
@@ -906,12 +928,12 @@
 
             mTmpBounds.set(inOutBounds);
             while (boundsConflict(taskBoundsToCheck, mTmpBounds)
-                    && displayBounds.contains(mTmpBounds)) {
-                shiftBounds(direction, displayBounds, mTmpBounds);
+                    && displayAreaBounds.contains(mTmpBounds)) {
+                shiftBounds(direction, displayAreaBounds, mTmpBounds);
             }
 
             if (!boundsConflict(taskBoundsToCheck, mTmpBounds)
-                    && displayBounds.contains(mTmpBounds)) {
+                    && displayAreaBounds.contains(mTmpBounds)) {
                 // Found a candidate. Just use this.
                 inOutBounds.set(mTmpBounds);
                 if (DEBUG) appendLog("avoid-bounds-conflict=" + inOutBounds);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 98eb11f..aadb272 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -135,6 +135,11 @@
         mSyncId = mSyncEngine.startSyncSet(this);
     }
 
+    @VisibleForTesting
+    int getSyncId() {
+        return mSyncId;
+    }
+
     /**
      * Formally starts the transition. Participants can be collected before this is started,
      * but this won't consider itself ready until started -- even if all the participants have
@@ -235,16 +240,18 @@
         for (int i = mTargets.size() - 1; i >= 0; --i) {
             final WindowContainer target = mTargets.valueAt(i);
             if (target.getParent() != null) {
+                final SurfaceControl targetLeash = getLeashSurface(target);
+                final SurfaceControl origParent = getOrigParentSurface(target);
                 // Ensure surfaceControls are re-parented back into the hierarchy.
-                t.reparent(target.getSurfaceControl(), target.getParent().getSurfaceControl());
-                t.setLayer(target.getSurfaceControl(), target.getLastLayer());
+                t.reparent(targetLeash, origParent);
+                t.setLayer(targetLeash, target.getLastLayer());
                 // TODO(shell-transitions): Once all remotables have been moved, see if there is
                 //                          a more appropriate place to do the following. This may
                 //                          involve passing an SF transaction from shell on finish.
                 target.getRelativePosition(tmpPos);
-                t.setPosition(target.getSurfaceControl(), tmpPos.x, tmpPos.y);
-                t.setCornerRadius(target.getSurfaceControl(), 0);
-                t.setShadowRadius(target.getSurfaceControl(), 0);
+                t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
+                t.setCornerRadius(targetLeash, 0);
+                t.setShadowRadius(targetLeash, 0);
                 displays.add(target.getDisplayContent());
             }
         }
@@ -271,12 +278,17 @@
         // Commit all going-invisible containers
         for (int i = 0; i < mParticipants.size(); ++i) {
             final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
-            if (ar == null || ar.mVisibleRequested) {
-                continue;
+            if (ar != null && !ar.isVisibleRequested()) {
+                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+                        "  Commit activity becoming invisible: %s", ar);
+                ar.commitVisibility(false /* visible */, false /* performLayout */);
             }
-            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                    "  Commit activity becoming invisible: %s", ar);
-            ar.commitVisibility(false /* visible */, false /* performLayout */);
+            final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
+            if (wt != null && !wt.isVisibleRequested()) {
+                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+                        "  Commit wallpaper becoming invisible: %s", ar);
+                wt.commitVisibility(false /* visible */);
+            }
         }
     }
 
@@ -455,8 +467,7 @@
                 final int depth = getChildDepth(topTargets.valueAt(j), sibling);
                 if (depth < 0) continue;
                 if (depth == 0) {
-                    final int siblingMode = sibling.isVisibleRequested()
-                            ? TRANSIT_OPEN : TRANSIT_CLOSE;
+                    final int siblingMode = changes.get(sibling).getTransitMode(sibling);
                     ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                             "        sibling is a top target with mode %s",
                             TransitionInfo.modeToString(siblingMode));
@@ -638,6 +649,15 @@
         }
     }
 
+    /** Gets the leash surface for a window container */
+    private static SurfaceControl getLeashSurface(WindowContainer wc) {
+        return wc.getSurfaceControl();
+    }
+
+    private static SurfaceControl getOrigParentSurface(WindowContainer wc) {
+        return wc.getParent().getSurfaceControl();
+    }
+
     /**
      * Construct a TransitionInfo object from a set of targets and changes. Also populates the
      * root surface.
@@ -713,7 +733,7 @@
             final ChangeInfo info = changes.get(target);
             final TransitionInfo.Change change = new TransitionInfo.Change(
                     target.mRemoteToken != null ? target.mRemoteToken.toWindowContainerToken()
-                            : null, target.getSurfaceControl());
+                            : null, getLeashSurface(target));
             // TODO(shell-transitions): Use leash for non-organized windows.
             if (info.mParent != null) {
                 change.setParent(info.mParent.mRemoteToken.toWindowContainerToken());
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 5f46ffe..6338f39 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -69,7 +69,7 @@
      * Creates a transition. It can immediately collect participants.
      */
     @NonNull
-    Transition createTransition(@WindowManager.TransitionOldType int type,
+    Transition createTransition(@WindowManager.TransitionType int type,
             @WindowManager.TransitionFlags int flags) {
         if (mTransitionPlayer == null) {
             throw new IllegalStateException("Shell Transitions not enabled");
@@ -113,6 +113,14 @@
     }
 
     /**
+     * @return {@code true} if transition is actively collecting changes and `wc` is one of them.
+     *                      This is {@code false} once a transition is playing.
+     */
+    boolean isCollecting(@NonNull WindowContainer wc) {
+        return mCollectingTransition != null && mCollectingTransition.mParticipants.contains(wc);
+    }
+
+    /**
      * @return {@code true} if transition is actively playing. This is not necessarily {@code true}
      * during collection.
      */
@@ -128,9 +136,7 @@
 
     /** @return {@code true} if wc is in a participant subtree */
     boolean inTransition(@NonNull WindowContainer wc) {
-        if (mCollectingTransition != null && mCollectingTransition.mParticipants.contains(wc)) {
-            return true;
-        }
+        if (isCollecting(wc))  return true;
         for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
             for (WindowContainer p = wc; p != null; p = p.getParent()) {
                 if (mPlayingTransitions.get(i).mParticipants.contains(p)) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 1a3138d..7c5afa8 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -126,12 +126,18 @@
         }
 
         mFindResults.resetTopWallpaper = true;
-        if (w.mActivityRecord != null && !w.mActivityRecord.isVisible()
-                && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) {
-
-            // If this window's app token is hidden and not animating, it is of no interest to us.
-            if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w);
-            return false;
+        if (mService.mAtmService.getTransitionController().getTransitionPlayer() == null) {
+            if (w.mActivityRecord != null && !w.mActivityRecord.isVisible()
+                    && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) {
+                // If this window's app token is hidden and not animating, it is of no interest.
+                if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w);
+                return false;
+            }
+        } else {
+            if (w.mActivityRecord != null && !w.mActivityRecord.isVisibleRequested()) {
+                // An activity that is not going to remain visible shouldn't be the target.
+                return false;
+            }
         }
         if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen()
                 + " mDrawState=" + w.mWinAnimator.mDrawState);
@@ -227,7 +233,10 @@
     }
 
     boolean isWallpaperVisible() {
-        return isWallpaperVisible(mWallpaperTarget);
+        for (int i = mWallpaperTokens.size() - 1; i >= 0; --i) {
+            if (mWallpaperTokens.get(i).isVisible()) return true;
+        }
+        return false;
     }
 
     /**
@@ -240,7 +249,7 @@
         }
     }
 
-    private boolean isWallpaperVisible(WindowState wallpaperTarget) {
+    private boolean shouldWallpaperBeVisible(WindowState wallpaperTarget) {
         if (DEBUG_WALLPAPER) {
             Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + " prev="
                     + mPrevWallpaperTarget);
@@ -255,18 +264,18 @@
     }
 
     void updateWallpaperVisibility() {
-        final boolean visible = isWallpaperVisible(mWallpaperTarget);
+        final boolean visible = shouldWallpaperBeVisible(mWallpaperTarget);
 
         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
-            token.updateWallpaperVisibility(visible);
+            token.setVisibility(visible);
         }
     }
 
-    void hideDeferredWallpapersIfNeeded() {
-        if (mDeferredHideWallpaper != null) {
-            hideWallpapers(mDeferredHideWallpaper);
-            mDeferredHideWallpaper = null;
+    void hideDeferredWallpapersIfNeededLegacy() {
+        for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
+            final WallpaperWindowToken token = mWallpaperTokens.get(i);
+            token.commitVisibility(false);
         }
     }
 
@@ -275,18 +284,9 @@
                 && (mWallpaperTarget != winGoingAway || mPrevWallpaperTarget != null)) {
             return;
         }
-        if (mWallpaperTarget != null
-                && mWallpaperTarget.getDisplayContent().mAppTransition.isRunning()) {
-            // Defer hiding the wallpaper when app transition is running until the animations
-            // are done.
-            mDeferredHideWallpaper = winGoingAway;
-            return;
-        }
-
-        final boolean wasDeferred = (mDeferredHideWallpaper == winGoingAway);
         for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
             final WallpaperWindowToken token = mWallpaperTokens.get(i);
-            token.hideWallpaperToken(wasDeferred, "hideWallpapers");
+            token.setVisibility(false);
             if (DEBUG_WALLPAPER_LIGHT && token.isVisible()) {
                 Slog.d(TAG, "Hiding wallpaper " + token
                         + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
@@ -616,7 +616,7 @@
 
         // The window is visible to the compositor...but is it visible to the user?
         // That is what the wallpaper cares about.
-        final boolean visible = mWallpaperTarget != null && isWallpaperVisible(mWallpaperTarget);
+        final boolean visible = mWallpaperTarget != null;
         if (DEBUG_WALLPAPER) {
             Slog.v(TAG, "Wallpaper visibility: " + visible + " at display "
                     + mDisplayContent.getDisplayId());
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 43303d4..717775605 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -19,7 +19,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -34,6 +34,8 @@
 import android.view.WindowManager;
 import android.view.animation.Animation;
 
+import com.android.internal.protolog.common.ProtoLog;
+
 import java.util.function.Consumer;
 
 /**
@@ -43,6 +45,8 @@
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperWindowToken" : TAG_WM;
 
+    private boolean mVisibleRequested = false;
+
     WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
             DisplayContent dc, boolean ownerCanManageAppTokens) {
         this(service, token, explicit, dc, ownerCanManageAppTokens, null /* options */);
@@ -57,18 +61,16 @@
     }
 
     @Override
+    WallpaperWindowToken asWallpaperToken() {
+        return this;
+    }
+
+    @Override
     void setExiting() {
         super.setExiting();
         mDisplayContent.mWallpaperController.removeWallpaperToken(this);
     }
 
-    void hideWallpaperToken(boolean wasDeferred, String reason) {
-        for (int j = mChildren.size() - 1; j >= 0; j--) {
-            final WindowState wallpaper = mChildren.get(j);
-            wallpaper.hideWallpaperWindow(wasDeferred, reason);
-        }
-    }
-
     void sendWindowWallpaperCommand(
             String action, int x, int y, int z, Bundle extras, boolean sync) {
         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
@@ -93,24 +95,6 @@
         }
     }
 
-    void updateWallpaperVisibility(boolean visible) {
-        if (isVisible() != visible) {
-            mWmService.mAtmService.getTransitionController().collect(this);
-            // Need to do a layout to ensure the wallpaper now has the correct size.
-            mDisplayContent.setLayoutNeeded();
-        }
-
-        final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
-        for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
-            final WindowState wallpaper = mChildren.get(wallpaperNdx);
-            if (visible) {
-                wallpaperController.updateWallpaperOffset(wallpaper, false /* sync */);
-            }
-
-            wallpaper.dispatchWallpaperVisibility(visible);
-        }
-    }
-
     /**
      * Starts {@param anim} on all children.
      */
@@ -122,16 +106,16 @@
     }
 
     void updateWallpaperWindows(boolean visible) {
-
         if (isVisible() != visible) {
             if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
                     "Wallpaper token " + token + " visible=" + visible);
-            mWmService.mAtmService.getTransitionController().collect(this);
-            // Need to do a layout to ensure the wallpaper now has the correct size.
-            mDisplayContent.setLayoutNeeded();
+            setVisibility(visible);
+        }
+        final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+        if (mWmService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+            return;
         }
 
-        final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
         final WindowState wallpaperTarget = wallpaperController.getWallpaperTarget();
 
         if (visible && wallpaperTarget != null) {
@@ -153,21 +137,54 @@
             }
         }
 
-        for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
-            final WindowState wallpaper = mChildren.get(wallpaperNdx);
+        setVisible(visible);
+    }
 
-            if (visible) {
-                wallpaperController.updateWallpaperOffset(wallpaper, false /* sync */);
+    private void setVisible(boolean visible) {
+        final boolean wasClientVisible = isClientVisible();
+        setClientVisible(visible);
+        if (visible && !wasClientVisible) {
+            for (int i = mChildren.size() - 1; i >= 0; i--) {
+                final WindowState wallpaper = mChildren.get(i);
+                wallpaper.requestUpdateWallpaperIfNeeded();
             }
-
-            // First, make sure the client has the current visibility state.
-            wallpaper.dispatchWallpaperVisibility(visible);
-
-            if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win "
-                    + wallpaper);
         }
     }
 
+    /**
+     * Sets the requested visibility of this token. The visibility may not be if this is part of a
+     * transition. In that situation, make sure to call {@link #commitVisibility} when done.
+     */
+    void setVisibility(boolean visible) {
+        // Before setting mVisibleRequested so we can track changes.
+        mWmService.mAtmService.getTransitionController().collect(this);
+
+        setVisibleRequested(visible);
+
+        // If in a transition, defer commits for activities that are going invisible
+        if (!visible && (mWmService.mAtmService.getTransitionController().inTransition()
+                || getDisplayContent().mAppTransition.isRunning())) {
+            return;
+        }
+
+        commitVisibility(visible);
+    }
+
+    /**
+     * Commits the visibility of this token. This will directly update the visibility without
+     * regard for other state (like being in a transition).
+     */
+    void commitVisibility(boolean visible) {
+        if (visible == isVisible()) return;
+
+        ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS,
+                "commitVisibility: %s: visible=%b mVisibleRequested=%b", this,
+                isVisible(), mVisibleRequested);
+
+        setVisibleRequested(visible);
+        setVisible(visible);
+    }
+
     @Override
     void adjustWindowParams(WindowState win, WindowManager.LayoutParams attrs) {
         if (attrs.height == ViewGroup.LayoutParams.MATCH_PARENT
@@ -186,9 +203,10 @@
     }
 
     boolean hasVisibleNotDrawnWallpaper() {
+        if (!isVisible()) return false;
         for (int j = mChildren.size() - 1; j >= 0; --j) {
             final WindowState wallpaper = mChildren.get(j);
-            if (wallpaper.hasVisibleNotDrawnWallpaper()) {
+            if (!wallpaper.isDrawn() && wallpaper.isVisible()) {
                 return true;
             }
         }
@@ -210,6 +228,21 @@
         return false;
     }
 
+    void setVisibleRequested(boolean visible) {
+        if (mVisibleRequested == visible) return;
+        mVisibleRequested = visible;
+        setInsetsFrozen(!visible);
+    }
+
+    @Override
+    boolean isVisibleRequested() {
+        return mVisibleRequested;
+    }
+
+    @Override
+    boolean isVisible() {
+        return isClientVisible();
+    }
 
     @Override
     public String toString() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 000889a..0c4ff2f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3060,6 +3060,11 @@
     }
 
     /** Cheap way of doing cast and instanceof. */
+    WallpaperWindowToken asWallpaperToken() {
+        return null;
+    }
+
+    /** Cheap way of doing cast and instanceof. */
     DisplayArea asDisplayArea() {
         return null;
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 70b0f58..04a560b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -463,12 +463,12 @@
     private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000;
 
     /**
-     * Override of task letterbox aspect ratio that is set via ADB with
-     * set-task-letterbox-aspect-ratio or via {@link
-     * com.android.internal.R.dimen.config_taskLetterboxAspectRatio} will be ignored
+     * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
+     * set-fixed-orientation-letterbox-aspect-ratio or via {@link
+     * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored
      * if it is <= this value.
      */
-    static final float MIN_TASK_LETTERBOX_ASPECT_RATIO = 1.0f;
+    static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f;
 
     @VisibleForTesting
     WindowManagerConstants mConstants;
@@ -1003,9 +1003,9 @@
     private boolean mAnimationsDisabled = false;
     boolean mPointerLocationEnabled = false;
 
-    // Aspect ratio of task level letterboxing, values <= MIN_TASK_LETTERBOX_ASPECT_RATIO will be
-    // ignored.
-    private volatile float mTaskLetterboxAspectRatio;
+    // Aspect ratio of letterbox for fixed orientation, values <=
+    // MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
+    private volatile float mFixedOrientationLetterboxAspectRatio;
 
     /** Enum for Letterbox background type. */
     @Retention(RetentionPolicy.SOURCE)
@@ -1256,8 +1256,8 @@
         mAssistantOnTopOfDream = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_assistantOnTopOfDream);
 
-        mTaskLetterboxAspectRatio = context.getResources().getFloat(
-                com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
+        mFixedOrientationLetterboxAspectRatio = context.getResources().getFloat(
+                com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
         mLetterboxActivityCornersRadius = context.getResources().getInteger(
                 com.android.internal.R.integer.config_letterboxActivityCornersRadius);
         mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor(
@@ -3853,29 +3853,29 @@
     }
 
     /**
-     * Overrides the aspect ratio of task level letterboxing. If given value is <= {@link
-     * #MIN_TASK_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
-     * com.android.internal.R.dimen.config_taskLetterboxAspectRatio} will be ignored and
+     * Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
+     * #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
+     * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
      * the framework implementation will be used to determine the aspect ratio.
      */
-    void setTaskLetterboxAspectRatio(float aspectRatio) {
-        mTaskLetterboxAspectRatio = aspectRatio;
+    void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
+        mFixedOrientationLetterboxAspectRatio = aspectRatio;
     }
 
     /**
-     * Resets the aspect ratio of task level letterboxing to {@link
-     * com.android.internal.R.dimen.config_taskLetterboxAspectRatio}.
+     * Resets the aspect ratio of letterbox for fixed orientation to {@link
+     * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
      */
-    void resetTaskLetterboxAspectRatio() {
-        mTaskLetterboxAspectRatio = mContext.getResources().getFloat(
-                com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
+    void resetFixedOrientationLetterboxAspectRatio() {
+        mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
     }
 
     /**
-     * Gets the aspect ratio of task level letterboxing.
+     * Gets the aspect ratio of letterbox for fixed orientation.
      */
-    float getTaskLetterboxAspectRatio() {
-        return mTaskLetterboxAspectRatio;
+    float getFixedOrientationLetterboxAspectRatio() {
+        return mFixedOrientationLetterboxAspectRatio;
     }
 
     /**
@@ -3971,6 +3971,21 @@
                     ? backgroundType : LETTERBOX_BACKGROUND_SOLID_COLOR;
     }
 
+    /** Returns a string representing the given {@link LetterboxBackgroundType}. */
+    static String letterboxBackgroundTypeToString(
+            @LetterboxBackgroundType int backgroundType) {
+        switch (backgroundType) {
+            case LETTERBOX_BACKGROUND_SOLID_COLOR:
+                return "LETTERBOX_BACKGROUND_SOLID_COLOR";
+            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+                return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND";
+            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+                return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING";
+            default:
+                return "unknown=" + backgroundType;
+        }
+    }
+
     @Override
     public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
         if (!checkCallingPermission(
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 645786c..a46a8d5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -117,10 +117,10 @@
                     return runGetIgnoreOrientationRequest(pw);
                 case "dump-visible-window-views":
                     return runDumpVisibleWindowViews(pw);
-                case "set-task-letterbox-aspect-ratio":
-                    return runSetTaskLetterboxAspectRatio(pw);
-                case "get-task-letterbox-aspect-ratio":
-                    return runGetTaskLetterboxAspectRatio(pw);
+                case "set-fixed-orientation-letterbox-aspect-ratio":
+                    return runSetFixedOrientationLetterboxAspectRatio(pw);
+                case "get-fixed-orientation-letterbox-aspect-ratio":
+                    return runGetFixedOrientationLetterboxAspectRatio(pw);
                 case "set-letterbox-activity-corners-radius":
                     return runSetLetterboxActivityCornersRadius(pw);
                 case "get-letterbox-activity-corners-radius":
@@ -531,12 +531,12 @@
         return 0;
     }
 
-    private int runSetTaskLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
+    private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
         final float aspectRatio;
         try {
             String arg = getNextArgRequired();
             if ("reset".equals(arg)) {
-                mInternal.resetTaskLetterboxAspectRatio();
+                mInternal.resetFixedOrientationLetterboxAspectRatio();
                 return 0;
             }
             aspectRatio = Float.parseFloat(arg);
@@ -549,13 +549,13 @@
             return -1;
         }
 
-        mInternal.setTaskLetterboxAspectRatio(aspectRatio);
+        mInternal.setFixedOrientationLetterboxAspectRatio(aspectRatio);
         return 0;
     }
 
-    private int runGetTaskLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
-        final float aspectRatio = mInternal.getTaskLetterboxAspectRatio();
-        if (aspectRatio <= WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO) {
+    private int runGetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
+        final float aspectRatio = mInternal.getFixedOrientationLetterboxAspectRatio();
+        if (aspectRatio <= WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
             pw.println("Letterbox aspect ratio is not set");
         } else {
             pw.println("Letterbox aspect ratio is " + aspectRatio);
@@ -692,8 +692,8 @@
         // set-ignore-orientation-request
         mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
 
-        // set-task-letterbox-aspect-ratio
-        mInternal.resetTaskLetterboxAspectRatio();
+        // set-fixed-orientation-letterbox-aspect-ratio
+        mInternal.resetFixedOrientationLetterboxAspectRatio();
 
         // set-letterbox-activity-corners-radius
         mInternal.resetLetterboxActivityCornersRadius();
@@ -734,12 +734,12 @@
         pw.println("  set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
         pw.println("  get-ignore-orientation-request [-d DISPLAY_ID] ");
         pw.println("    If app requested orientation should be ignored.");
-        pw.println("  set-task-letterbox-aspect-ratio [reset|aspectRatio]");
-        pw.println("  get-task-letterbox-aspect-ratio");
-        pw.println("    Aspect ratio of task level letterboxing. If aspectRatio <= "
-                + WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO);
-        pw.println("    both it and R.dimen.config_taskLetterboxAspectRatio will be ignored");
-        pw.println("    and framework implementation will be used to determine aspect ratio.");
+        pw.println("  set-fixed-orientation-letterbox-aspect-ratio [reset|aspectRatio]");
+        pw.println("  get-fixed-orientation-letterbox-aspect-ratio");
+        pw.println("    Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
+                + WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+        pw.println("    both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will be");
+        pw.println("    ignored and framework implementation will determine aspect ratio.");
         pw.println("  set-letterbox-activity-corners-radius [reset|cornersRadius]");
         pw.println("  get-letterbox-activity-corners-radius");
         pw.println("    Corners radius for activities in the letterbox mode. If radius < 0,");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fec715e..1830c07 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -141,11 +141,9 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
@@ -358,7 +356,6 @@
     private boolean mForceHideNonSystemOverlayWindow;
     boolean mAppFreezing;
     boolean mHidden = true;    // Used to determine if to show child windows.
-    boolean mWallpaperVisible;  // for wallpaper, what was last vis report?
     private boolean mDragResizing;
     private boolean mDragResizingChangeReported = true;
     private int mResizeMode;
@@ -1715,7 +1712,11 @@
 
     @Override
     boolean isVisibleRequested() {
-        return isVisible() && (mActivityRecord == null || mActivityRecord.isVisibleRequested());
+        if (mToken != null && (mActivityRecord != null || mToken.asWallpaperToken() != null)) {
+            // Currently only ActivityRecord and WallpaperToken support visibleRequested.
+            return isVisible() && mToken.isVisibleRequested();
+        }
+        return isVisible();
     }
 
     /**
@@ -1745,8 +1746,11 @@
      *         {@code false} otherwise.
      */
     boolean wouldBeVisibleIfPolicyIgnored() {
-        return mHasSurface && !isParentWindowHidden()
-                && !mAnimatingExit && !mDestroying && (!mIsWallpaper || mWallpaperVisible);
+        if (!mHasSurface || isParentWindowHidden() || mAnimatingExit || mDestroying) {
+            return false;
+        }
+        final boolean isWallpaper = mToken != null && mToken.asWallpaperToken() != null;
+        return !isWallpaper || mToken.isVisible();
     }
 
     /**
@@ -1804,6 +1808,10 @@
             return ((!isParentWindowHidden() && atoken.isVisible())
                     || isAnimating(TRANSITION | PARENTS));
         }
+        final WallpaperWindowToken wtoken = mToken.asWallpaperToken();
+        if (wtoken != null) {
+            return !isParentWindowHidden() && wtoken.isVisible();
+        }
         return !isParentWindowHidden() || isAnimating(TRANSITION | PARENTS);
     }
 
@@ -1943,8 +1951,9 @@
         // When there is keyguard, wallpaper could be placed over the secure app
         // window but invisible. We need to check wallpaper visibility explicitly
         // to determine if it's occluding apps.
-        return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE)
-                || (mIsWallpaper && mWallpaperVisible))
+        final boolean isWallpaper = mToken != null && mToken.asWallpaperToken() != null;
+        return ((!isWallpaper && mAttrs.format == PixelFormat.OPAQUE)
+                || (isWallpaper && mToken.isVisible()))
                 && isDrawn() && !isAnimating(TRANSITION | PARENTS);
     }
 
@@ -3224,7 +3233,11 @@
     void sendAppVisibilityToClients() {
         super.sendAppVisibilityToClients();
 
-        final boolean clientVisible = mActivityRecord.isClientVisible();
+        if (mToken == null) return;
+
+        final boolean clientVisible = mToken.isClientVisible();
+        // TODO(shell-transitions): This is currently only applicable to app windows, BUT we
+        //                          want to extend the "starting" concept to other windows.
         if (mAttrs.type == TYPE_APPLICATION_STARTING && !clientVisible) {
             // Don't hide the starting window.
             return;
@@ -3608,9 +3621,11 @@
         if (mActivityRecord != null && mActivityRecord.isRelaunching()) {
             return;
         }
-        // If the activity is invisible or going invisible, don't report either since it is going
-        // away. This is likely during a transition so we want to preserve the original state.
-        if (mActivityRecord != null && !mActivityRecord.isVisibleRequested()) {
+        // If this is an activity or wallpaper and is invisible or going invisible, don't report
+        // either since it is going away. This is likely during a transition so we want to preserve
+        // the original state.
+        if ((mActivityRecord != null || mToken.asWallpaperToken() != null)
+                && !mToken.isVisibleRequested()) {
             return;
         }
 
@@ -4024,8 +4039,7 @@
         if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) {
             pw.println(prefix + "mIsImWindow=" + mIsImWindow
                     + " mIsWallpaper=" + mIsWallpaper
-                    + " mIsFloatingLayer=" + mIsFloatingLayer
-                    + " mWallpaperVisible=" + mWallpaperVisible);
+                    + " mIsFloatingLayer=" + mIsFloatingLayer);
         }
         if (dumpAll) {
             pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
@@ -4839,61 +4853,6 @@
         return getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
     }
 
-    void hideWallpaperWindow(boolean wasDeferred, String reason) {
-        for (int j = mChildren.size() - 1; j >= 0; --j) {
-            final WindowState c = mChildren.get(j);
-            c.hideWallpaperWindow(wasDeferred, reason);
-        }
-        if (!mWinAnimator.mLastHidden || wasDeferred) {
-            mWinAnimator.hide(getGlobalTransaction(), reason);
-            getDisplayContent().mWallpaperController.mDeferredHideWallpaper = null;
-            dispatchWallpaperVisibility(false);
-            final DisplayContent displayContent = getDisplayContent();
-            if (displayContent != null) {
-                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-                if (DEBUG_LAYOUT_REPEATS) {
-                    mWmService.mWindowPlacerLocked.debugLayoutRepeats("hideWallpaperWindow " + this,
-                            displayContent.pendingLayoutChanges);
-                }
-            }
-        }
-    }
-
-    /**
-     * Check wallpaper window for visibility change and notify window if so.
-     * @param visible Current visibility.
-     */
-    void dispatchWallpaperVisibility(final boolean visible) {
-        final boolean hideAllowed =
-                getDisplayContent().mWallpaperController.mDeferredHideWallpaper == null;
-
-        // Only send notification if the visibility actually changed and we are not trying to hide
-        // the wallpaper when we are deferring hiding of the wallpaper.
-        if (mWallpaperVisible != visible && (hideAllowed || visible)) {
-            mWallpaperVisible = visible;
-            try {
-                if (DEBUG_VISIBILITY || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
-                        "Updating vis of wallpaper " + this
-                                + ": " + visible + " from:\n" + Debug.getCallers(4, "  "));
-                mClient.dispatchAppVisibility(visible);
-            } catch (RemoteException e) {
-            }
-        }
-    }
-
-    boolean hasVisibleNotDrawnWallpaper() {
-        if (mWallpaperVisible && !isDrawn()) {
-            return true;
-        }
-        for (int j = mChildren.size() - 1; j >= 0; --j) {
-            final WindowState c = mChildren.get(j);
-            if (c.hasVisibleNotDrawnWallpaper()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void updateReportedVisibility(UpdateReportedVisibilityResults results) {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowState c = mChildren.get(i);
@@ -5246,7 +5205,7 @@
             mIsDimming = true;
             final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0;
             final int blurRadius = shouldDrawBlurBehind() ? mAttrs.getBlurBehindRadius() : 0;
-            getDimmer().dimBelow(getSyncTransaction(), this, mAttrs.dimAmount, blurRadius);
+            getDimmer().dimBelow(getSyncTransaction(), this, dimAmount, blurRadius);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ece256e..ebbebbb 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -57,7 +57,6 @@
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.Region;
 import android.os.Debug;
 import android.os.Trace;
 import android.util.Slog;
@@ -575,10 +574,7 @@
 
         setSurfaceBoundariesLocked(t);
 
-        if (mIsWallpaper && !w.mWallpaperVisible) {
-            // Wallpaper is no longer visible and there is no wp target => hide it.
-            hide(t, "prepareSurfaceLocked");
-        } else if (w.isParentWindowHidden() || !w.isOnScreen()) {
+        if (w.isParentWindowHidden() || !w.isOnScreen()) {
             hide(t, "prepareSurfaceLocked");
             mWallpaperControllerLocked.hideWallpapers(w);
 
@@ -631,9 +627,6 @@
                     if (showSurfaceRobustlyLocked(t)) {
                         mAnimator.requestRemovalOfReplacedWindows(w);
                         mLastHidden = false;
-                        if (mIsWallpaper) {
-                            w.dispatchWallpaperVisibility(true);
-                        }
                         final DisplayContent displayContent = w.getDisplayContent();
                         if (!displayContent.getLastHasContent()) {
                             // This draw means the difference between unique content and mirroring.
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 066cc1e..8867aa7 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -22,6 +22,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -112,6 +113,9 @@
      */
     private final boolean mFromClientToken;
 
+    /** Have we told the window clients to show themselves? */
+    private boolean mClientVisible;
+
     /**
      * Used to fix the transform of the token to be rotated to a rotation different than it's
      * display. The window frames and surfaces corresponding to this token will be layouted and
@@ -397,6 +401,21 @@
         return builder;
     }
 
+    boolean isClientVisible() {
+        return mClientVisible;
+    }
+
+    void setClientVisible(boolean clientVisible) {
+        if (mClientVisible == clientVisible) {
+            return;
+        }
+        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+                "setClientVisible: %s clientVisible=%b Callers=%s", this, clientVisible,
+                Debug.getCallers(5));
+        mClientVisible = clientVisible;
+        sendAppVisibilityToClients();
+    }
+
     boolean hasFixedRotationTransform() {
         return mFixedRotationTransformState != null;
     }
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 7b78b8d..8efbaf5 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -39,6 +39,8 @@
 
 #include "dataloader.h"
 
+// #define VERBOSE_READ_LOGS
+
 namespace android {
 
 namespace {
@@ -631,28 +633,65 @@
     };
 
     void onPageReadsWithUid(dataloader::PageReadsWithUid pageReads) final {
+        if (!pageReads.size()) {
+            return;
+        }
+
         auto trace = atrace_is_tag_enabled(ATRACE_TAG);
         if (CC_LIKELY(!trace)) {
             return;
         }
 
         TracedRead last = {};
+        auto lastSerialNo = mLastSerialNo;
         for (auto&& read : pageReads) {
-            if (read.id != last.fileId || read.uid != last.uid ||
-                read.block != last.firstBlockIdx + last.count) {
-                traceRead(last);
-                last = TracedRead{
-                        .timestampUs = read.bootClockTsUs,
-                        .fileId = read.id,
-                        .uid = read.uid,
-                        .firstBlockIdx = (uint32_t)read.block,
-                        .count = 1,
-                };
-            } else {
-                ++last.count;
+            const auto expectedSerialNo = lastSerialNo + last.count;
+#ifdef VERBOSE_READ_LOGS
+            {
+                FileIdx fileIdx = convertFileIdToFileIndex(read.id);
+
+                auto appId = multiuser_get_app_id(read.uid);
+                auto userId = multiuser_get_user_id(read.uid);
+                auto trace = android::base::
+                        StringPrintf("verbose_page_read: serialNo=%lld (expected=%lld) index=%lld "
+                                     "file=%d appid=%d userid=%d",
+                                     static_cast<long long>(read.serialNo),
+                                     static_cast<long long>(expectedSerialNo),
+                                     static_cast<long long>(read.block), static_cast<int>(fileIdx),
+                                     static_cast<int>(appId), static_cast<int>(userId));
+
+                ATRACE_BEGIN(trace.c_str());
+                ATRACE_END();
             }
+#endif // VERBOSE_READ_LOGS
+
+            if (read.serialNo == expectedSerialNo && read.id == last.fileId &&
+                read.uid == last.uid && read.block == last.firstBlockIdx + last.count) {
+                ++last.count;
+                continue;
+            }
+
+            // First, trace the reads.
+            traceRead(last);
+
+            // Second, report missing reads, if any.
+            if (read.serialNo != expectedSerialNo) {
+                const auto readsMissing = read.serialNo - expectedSerialNo;
+                traceMissingReads(readsMissing);
+            }
+
+            last = TracedRead{
+                    .timestampUs = read.bootClockTsUs,
+                    .fileId = read.id,
+                    .uid = read.uid,
+                    .firstBlockIdx = (uint32_t)read.block,
+                    .count = 1,
+            };
+            lastSerialNo = read.serialNo;
         }
+
         traceRead(last);
+        mLastSerialNo = lastSerialNo + last.count;
     }
 
     void traceRead(const TracedRead& read) {
@@ -682,6 +721,13 @@
         ATRACE_END();
     }
 
+    void traceMissingReads(int64_t count) {
+        const auto trace = android::base::StringPrintf("missing_page_reads: count=%lld",
+                                                       static_cast<long long>(count));
+        ATRACE_BEGIN(trace.c_str());
+        ATRACE_END();
+    }
+
     void receiver(unique_fd inout, MetadataMode mode) {
         std::vector<uint8_t> data;
         std::vector<IncFsDataBlock> instructions;
@@ -828,6 +874,7 @@
     std::atomic<bool> mStopReceiving = false;
     std::atomic<bool> mReadLogsEnabled = false;
     std::chrono::milliseconds mWaitOnEofInterval{WaitOnEofMinInterval};
+    int64_t mLastSerialNo{1};
     /** Tracks which files have been requested */
     std::unordered_set<FileIdx> mRequestedFiles;
 };
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 89b931d..ef2d0ba 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -238,12 +238,12 @@
     return result.isOk() ? result.value().count() : -1;
 }
 
-static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
-                                          jobjectArray composition, jlong vibrationId) {
+static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                           jobjectArray composition, jlong vibrationId) {
     VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
     if (wrapper == nullptr) {
         ALOGE("vibratorPerformComposedEffect failed because native wrapper was not initialized");
-        return;
+        return -1;
     }
     size_t size = env->GetArrayLength(composition);
     std::vector<aidl::CompositeEffect> effects;
@@ -252,7 +252,8 @@
         effects.push_back(effectFromJavaPrimitive(env, element));
     }
     auto callback = wrapper->createCallback(vibrationId);
-    wrapper->hal()->performComposedEffect(effects, callback);
+    auto result = wrapper->hal()->performComposedEffect(effects, callback);
+    return result.isOk() ? result.value().count() : -1;
 }
 
 static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong ptr) {
@@ -296,7 +297,7 @@
         {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
         {"vibratorPerformEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
         {"vibratorPerformComposedEffect",
-         "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)V",
+         "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)J",
          (void*)vibratorPerformComposedEffect},
         {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
         {"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 28e9acf..04af5c9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1583,8 +1583,7 @@
         }
 
         public String[] getPersonalAppsForSuspension(int userId) {
-            return new PersonalAppsSuspensionHelper(
-                    mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */))
+            return PersonalAppsSuspensionHelper.forUser(mContext, userId)
                     .getPersonalAppsForSuspension();
         }
 
@@ -1599,6 +1598,10 @@
         void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
             mSafetyChecker = safetyChecker;
         }
+
+        void dumpPerUserData(IndentingPrintWriter pw, @UserIdInt int userId) {
+            PersonalAppsSuspensionHelper.forUser(mContext, userId).dump(pw);
+        }
     }
 
     /**
@@ -9161,11 +9164,17 @@
         }
     }
 
-    private void dumpDevicePolicyData(IndentingPrintWriter pw) {
+    private void dumpPerUserData(IndentingPrintWriter pw) {
         int userCount = mUserData.size();
-        for (int u = 0; u < userCount; u++) {
-            DevicePolicyData policy = getUserData(mUserData.keyAt(u));
+        for (int userId = 0; userId < userCount; userId++) {
+            DevicePolicyData policy = getUserData(mUserData.keyAt(userId));
             policy.dump(pw);
+            pw.println();
+
+            pw.increaseIndent();
+            mInjector.dumpPerUserData(pw, userId);
+            pw.decreaseIndent();
+            pw.println();
         }
     }
 
@@ -9183,7 +9192,7 @@
                 pw.println();
                 mDeviceAdminServiceController.dump(pw);
                 pw.println();
-                dumpDevicePolicyData(pw);
+                dumpPerUserData(pw);
                 pw.println();
                 mConstants.dump(pw);
                 pw.println();
@@ -9229,20 +9238,30 @@
         pw.increaseIndent();
         dumpResources(pw, mContext, "cross_profile_apps", R.array.cross_profile_apps);
         dumpResources(pw, mContext, "vendor_cross_profile_apps", R.array.vendor_cross_profile_apps);
+        dumpResources(pw, mContext, "config_packagesExemptFromSuspension",
+                R.array.config_packagesExemptFromSuspension);
         pw.decreaseIndent();
         pw.println();
     }
 
     static void dumpResources(IndentingPrintWriter pw, Context context, String resName, int resId) {
-        String[] apps = context.getResources().getStringArray(resId);
-        if (apps == null || apps.length == 0) {
-            pw.printf("%s: empty\n", resName);
+        dumpApps(pw, resName, context.getResources().getStringArray(resId));
+    }
+
+    static void dumpApps(IndentingPrintWriter pw, String name, String[] apps) {
+        dumpApps(pw, name, Arrays.asList(apps));
+    }
+
+    static void dumpApps(IndentingPrintWriter pw, String name, List apps) {
+        if (apps == null || apps.isEmpty()) {
+            pw.printf("%s: empty\n", name);
             return;
         }
-        pw.printf("%s: %d app%s\n", resName, apps.length, apps.length == 1 ? "" : "s");
+        int size = apps.size();
+        pw.printf("%s: %d app%s\n", name, size, size == 1 ? "" : "s");
         pw.increaseIndent();
-        for (int i = 0; i < apps.length; i++) {
-            pw.printf("%d: %s\n", i, apps[i]);
+        for (int i = 0; i < size; i++) {
+            pw.printf("%d: %s\n", i, apps.get(i));
         }
         pw.decreaseIndent();
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index c6871842..37dbfc1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -20,6 +20,7 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -29,10 +30,12 @@
 import android.content.pm.ResolveInfo;
 import android.os.IBinder;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Telephony;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManager;
@@ -49,7 +52,7 @@
 /**
  * Utility class to find what personal apps should be suspended to limit personal device use.
  */
-public class PersonalAppsSuspensionHelper {
+public final class PersonalAppsSuspensionHelper {
     private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
 
     // Flags to get all packages even if the user is still locked.
@@ -60,9 +63,17 @@
     private final PackageManager mPackageManager;
 
     /**
+     * Factory method
+     */
+    public static PersonalAppsSuspensionHelper forUser(Context context, @UserIdInt int userId) {
+        return new PersonalAppsSuspensionHelper(context.createContextAsUser(UserHandle.of(userId),
+                /* flags= */ 0));
+    }
+
+    /**
      * @param context Context for the user whose apps should to be suspended.
      */
-    public PersonalAppsSuspensionHelper(Context context) {
+    private PersonalAppsSuspensionHelper(Context context) {
         mContext = context;
         mPackageManager = context.getPackageManager();
     }
@@ -181,4 +192,21 @@
                 iBinder == null ? null : IAccessibilityManager.Stub.asInterface(iBinder);
         return new AccessibilityManager(mContext, service, userId);
     }
+
+    void dump(IndentingPrintWriter pw) {
+        pw.println("PersonalAppsSuspensionHelper");
+        pw.increaseIndent();
+
+        DevicePolicyManagerService.dumpApps(pw, "critical packages", getCriticalPackages());
+        DevicePolicyManagerService.dumpApps(pw, "launcher packages", getSystemLauncherPackages());
+        DevicePolicyManagerService.dumpApps(pw, "accessibility services",
+                getAccessibilityServices());
+        DevicePolicyManagerService.dumpApps(pw, "input method packages", getInputMethodPackages());
+        pw.printf("SMS package: %s\n", Telephony.Sms.getDefaultSmsPackage(mContext));
+        pw.printf("Settings package: %s\n", getSettingsPackageName());
+        DevicePolicyManagerService.dumpApps(pw, "Packages subject to suspension",
+                getPersonalAppsForSuspension());
+
+        pw.decreaseIndent();
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a3d3353..5fbf1c4 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -53,7 +53,6 @@
 import android.hardware.display.DisplayManagerInternal;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityModuleConnector;
-import android.net.IConnectivityManager;
 import android.net.NetworkStackClient;
 import android.os.BaseBundle;
 import android.os.Binder;
@@ -1307,7 +1306,6 @@
         VcnManagementService vcnManagement = null;
         NetworkStatsService networkStats = null;
         NetworkPolicyManagerService networkPolicy = null;
-        IConnectivityManager connectivity = null;
         NsdService serviceDiscovery = null;
         WindowManagerService wm = null;
         SerialService serial = null;
@@ -1882,10 +1880,7 @@
             // services to initialize.
             mSystemServiceManager.startServiceFromJar(CONNECTIVITY_SERVICE_INITIALIZER_CLASS,
                     CONNECTIVITY_SERVICE_APEX_PATH);
-            connectivity = IConnectivityManager.Stub.asInterface(
-                    ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-            // TODO: Use ConnectivityManager instead of ConnectivityService.
-            networkPolicy.bindConnectivityManager(connectivity);
+            networkPolicy.bindConnectivityManager();
             t.traceEnd();
 
             t.traceBegin("StartVpnManagerService");
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
index 9447f39..8ef9239 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
@@ -19,7 +19,7 @@
 import android.content.pm.verify.domain.DomainSet
 import android.content.pm.verify.domain.DomainVerificationInfo
 import android.content.pm.verify.domain.DomainVerificationRequest
-import android.content.pm.verify.domain.DomainVerificationUserSelection
+import android.content.pm.verify.domain.DomainVerificationUserState
 import android.os.Parcel
 import android.os.Parcelable
 import android.os.UserHandle
@@ -28,7 +28,6 @@
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 import java.util.UUID
-import kotlin.random.Random
 
 @RunWith(Parameterized::class)
 class DomainVerificationCoreApiTest {
@@ -92,9 +91,9 @@
                 }
             ),
             Parameter(
-                testName = "DomainVerificationUserSelection",
+                testName = "DomainVerificationUserState",
                 initial = {
-                    DomainVerificationUserSelection(
+                    DomainVerificationUserState(
                         UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
                         "com.test.pkg",
                         UserHandle.of(10),
@@ -103,22 +102,22 @@
                             .associate { it.value to (it.index % 3) }
                     )
                 },
-                unparcel = { DomainVerificationUserSelection.CREATOR.createFromParcel(it) },
+                unparcel = { DomainVerificationUserState.CREATOR.createFromParcel(it) },
                 assertion = { first, second ->
-                    assertAll<DomainVerificationUserSelection, UUID>(first, second,
+                    assertAll<DomainVerificationUserState, UUID>(first, second,
                         { it.identifier }, { it.component1() }, IS_EQUAL_TO
                     )
-                    assertAll<DomainVerificationUserSelection, String>(first, second,
+                    assertAll<DomainVerificationUserState, String>(first, second,
                         { it.packageName }, { it.component2() }, IS_EQUAL_TO
                     )
-                    assertAll<DomainVerificationUserSelection, UserHandle>(first, second,
+                    assertAll<DomainVerificationUserState, UserHandle>(first, second,
                         { it.user }, { it.component3() }, IS_EQUAL_TO
                     )
-                    assertAll<DomainVerificationUserSelection, Boolean>(
+                    assertAll<DomainVerificationUserState, Boolean>(
                         first, second, { it.isLinkHandlingAllowed },
                         { it.component4() }, IS_EQUAL_TO
                     )
-                    assertAll<DomainVerificationUserSelection, Map<String, Int>>(
+                    assertAll<DomainVerificationUserState, Map<String, Int>>(
                         first, second, { it.hostToStateMap },
                         { it.component5() }, IS_MAP_EQUAL_TO
                     )
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 89394837..53f0ca2 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -155,6 +155,15 @@
                     assertApprovedVerifier(it.callingUid, it.proxy)
                 },
                 enforcer(
+                    Type.SELECTION_QUERENT,
+                    "approvedUserStateQuerent"
+                ) {
+                    assertApprovedUserStateQuerent(
+                        it.callingUid, it.callingUserId,
+                        it.targetPackageName, it.userId
+                    )
+                },
+                enforcer(
                     Type.SELECTOR,
                     "approvedUserSelector"
                 ) {
@@ -170,7 +179,7 @@
                         ArraySet(setOf("example.com"))
                     )
                 },
-                service(Type.INTERNAL, "setUserSelectionInternal") {
+                service(Type.INTERNAL, "setUserStateInternal") {
                     setDomainVerificationUserSelectionInternal(
                         it.userId,
                         it.targetPackageName,
@@ -184,11 +193,11 @@
                 service(Type.INTERNAL, "clearState") {
                     clearDomainVerificationState(listOf(it.targetPackageName))
                 },
-                service(Type.INTERNAL, "clearUserSelections") {
-                    clearUserSelections(listOf(it.targetPackageName), it.userId)
+                service(Type.INTERNAL, "clearUserStates") {
+                    clearUserStates(listOf(it.targetPackageName), it.userId)
                 },
-                service(Type.VERIFIER, "getPackageNames") {
-                    validVerificationPackageNames
+                service(Type.VERIFIER, "queryValidPackageNames") {
+                    queryValidVerificationPackageNames()
                 },
                 service(Type.QUERENT, "getInfo") {
                     getDomainVerificationInfo(it.targetPackageName)
@@ -208,26 +217,13 @@
                         DomainVerificationManager.STATE_SUCCESS
                     )
                 },
-                service(Type.SELECTOR, "setLinkHandlingAllowed") {
-                    setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true)
-                },
                 service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") {
                     setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true, it.userId)
                 },
-                service(Type.SELECTOR, "getUserSelection") {
-                    getDomainVerificationUserSelection(it.targetPackageName)
+                service(Type.SELECTION_QUERENT, "getUserStateUserId") {
+                    getDomainVerificationUserState(it.targetPackageName, it.userId)
                 },
-                service(Type.SELECTOR_USER, "getUserSelectionUserId") {
-                    getDomainVerificationUserSelection(it.targetPackageName, it.userId)
-                },
-                service(Type.SELECTOR, "setUserSelection") {
-                    setDomainVerificationUserSelection(
-                        it.targetDomainSetId,
-                        setOf("example.com"),
-                        true
-                    )
-                },
-                service(Type.SELECTOR_USER, "setUserSelectionUserId") {
+                service(Type.SELECTOR_USER, "setUserStateUserId") {
                     setDomainVerificationUserSelection(
                         it.targetDomainSetId,
                         setOf("example.com"),
@@ -244,10 +240,6 @@
                 service(Type.LEGACY_QUERENT, "getLegacyUserState") {
                     getLegacyState(it.targetPackageName, it.userId)
                 },
-                service(Type.OWNER_QUERENT, "getOwnersForDomain") {
-                    // Re-use package name, since the result itself isn't relevant
-                    getOwnersForDomain(it.targetPackageName)
-                },
                 service(Type.OWNER_QUERENT_USER, "getOwnersForDomainUserId") {
                     // Re-use package name, since the result itself isn't relevant
                     getOwnersForDomain(it.targetPackageName, it.userId)
@@ -362,6 +354,7 @@
             Type.INTERNAL -> internal()
             Type.QUERENT -> approvedQuerent()
             Type.VERIFIER -> approvedVerifier()
+            Type.SELECTION_QUERENT -> approvedUserStateQuerent(verifyCrossUser = true)
             Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false)
             Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true)
             Type.LEGACY_QUERENT -> legacyQuerent()
@@ -371,7 +364,7 @@
         }.run { /*exhaust*/ }
     }
 
-    fun internal() {
+    private fun internal() {
         val context: Context = mockThrowOnUnmocked()
         val target = params.construct(context)
 
@@ -385,13 +378,13 @@
         }
     }
 
-    fun approvedQuerent() {
-        val allowUserSelection = AtomicBoolean(false)
+    private fun approvedQuerent() {
+        val allowUserState = AtomicBoolean(false)
         val allowPreferredApps = AtomicBoolean(false)
         val allowQueryAll = AtomicBoolean(false)
         val context: Context = mockThrowOnUnmocked {
             initPermission(
-                allowUserSelection,
+                allowUserState,
                 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
             )
             initPermission(
@@ -418,7 +411,7 @@
 
         assertFails { runMethod(target, NON_VERIFIER_UID) }
 
-        allowUserSelection.set(true)
+        allowUserState.set(true)
 
         assertFails { runMethod(target, NON_VERIFIER_UID) }
 
@@ -427,7 +420,7 @@
         runMethod(target, NON_VERIFIER_UID)
     }
 
-    fun approvedVerifier() {
+    private fun approvedVerifier() {
         val allowDomainVerificationAgent = AtomicBoolean(false)
         val allowIntentVerificationAgent = AtomicBoolean(false)
         val allowQueryAll = AtomicBoolean(false)
@@ -469,12 +462,61 @@
         assertFails { runMethod(target, NON_VERIFIER_UID) }
     }
 
-    fun approvedUserSelector(verifyCrossUser: Boolean) {
-        val allowUserSelection = AtomicBoolean(false)
+    private fun approvedUserStateQuerent(verifyCrossUser: Boolean) {
         val allowInteractAcrossUsers = AtomicBoolean(false)
         val context: Context = mockThrowOnUnmocked {
             initPermission(
-                allowUserSelection,
+                allowInteractAcrossUsers,
+                android.Manifest.permission.INTERACT_ACROSS_USERS
+            )
+        }
+        val target = params.construct(context)
+
+        fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+            // User selector makes no distinction by UID
+            val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+            if (throws) {
+                allUids.forEach {
+                    assertFails {
+                        runMethod(target, it, visible = true, callingUserId, targetUserId)
+                    }
+                }
+            } else {
+                allUids.forEach {
+                    runMethod(target, it, visible = true, callingUserId, targetUserId)
+                }
+            }
+
+            // User selector doesn't use QUERY_ALL, so the invisible package should always fail
+            allUids.forEach {
+                assertFails {
+                    runMethod(target, it, visible = false, callingUserId, targetUserId)
+                }
+            }
+        }
+
+        val callingUserId = 0
+        val notCallingUserId = 1
+
+        runTestCases(callingUserId, callingUserId, throws = false)
+        if (verifyCrossUser) {
+            runTestCases(callingUserId, notCallingUserId, throws = true)
+        }
+
+        allowInteractAcrossUsers.set(true)
+
+        runTestCases(callingUserId, callingUserId, throws = false)
+        if (verifyCrossUser) {
+            runTestCases(callingUserId, notCallingUserId, throws = false)
+        }
+    }
+
+    private fun approvedUserSelector(verifyCrossUser: Boolean) {
+        val allowUserState = AtomicBoolean(false)
+        val allowInteractAcrossUsers = AtomicBoolean(false)
+        val context: Context = mockThrowOnUnmocked {
+            initPermission(
+                allowUserState,
                 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
             )
             initPermission(
@@ -515,7 +557,7 @@
             runTestCases(callingUserId, notCallingUserId, throws = true)
         }
 
-        allowUserSelection.set(true)
+        allowUserState.set(true)
 
         runTestCases(callingUserId, callingUserId, throws = false)
         if (verifyCrossUser) {
@@ -641,7 +683,7 @@
 
     private fun ownerQuerent(verifyCrossUser: Boolean) {
         val allowQueryAll = AtomicBoolean(false)
-        val allowUserSelection = AtomicBoolean(false)
+        val allowUserState = AtomicBoolean(false)
         val allowInteractAcrossUsers = AtomicBoolean(false)
         val context: Context = mockThrowOnUnmocked {
             initPermission(
@@ -649,7 +691,7 @@
                 android.Manifest.permission.QUERY_ALL_PACKAGES
             )
             initPermission(
-                allowUserSelection,
+                allowUserState,
                 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
             )
             initPermission(
@@ -690,7 +732,7 @@
             runTestCases(callingUserId, notCallingUserId, throws = true)
         }
 
-        allowUserSelection.set(true)
+        allowUserState.set(true)
 
         runTestCases(callingUserId, callingUserId, throws = false)
         if (verifyCrossUser) {
@@ -769,6 +811,9 @@
         // INTERNAL || domain verification agent
         VERIFIER,
 
+        // No permissions, allows all apps to view domain state for visible packages
+        SELECTION_QUERENT,
+
         // Holding the user setting permission
         SELECTOR,
 
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
index 439048c..8c31c65 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
@@ -18,7 +18,7 @@
 
 import android.content.pm.verify.domain.DomainVerificationRequest
 import android.content.pm.verify.domain.DomainVerificationInfo
-import android.content.pm.verify.domain.DomainVerificationUserSelection
+import android.content.pm.verify.domain.DomainVerificationUserState
 import com.android.server.pm.verify.domain.DomainVerificationPersistence
 
 operator fun <F> android.util.Pair<F, *>.component1() = first
@@ -30,11 +30,11 @@
 operator fun DomainVerificationInfo.component2() = packageName
 operator fun DomainVerificationInfo.component3() = hostToStateMap
 
-operator fun DomainVerificationUserSelection.component1() = identifier
-operator fun DomainVerificationUserSelection.component2() = packageName
-operator fun DomainVerificationUserSelection.component3() = user
-operator fun DomainVerificationUserSelection.component4() = isLinkHandlingAllowed
-operator fun DomainVerificationUserSelection.component5() = hostToStateMap
+operator fun DomainVerificationUserState.component1() = identifier
+operator fun DomainVerificationUserState.component2() = packageName
+operator fun DomainVerificationUserState.component3() = user
+operator fun DomainVerificationUserState.component4() = isLinkHandlingAllowed
+operator fun DomainVerificationUserState.component5() = hostToStateMap
 
 operator fun DomainVerificationPersistence.ReadResult.component1() = active
 operator fun DomainVerificationPersistence.ReadResult.component2() = restored
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
index a92ab9e..6597577 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
@@ -22,14 +22,15 @@
 import android.util.TypedXmlSerializer
 import android.util.Xml
 import com.android.server.pm.verify.domain.DomainVerificationPersistence
+import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState
 import com.android.server.pm.verify.domain.models.DomainVerificationPkgState
 import com.android.server.pm.verify.domain.models.DomainVerificationStateMap
-import com.android.server.pm.verify.domain.models.DomainVerificationUserState
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
+import org.xmlpull.v1.XmlPullParser
 import java.io.File
 import java.nio.charset.StandardCharsets
 import java.util.UUID
@@ -41,21 +42,41 @@
 
         internal fun File.writeXml(block: (serializer: TypedXmlSerializer) -> Unit) = apply {
             outputStream().use {
-                // Explicitly use string based XML so it can printed in the test failure output
-                Xml.newFastSerializer()
+                // This must use the binary serializer the mirror the production behavior, as
+                // there are slight differences with the string based one.
+                Xml.newBinarySerializer()
                     .apply {
                         setOutput(it, StandardCharsets.UTF_8.name())
                         startDocument(null, true)
                         setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true)
+                        // Write a wrapping tag to ensure the domain verification settings didn't
+                        // close out the document, allowing other settings to be written
+                        startTag(null, "wrapper-tag")
                     }
                     .apply(block)
+                    .apply {
+                        startTag(null, "trailing-tag")
+                        endTag(null, "trailing-tag")
+                        endTag(null, "wrapper-tag")
+                    }
                     .endDocument()
             }
         }
 
         internal fun <T> File.readXml(block: (parser: TypedXmlPullParser) -> T) =
             inputStream().use {
-                block(Xml.resolvePullParser(it))
+                val parser = Xml.resolvePullParser(it)
+                assertThat(parser.nextTag()).isEqualTo(XmlPullParser.START_TAG)
+                assertThat(parser.name).isEqualTo("wrapper-tag")
+                assertThat(parser.nextTag()).isEqualTo(XmlPullParser.START_TAG)
+                block(parser).also {
+                    assertThat(parser.nextTag()).isEqualTo(XmlPullParser.START_TAG)
+                    assertThat(parser.name).isEqualTo("trailing-tag")
+                    assertThat(parser.nextTag()).isEqualTo(XmlPullParser.END_TAG)
+                    assertThat(parser.name).isEqualTo("trailing-tag")
+                    assertThat(parser.nextTag()).isEqualTo(XmlPullParser.END_TAG)
+                    assertThat(parser.name).isEqualTo("wrapper-tag")
+                }
             }
     }
 
@@ -102,14 +123,14 @@
             // A domain without a written state falls back to default
             stateMap["missing-state.com"] = DomainVerificationManager.STATE_NO_RESPONSE
 
-            userSelectionStates[1] = DomainVerificationUserState(1).apply {
+            userStates[1] = DomainVerificationInternalUserState(1).apply {
                 addHosts(setOf("example-user1.com", "example-user1.org"))
                 isLinkHandlingAllowed = true
             }
         }
         val stateOne = mockEmptyPkgState(1).apply {
             // It's valid to have a user selection without any autoVerify domains
-            userSelectionStates[1] = DomainVerificationUserState(1).apply {
+            userStates[1] = DomainVerificationInternalUserState(1).apply {
                 addHosts(setOf("example-user1.com", "example-user1.org"))
                 isLinkHandlingAllowed = false
             }
@@ -214,7 +235,7 @@
 
     private fun mockPkgState(id: Int) = mockEmptyPkgState(id).apply {
         stateMap["$packageName.com"] = id
-        userSelectionStates[id] = DomainVerificationUserState(id).apply {
+        userStates[id] = DomainVerificationInternalUserState(id).apply {
             addHosts(setOf("$packageName-user.com"))
             isLinkHandlingAllowed = true
         }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 010eacf..0d8f275 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -122,8 +122,8 @@
                 service("clearState") {
                     clearDomainVerificationState(listOf(TEST_PKG))
                 },
-                service("clearUserSelections") {
-                    clearUserSelections(listOf(TEST_PKG), TEST_USER_ID)
+                service("clearUserStates") {
+                    clearUserStates(listOf(TEST_PKG), TEST_USER_ID)
                 },
                 service("setStatus") {
                     setDomainVerificationStatus(
@@ -147,19 +147,13 @@
                         DomainVerificationManager.STATE_SUCCESS
                     )
                 },
-                service("setLinkHandlingAllowed") {
-                    setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
-                },
                 service("setLinkHandlingAllowedUserId") {
                     setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, TEST_USER_ID)
                 },
                 service("setLinkHandlingAllowedInternal") {
                     setDomainVerificationLinkHandlingAllowedInternal(TEST_PKG, true, TEST_USER_ID)
                 },
-                service("setUserSelection") {
-                    setDomainVerificationUserSelection(TEST_UUID, setOf("example.com"), true)
-                },
-                service("setUserSelectionUserId") {
+                service("setUserStateUserId") {
                     setDomainVerificationUserSelection(
                         TEST_UUID,
                         setOf("example.com"),
@@ -167,7 +161,7 @@
                         TEST_USER_ID
                     )
                 },
-                service("setUserSelectionInternal") {
+                service("setUserStateInternal") {
                     setDomainVerificationUserSelectionInternal(
                         TEST_USER_ID,
                         TEST_PKG,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
similarity index 82%
rename from services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt
rename to services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 48056a2..0576125 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -16,18 +16,16 @@
 
 package com.android.server.pm.test.verify.domain
 
-import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.pm.parsing.component.ParsedActivity
 import android.content.pm.parsing.component.ParsedIntentInfo
 import android.content.pm.verify.domain.DomainVerificationManager
-import android.content.pm.verify.domain.DomainVerificationUserSelection
+import android.content.pm.verify.domain.DomainVerificationUserState
 import android.os.Build
 import android.os.PatternMatcher
 import android.os.Process
 import android.util.ArraySet
-import androidx.test.InstrumentationRegistry
 import com.android.server.pm.PackageSetting
 import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.pm.verify.domain.DomainVerificationService
@@ -41,7 +39,7 @@
 import org.mockito.ArgumentMatchers.anyString
 import java.util.UUID
 
-class DomainVerificationManagerUserSelectionOverrideTest {
+class DomainVerificationUserStateOverrideTest {
 
     companion object {
         private const val PKG_ONE = "com.test.one"
@@ -50,17 +48,19 @@
         private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
 
         private val DOMAIN_ONE =
-            DomainVerificationManagerUserSelectionOverrideTest::class.java.packageName
+            DomainVerificationUserStateOverrideTest::class.java.packageName
 
-        private const val STATE_NONE = DomainVerificationUserSelection.DOMAIN_STATE_NONE
-        private const val STATE_SELECTED = DomainVerificationUserSelection.DOMAIN_STATE_SELECTED
-        private const val STATE_VERIFIED = DomainVerificationUserSelection.DOMAIN_STATE_VERIFIED
+        private const val STATE_NONE = DomainVerificationUserState.DOMAIN_STATE_NONE
+        private const val STATE_SELECTED = DomainVerificationUserState.DOMAIN_STATE_SELECTED
+        private const val STATE_VERIFIED = DomainVerificationUserState.DOMAIN_STATE_VERIFIED
+
+        private const val USER_ID = 0
     }
 
     private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE)
     private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO)
 
-    fun makeManager(): DomainVerificationManager =
+    fun makeService() =
         DomainVerificationService(mockThrowOnUnmocked {
             // Assume the test has every permission necessary
             whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
@@ -88,7 +88,7 @@
             addPackage(pkg2)
 
             // Starting state for all tests is to have domain 1 enabled for the first package
-            setDomainVerificationUserSelection(UUID_ONE, setOf(DOMAIN_ONE), true)
+            setDomainVerificationUserSelection(UUID_ONE, setOf(DOMAIN_ONE), true, USER_ID)
 
             assertThat(stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_SELECTED)
         }
@@ -138,37 +138,37 @@
 
     @Test
     fun anotherPackageTakeoverSuccess() {
-        val manager = makeManager()
+        val service = makeService()
 
         // Attempt override by package 2
-        manager.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true)
+        service.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true, USER_ID)
 
         // 1 loses approval
-        assertThat(manager.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_NONE)
+        assertThat(service.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_NONE)
 
         // 2 gains approval
-        assertThat(manager.stateFor(PKG_TWO, DOMAIN_ONE)).isEqualTo(STATE_SELECTED)
+        assertThat(service.stateFor(PKG_TWO, DOMAIN_ONE)).isEqualTo(STATE_SELECTED)
 
         // 2 is the only owner
-        assertThat(manager.getOwnersForDomain(DOMAIN_ONE).map { it.packageName })
+        assertThat(service.getOwnersForDomain(DOMAIN_ONE, USER_ID).map { it.packageName })
             .containsExactly(PKG_TWO)
     }
 
     @Test(expected = IllegalArgumentException::class)
     fun anotherPackageTakeoverFailure() {
-        val manager = makeManager()
+        val service = makeService()
 
         // Verify 1 to give it a higher approval level
-        manager.setDomainVerificationStatus(UUID_ONE, setOf(DOMAIN_ONE),
+        service.setDomainVerificationStatus(UUID_ONE, setOf(DOMAIN_ONE),
             DomainVerificationManager.STATE_SUCCESS)
-        assertThat(manager.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_VERIFIED)
-        assertThat(manager.getOwnersForDomain(DOMAIN_ONE).map { it.packageName })
+        assertThat(service.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_VERIFIED)
+        assertThat(service.getOwnersForDomain(DOMAIN_ONE, USER_ID).map { it.packageName })
             .containsExactly(PKG_ONE)
 
         // Attempt override by package 2
-        manager.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true)
+        service.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true, USER_ID)
     }
 
-    private fun DomainVerificationManager.stateFor(pkgName: String, host: String) =
-        getDomainVerificationUserSelection(pkgName)!!.hostToStateMap[host]
+    private fun DomainVerificationService.stateFor(pkgName: String, host: String) =
+        getDomainVerificationUserState(pkgName, USER_ID)!!.hostToStateMap[host]
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index f7f5928..3870b02 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -641,6 +641,6 @@
     private static JobStatus createJobStatus(JobInfo.Builder job, int uid,
             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
         return new JobStatus(job.build(), uid, null, -1, 0, null,
-                earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0);
+                earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0, 0);
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 91b3cb7..7925b69 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -685,7 +685,7 @@
         final JobInfo job = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();
         return new JobStatus(job, 0, null, -1, 0, null, earliestRunTimeElapsedMillis,
-                latestRunTimeElapsedMillis, 0, 0, null, 0);
+                latestRunTimeElapsedMillis, 0, 0, null, 0, 0);
     }
 
     private static JobStatus createJobStatus(JobInfo job) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
new file mode 100644
index 0000000..d786a5d
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.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.server.usage;
+
+import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
+import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mockitoSession;
+
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
+import android.content.Context;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.usage.UserUsageStatsService.StatsUpdatedListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.io.File;
+import java.util.HashMap;
+
+@RunWith(AndroidJUnit4.class)
+public class UserUsageStatsServiceTest {
+    private static final int TEST_USER_ID = 0;
+    private static final String TEST_PACKAGE_NAME = "test.package";
+    private static final long TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
+
+    private UserUsageStatsService mService;
+    private MockitoSession mMockitoSession;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private StatsUpdatedListener mStatsUpdatedListener;
+
+    @Before
+    public void setUp() {
+        mMockitoSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+
+        File dir = new File(InstrumentationRegistry.getContext().getCacheDir(), "test");
+        mService = new UserUsageStatsService(mContext, TEST_USER_ID, dir, mStatsUpdatedListener);
+
+        HashMap<String, Long> installedPkgs = new HashMap<>();
+        installedPkgs.put(TEST_PACKAGE_NAME, System.currentTimeMillis());
+
+        mService.init(System.currentTimeMillis(), installedPkgs);
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockitoSession != null) {
+            mMockitoSession.finishMocking();
+        }
+    }
+
+    @Test
+    public void testReportEvent_eventAppearsInQueries() {
+        Event event = new Event(ACTIVITY_RESUMED, SystemClock.elapsedRealtime());
+        event.mPackage = TEST_PACKAGE_NAME;
+        mService.reportEvent(event);
+
+        long now = System.currentTimeMillis();
+        long startTime = now - TIME_INTERVAL_MILLIS;
+        UsageEvents events = mService.queryEventsForPackage(
+                startTime, now, TEST_PACKAGE_NAME, false /* includeTaskRoot */);
+
+        boolean hasTestEvent = false;
+        while (events != null && events.hasNextEvent()) {
+            Event outEvent = new Event();
+            events.getNextEvent(outEvent);
+            if (outEvent.mEventType == ACTIVITY_RESUMED) {
+                hasTestEvent = true;
+            }
+        }
+        assertTrue(hasTestEvent);
+    }
+
+    @Test
+    public void testReportEvent_packageUsedEventNotTracked() {
+        Event event = new Event(APP_COMPONENT_USED, SystemClock.elapsedRealtime());
+        event.mPackage = TEST_PACKAGE_NAME;
+        mService.reportEvent(event);
+
+        long now = System.currentTimeMillis();
+        long startTime = now - TIME_INTERVAL_MILLIS;
+        UsageEvents events = mService.queryEventsForPackage(
+                startTime, now, TEST_PACKAGE_NAME, false /* includeTaskRoot */);
+
+        boolean hasTestEvent = false;
+        while (events != null && events.hasNextEvent()) {
+            Event outEvent = new Event();
+            events.getNextEvent(outEvent);
+            if (outEvent.mEventType == APP_COMPONENT_USED) {
+                hasTestEvent = true;
+            }
+        }
+        assertFalse(hasTestEvent);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
new file mode 100644
index 0000000..489e2f7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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;
+
+import android.test.AndroidTestCase;
+
+/**
+ * Tests for {@link com.android.server.BootReceiver}
+ */
+public class BootReceiverTest extends AndroidTestCase {
+    public void testLogLinePotentiallySensitive() throws Exception {
+        /*
+         * Strings to be dropped from the log as potentially sensitive: register dumps, process
+         * names, hardware info.
+         */
+        final String[] becomeNull = {
+            "CPU: 4 PID: 120 Comm: kunit_try_catch Tainted: G        W         5.8.0-rc6+ #7",
+            "Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014",
+            "[    0.083207] RSP: 0000:ffffffff8fe07ca8 EFLAGS: 00010046 ORIG_RAX: 0000000000000000",
+            "[    0.084709] RAX: 0000000000000000 RBX: ffffffffff240000 RCX: ffffffff815fcf01",
+            "[    0.086109] RDX: dffffc0000000000 RSI: 0000000000000001 RDI: ffffffffff240004",
+            "[    0.087509] RBP: ffffffff8fe07d60 R08: fffffbfff1fc0f21 R09: fffffbfff1fc0f21",
+            "[    0.088911] R10: ffffffff8fe07907 R11: fffffbfff1fc0f20 R12: ffffffff8fe07d38",
+            "R13: 0000000000000001 R14: 0000000000000001 R15: ffffffff8fe07e80",
+            "x29: ffff00003ce07150 x28: ffff80001aa29cc0",
+            "x1 : 0000000000000000 x0 : ffff00000f628000",
+        };
+
+        /* Strings to be left unchanged, including non-sensitive registers and parts of reports. */
+        final String[] leftAsIs = {
+            "FS:  0000000000000000(0000) GS:ffffffff92409000(0000) knlGS:0000000000000000",
+            "[ 69.2366] [ T6006]c7   6006  =======================================================",
+            "[ 69.245688] [ T6006] BUG: KFENCE: out-of-bounds in kfence_handle_page_fault",
+            "[ 69.257816] [ T6006]c7   6006  Out-of-bounds access at 0xffffffca75c45000 ",
+            "[ 69.273536] [ T6006]c7   6006   __do_kernel_fault+0xa8/0x11c",
+            "pc : __mutex_lock+0x428/0x99c ",
+            "sp : ffff00003ce07150",
+            "Call trace:",
+            "",
+        };
+
+        final String[][] stripped = {
+            { "Detected corrupted memory at 0xffffffffb6797ff9 [ 0xac . . . . . . ]:",
+              "Detected corrupted memory at 0xffffffffb6797ff9" },
+        };
+        for (int i = 0; i < becomeNull.length; i++) {
+            assertEquals(BootReceiver.stripSensitiveData(becomeNull[i]), null);
+        }
+
+        for (int i = 0; i < leftAsIs.length; i++) {
+            assertEquals(BootReceiver.stripSensitiveData(leftAsIs[i]), leftAsIs[i]);
+        }
+
+        for (int i = 0; i < stripped.length; i++) {
+            assertEquals(BootReceiver.stripSensitiveData(stripped[i][0]), stripped[i][1]);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index f897d5c..d6c11a5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -495,7 +495,8 @@
         mServiceConnection.performAccessibilityAction(PIP_WINDOWID, ROOT_NODE_ID,
                 ACTION_ACCESSIBILITY_FOCUS, null, INTERACTION_ID, mMockCallback, TID);
 
-        verify(mMockIPowerManager).userActivity(anyLong(), anyInt(), anyInt());
+        verify(mMockIPowerManager).userActivity(eq(Display.DEFAULT_DISPLAY), anyLong(), anyInt(),
+                anyInt());
         verify(mMockIA11yInteractionConnection).performAccessibilityAction(eq(ROOT_NODE_ID),
                 eq(ACTION_ACCESSIBILITY_FOCUS), any(), eq(INTERACTION_ID), eq(mMockCallback),
                 anyInt(), eq(PID), eq(TID));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
new file mode 100644
index 0000000..170f561
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
@@ -0,0 +1,581 @@
+/*
+ * 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.accessibility;
+
+
+import static android.view.accessibility.AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS;
+import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS;
+import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.RemoteException;
+import android.view.AccessibilityInteractionController;
+import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeIdManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests that verify expected node and prefetched node results when finding a view by node id. We
+ * send some requests to the controller via View methods to control message timing.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityInteractionControllerNodeRequestsTest {
+    private AccessibilityInteractionController mAccessibilityInteractionController;
+    @Mock
+    private IAccessibilityInteractionConnectionCallback mMockClientCallback1;
+    @Mock
+    private IAccessibilityInteractionConnectionCallback mMockClientCallback2;
+
+    @Captor
+    private ArgumentCaptor<AccessibilityNodeInfo> mFindInfoCaptor;
+    @Captor private ArgumentCaptor<List<AccessibilityNodeInfo>> mPrefetchInfoListCaptor;
+
+    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    private static final int MOCK_CLIENT_1_THREAD_AND_PROCESS_ID = 1;
+    private static final int MOCK_CLIENT_2_THREAD_AND_PROCESS_ID = 2;
+
+    private static final String FRAME_LAYOUT_DESCRIPTION = "frameLayout";
+    private static final String TEXT_VIEW_1_DESCRIPTION = "textView1";
+    private static final String TEXT_VIEW_2_DESCRIPTION = "textView2";
+
+    private TestFrameLayout mFrameLayout;
+    private TestTextView mTextView1;
+    private TestTextView2 mTextView2;
+
+    private boolean mSendClient1RequestForTextAfterTextPrefetched;
+    private boolean mSendClient2RequestForTextAfterTextPrefetched;
+    private boolean mSendRequestForTextAndIncludeUnImportantViews;
+    private int mMockClient1InteractionId;
+    private int mMockClient2InteractionId;
+
+    @Before
+    public void setUp() throws Throwable {
+        MockitoAnnotations.initMocks(this);
+
+        mInstrumentation.runOnMainSync(() -> {
+            final Context context = mInstrumentation.getTargetContext();
+            final ViewRootImpl viewRootImpl = new ViewRootImpl(context, context.getDisplay());
+
+            mFrameLayout = new TestFrameLayout(context);
+            mTextView1 = new TestTextView(context);
+            mTextView2 = new TestTextView2(context);
+
+            mFrameLayout.addView(mTextView1);
+            mFrameLayout.addView(mTextView2);
+
+            // The controller retrieves views through this manager, and registration happens on
+            // when attached to a window, which we don't have. We can simply reference FrameLayout
+            // with ROOT_NODE_ID
+            AccessibilityNodeIdManager.getInstance().registerViewWithId(
+                    mTextView1, mTextView1.getAccessibilityViewId());
+            AccessibilityNodeIdManager.getInstance().registerViewWithId(
+                    mTextView2, mTextView2.getAccessibilityViewId());
+
+            try {
+                viewRootImpl.setView(mFrameLayout, new WindowManager.LayoutParams(), null);
+
+            } catch (WindowManager.BadTokenException e) {
+                // activity isn't running, we will ignore BadTokenException.
+            }
+
+            mAccessibilityInteractionController =
+                    new AccessibilityInteractionController(viewRootImpl);
+        });
+
+    }
+
+    @After
+    public void tearDown() throws Throwable {
+        AccessibilityNodeIdManager.getInstance().unregisterViewWithId(
+                mTextView1.getAccessibilityViewId());
+        AccessibilityNodeIdManager.getInstance().unregisterViewWithId(
+                mTextView2.getAccessibilityViewId());
+    }
+
+    /**
+     * Tests a basic request for the root node with prefetch flag
+     * {@link AccessibilityNodeInfo#FLAG_PREFETCH_DESCENDANTS}
+     *
+     * @throws RemoteException
+     */
+    @Test
+    public void testFindRootView_withOneClient_shouldReturnRootNodeAndPrefetchDescendants()
+            throws RemoteException {
+        // Request for our FrameLayout
+        sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1,
+                mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS);
+        mInstrumentation.waitForIdleSync();
+
+        // Verify we get FrameLayout
+        verify(mMockClientCallback1).setFindAccessibilityNodeInfoResult(
+                mFindInfoCaptor.capture(), eq(mMockClient1InteractionId));
+        AccessibilityNodeInfo infoSentToService = mFindInfoCaptor.getValue();
+        assertEquals(FRAME_LAYOUT_DESCRIPTION, infoSentToService.getContentDescription());
+
+        verify(mMockClientCallback1).setPrefetchAccessibilityNodeInfoResult(
+                mPrefetchInfoListCaptor.capture(), eq(mMockClient1InteractionId));
+        // The descendants are our two TextViews
+        List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue();
+        assertEquals(2, prefetchedNodes.size());
+        assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetchedNodes.get(0).getContentDescription());
+        assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNodes.get(1).getContentDescription());
+
+    }
+
+    /**
+     * Tests a basic request for TestTextView1's node with prefetch flag
+     * {@link AccessibilityNodeInfo#FLAG_PREFETCH_SIBLINGS}
+     *
+     * @throws RemoteException
+     */
+    @Test
+    public void testFindTextView_withOneClient_shouldReturnNodeAndPrefetchedSiblings()
+            throws RemoteException {
+        // Request for TextView1
+        sendNodeRequestToController(AccessibilityNodeInfo.makeNodeId(
+                mTextView1.getAccessibilityViewId(), AccessibilityNodeProvider.HOST_VIEW_ID),
+                mMockClientCallback1, mMockClient1InteractionId, FLAG_PREFETCH_SIBLINGS);
+        mInstrumentation.waitForIdleSync();
+
+        // Verify we get TextView1
+        verify(mMockClientCallback1).setFindAccessibilityNodeInfoResult(
+                mFindInfoCaptor.capture(), eq(mMockClient1InteractionId));
+        AccessibilityNodeInfo infoSentToService = mFindInfoCaptor.getValue();
+        assertEquals(TEXT_VIEW_1_DESCRIPTION, infoSentToService.getContentDescription());
+
+        // Verify the prefetched sibling of TextView1 is TextView2
+        verify(mMockClientCallback1).setPrefetchAccessibilityNodeInfoResult(
+                mPrefetchInfoListCaptor.capture(), eq(mMockClient1InteractionId));
+        // TextView2 is the prefetched sibling
+        List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue();
+        assertEquals(1, prefetchedNodes.size());
+        assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNodes.get(0).getContentDescription());
+    }
+
+    /**
+     * Tests a series of controller requests to prevent prefetching.
+     *     Request 1: Client 1 requests the root node
+     *     Request 2: When the root node is initialized in
+     *     {@link TestFrameLayout#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)},
+     *     Client 2 requests TestTextView1's node
+     *
+     * Request 2 on the queue prevents prefetching for Request 1.
+     *
+     * @throws RemoteException
+     */
+    @Test
+    public void testFindRootAndTextNodes_withTwoClients_shouldPreventClient1Prefetch()
+            throws RemoteException {
+        mFrameLayout.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                final long nodeId = AccessibilityNodeInfo.makeNodeId(
+                        mTextView1.getAccessibilityViewId(),
+                        AccessibilityNodeProvider.HOST_VIEW_ID);
+
+                    // Enqueue a request when this node is found from a different service for
+                    // TextView1
+                    sendNodeRequestToController(nodeId, mMockClientCallback2,
+                            mMockClient2InteractionId, FLAG_PREFETCH_SIBLINGS);
+            }
+        });
+        // Client 1 request for FrameLayout
+        sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1,
+                mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS);
+
+        mInstrumentation.waitForIdleSync();
+
+        // Verify client 1 gets FrameLayout
+        verify(mMockClientCallback1).setFindAccessibilityNodeInfoResult(
+                mFindInfoCaptor.capture(), eq(mMockClient1InteractionId));
+        AccessibilityNodeInfo infoSentToService = mFindInfoCaptor.getValue();
+        assertEquals(FRAME_LAYOUT_DESCRIPTION, infoSentToService.getContentDescription());
+
+        // The second request is put in the queue in the FrameLayout's onInitializeA11yNodeInfo,
+        // meaning prefetching is interrupted and does not even begin for the first request
+        verify(mMockClientCallback1, never())
+                .setPrefetchAccessibilityNodeInfoResult(anyList(), anyInt());
+
+        // Verify client 2 gets TextView1
+        verify(mMockClientCallback2).setFindAccessibilityNodeInfoResult(
+                mFindInfoCaptor.capture(), eq(mMockClient2InteractionId));
+        infoSentToService = mFindInfoCaptor.getValue();
+        assertEquals(TEXT_VIEW_1_DESCRIPTION, infoSentToService.getContentDescription());
+
+        // Verify the prefetched sibling of TextView1 is TextView2 (FLAG_PREFETCH_SIBLINGS)
+        verify(mMockClientCallback2).setPrefetchAccessibilityNodeInfoResult(
+                mPrefetchInfoListCaptor.capture(), eq(mMockClient2InteractionId));
+        List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue();
+        assertEquals(1, prefetchedNodes.size());
+        assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNodes.get(0).getContentDescription());
+    }
+
+    /**
+     * Tests a series of controller same-service requests to interrupt prefetching and satisfy a
+     * pending node request.
+     *     Request 1: Request the root node
+     *     Request 2: When TextTextView1's node is initialized as part of Request 1's prefetching,
+     *     request TestTextView1's node
+     *
+     * Request 1 prefetches TestTextView1's node, is interrupted by a pending request, and checks
+     * if its prefetched nodes satisfy any pending requests. It satisfies Request 2's request for
+     * TestTextView1's node. Request 2 is fulfilled, so it is removed from queue and does not
+     * prefetch.
+     *
+     * @throws RemoteException
+     */
+    @Test
+    public void testFindRootAndTextNode_withOneClient_shouldInterruptPrefetchAndSatisfyPendingMsg()
+            throws RemoteException {
+        mSendClient1RequestForTextAfterTextPrefetched = true;
+
+        mTextView1.setAccessibilityDelegate(new View.AccessibilityDelegate(){
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.setContentDescription(TEXT_VIEW_1_DESCRIPTION);
+                final long nodeId = AccessibilityNodeInfo.makeNodeId(
+                        mTextView1.getAccessibilityViewId(),
+                        AccessibilityNodeProvider.HOST_VIEW_ID);
+
+                if (mSendClient1RequestForTextAfterTextPrefetched) {
+                    // Prevent a loop when processing second request
+                    mSendClient1RequestForTextAfterTextPrefetched = false;
+                    // TextView1 is prefetched here after the FrameLayout is found. Now enqueue a
+                    // same-client request for TextView1
+                    sendNodeRequestToController(nodeId, mMockClientCallback1,
+                            ++mMockClient1InteractionId, FLAG_PREFETCH_SIBLINGS);
+
+                }
+            }
+        });
+        // Client 1 requests FrameLayout
+        sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1,
+                mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS);
+
+        // Flush out all messages
+        mInstrumentation.waitForIdleSync();
+
+        // When TextView1 is prefetched for FrameLayout, we put a message on the queue in
+        // TextView1's onInitializeA11yNodeInfo that requests for TextView1. The service thus get
+        // two node results for FrameLayout and TextView1.
+        verify(mMockClientCallback1, times(2))
+                .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(), anyInt());
+
+        List<AccessibilityNodeInfo> foundNodes = mFindInfoCaptor.getAllValues();
+        assertEquals(FRAME_LAYOUT_DESCRIPTION, foundNodes.get(0).getContentDescription());
+        assertEquals(TEXT_VIEW_1_DESCRIPTION, foundNodes.get(1).getContentDescription());
+
+        // The controller will look at FrameLayout's prefetched nodes and find matching nodes in
+        // pending requests. The prefetched TextView1 matches the second request. The second
+        // request was removed from queue and prefetching for this request never occurred.
+        verify(mMockClientCallback1, times(1))
+                .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(),
+                        eq(mMockClient1InteractionId - 1));
+        List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue();
+        assertEquals(1, prefetchedNodes.size());
+        assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetchedNodes.get(0).getContentDescription());
+    }
+
+    /**
+     * Like above, but tests a series of controller requests from different services to interrupt
+     * prefetching and satisfy a pending node request.
+     *
+     * @throws RemoteException
+     */
+    @Test
+    public void testFindRootAndTextNode_withTwoClients_shouldInterruptPrefetchAndSatisfyPendingMsg()
+            throws RemoteException {
+        mSendClient2RequestForTextAfterTextPrefetched = true;
+        mTextView1.setAccessibilityDelegate(new View.AccessibilityDelegate(){
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.setContentDescription(TEXT_VIEW_1_DESCRIPTION);
+                final long nodeId = AccessibilityNodeInfo.makeNodeId(
+                        mTextView1.getAccessibilityViewId(),
+                        AccessibilityNodeProvider.HOST_VIEW_ID);
+
+                if (mSendClient2RequestForTextAfterTextPrefetched) {
+                    mSendClient2RequestForTextAfterTextPrefetched = false;
+                    // TextView1 is prefetched here. Now enqueue client 2's request for
+                    // TextView1
+                    sendNodeRequestToController(nodeId, mMockClientCallback2,
+                            mMockClient2InteractionId, FLAG_PREFETCH_SIBLINGS);
+                }
+            }
+        });
+        // Client 1 requests FrameLayout
+        sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1,
+                mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS);
+
+        mInstrumentation.waitForIdleSync();
+
+        // Verify client 1 gets FrameLayout
+        verify(mMockClientCallback1, times(1))
+                .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(), anyInt());
+        assertEquals(FRAME_LAYOUT_DESCRIPTION,
+                mFindInfoCaptor.getValue().getContentDescription());
+
+        // Verify client 1 has prefetched nodes
+        verify(mMockClientCallback1, times(1))
+                .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(),
+                        eq(mMockClient1InteractionId));
+
+        // Verify client 1's only prefetched node is TextView1
+        List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue();
+        assertEquals(1, prefetchedNodes.size());
+        assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetchedNodes.get(0).getContentDescription());
+
+        // Verify client 2 gets TextView1
+        verify(mMockClientCallback2, times(1))
+                .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(), anyInt());
+
+        assertEquals(TEXT_VIEW_1_DESCRIPTION, mFindInfoCaptor.getValue().getContentDescription());
+
+        // The second request was removed from queue and prefetching for this client request never
+        // occurred as it was satisfied.
+        verify(mMockClientCallback2, never())
+                .setPrefetchAccessibilityNodeInfoResult(anyList(), anyInt());
+
+    }
+
+    @Test
+    public void testFindNodeById_withTwoDifferentPrefetchFlags_shouldNotSatisfyPendingRequest()
+            throws RemoteException {
+        mSendRequestForTextAndIncludeUnImportantViews = true;
+        mTextView1.setAccessibilityDelegate(new View.AccessibilityDelegate(){
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.setContentDescription(TEXT_VIEW_1_DESCRIPTION);
+                final long nodeId = AccessibilityNodeInfo.makeNodeId(
+                        mTextView1.getAccessibilityViewId(),
+                        AccessibilityNodeProvider.HOST_VIEW_ID);
+
+                if (mSendRequestForTextAndIncludeUnImportantViews) {
+                    mSendRequestForTextAndIncludeUnImportantViews = false;
+                    // TextView1 is prefetched here for client 1. Now enqueue a request from a
+                    // different client that holds different fetch flags for TextView1
+                    sendNodeRequestToController(nodeId, mMockClientCallback2,
+                            mMockClient2InteractionId,
+                            FLAG_PREFETCH_SIBLINGS | FLAG_INCLUDE_NOT_IMPORTANT_VIEWS);
+                }
+            }
+        });
+
+        // Mockito does not make copies of objects when called. It holds references, so
+        // the captor would point to client 2's results after all requests are processed. Verify
+        // prefetched node immediately
+        doAnswer(invocation -> {
+            List<AccessibilityNodeInfo> prefetched = invocation.getArgument(0);
+            assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetched.get(0).getContentDescription());
+            return null;
+        }).when(mMockClientCallback1).setPrefetchAccessibilityNodeInfoResult(anyList(),
+                eq(mMockClient1InteractionId));
+
+        // Client 1 requests FrameLayout
+        sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1,
+                mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS);
+
+        mInstrumentation.waitForIdleSync();
+
+        // Verify client 1 gets FrameLayout
+        verify(mMockClientCallback1, times(1))
+                .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(),
+                        eq(mMockClient1InteractionId));
+
+        assertEquals(FRAME_LAYOUT_DESCRIPTION,
+                mFindInfoCaptor.getValue().getContentDescription());
+
+        // Verify client 1 has prefetched results. The only prefetched node is TextView1
+        // (from above doAnswer)
+        verify(mMockClientCallback1, times(1))
+                .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(),
+                        eq(mMockClient1InteractionId));
+
+        // Verify client 2 gets TextView1
+        verify(mMockClientCallback2, times(1))
+                .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(),
+                        eq(mMockClient2InteractionId));
+        assertEquals(TEXT_VIEW_1_DESCRIPTION,
+                mFindInfoCaptor.getValue().getContentDescription());
+        // Verify client 2 has TextView2 as a prefetched node
+        verify(mMockClientCallback2, times(1))
+                .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(),
+                        eq(mMockClient2InteractionId));
+        List<AccessibilityNodeInfo> prefetchedNode = mPrefetchInfoListCaptor.getValue();
+        assertEquals(1, prefetchedNode.size());
+        assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNode.get(0).getContentDescription());
+    }
+
+    private void sendNodeRequestToController(long requestedNodeId,
+            IAccessibilityInteractionConnectionCallback callback, int interactionId,
+            int prefetchFlags) {
+        final int processAndThreadId = callback == mMockClientCallback1
+                ? MOCK_CLIENT_1_THREAD_AND_PROCESS_ID
+                : MOCK_CLIENT_2_THREAD_AND_PROCESS_ID;
+
+        mAccessibilityInteractionController.findAccessibilityNodeInfoByAccessibilityIdClientThread(
+                requestedNodeId,
+                null, interactionId,
+                callback, prefetchFlags,
+                processAndThreadId,
+                processAndThreadId, null, null);
+
+    }
+
+    private class TestFrameLayout extends FrameLayout {
+
+        TestFrameLayout(Context context) {
+            super(context);
+        }
+
+        @Override
+        public int getWindowVisibility() {
+            // We aren't attached to a window so let's pretend
+            return VISIBLE;
+        }
+
+        @Override
+        public boolean isShown() {
+            // Controller check
+            return true;
+        }
+
+        @Override
+        public int getAccessibilityViewId() {
+            // static id doesn't reset after tests so return the same one
+            return 0;
+        }
+
+        @Override
+        public void addChildrenForAccessibility(ArrayList<View> outChildren) {
+            // ViewGroup#addChildrenForAccessbility sorting logic will switch these two
+            outChildren.add(mTextView1);
+            outChildren.add(mTextView2);
+        }
+
+        @Override
+        public boolean includeForAccessibility() {
+            return true;
+        }
+
+        @Override
+        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(info);
+            info.setContentDescription(FRAME_LAYOUT_DESCRIPTION);
+        }
+    }
+
+    private class TestTextView extends TextView {
+        TestTextView(Context context) {
+            super(context);
+        }
+
+        @Override
+        public int getWindowVisibility() {
+            return VISIBLE;
+        }
+
+        @Override
+        public boolean isShown() {
+            return true;
+        }
+
+        @Override
+        public int getAccessibilityViewId() {
+            return 1;
+        }
+
+        @Override
+        public boolean includeForAccessibility() {
+            return true;
+        }
+
+        @Override
+        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(info);
+            info.setContentDescription(TEXT_VIEW_1_DESCRIPTION);
+        }
+    }
+
+    private class TestTextView2 extends TextView {
+        TestTextView2(Context context) {
+            super(context);
+        }
+
+        @Override
+        public int getWindowVisibility() {
+            return VISIBLE;
+        }
+
+        @Override
+        public boolean isShown() {
+            return true;
+        }
+
+        @Override
+        public int getAccessibilityViewId() {
+            return 2;
+        }
+
+        @Override
+        public boolean includeForAccessibility() {
+            return true;
+        }
+
+        @Override
+        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(info);
+            info.setContentDescription(TEXT_VIEW_2_DESCRIPTION);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index 85b8fcb..ebb73e8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -39,6 +39,7 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.view.Display;
 import android.view.KeyEvent;
 
 import androidx.test.InstrumentationRegistry;
@@ -172,8 +173,8 @@
                 mFilter1SequenceCaptor.getValue());
 
         assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
-                eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+        verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+                anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
         assertFalse(isTimeoutPending(mMessageCapturingHandler));
     }
 
@@ -204,8 +205,8 @@
                 mFilter2SequenceCaptor.getValue());
 
         assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
-                eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+        verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+                anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
         assertFalse(isTimeoutPending(mMessageCapturingHandler));
     }
 
@@ -221,8 +222,8 @@
                 mFilter2SequenceCaptor.getValue());
 
         assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
-                eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+        verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+                anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
         assertFalse(isTimeoutPending(mMessageCapturingHandler));
     }
 
@@ -238,8 +239,8 @@
                 mFilter2SequenceCaptor.getValue());
 
         assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
-                eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+        verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+                anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
         assertFalse(isTimeoutPending(mMessageCapturingHandler));
     }
 
@@ -308,8 +309,8 @@
         mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
 
         assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
-                eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+        verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+                anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
     }
 
     @Test
@@ -357,8 +358,8 @@
         mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
 
         assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
-                eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+        verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+                anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
     }
 
     /*
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
index 97daea3..0b1c120 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
@@ -67,7 +67,6 @@
                                         .setPropertyName(propertyKeyString)
                                         .addSnippetMatches(
                                                 SnippetMatchProto.newBuilder()
-                                                        .setValuesIndex(0)
                                                         .setExactMatchPosition(29)
                                                         .setExactMatchBytes(3)
                                                         .setWindowPosition(26)
@@ -174,7 +173,6 @@
                                         .setPropertyName("sender.name")
                                         .addSnippetMatches(
                                                 SnippetMatchProto.newBuilder()
-                                                        .setValuesIndex(0)
                                                         .setExactMatchPosition(0)
                                                         .setExactMatchBytes(4)
                                                         .setWindowPosition(0)
@@ -186,7 +184,6 @@
                                         .setPropertyName("sender.email")
                                         .addSnippetMatches(
                                                 SnippetMatchProto.newBuilder()
-                                                        .setValuesIndex(0)
                                                         .setExactMatchPosition(0)
                                                         .setExactMatchBytes(20)
                                                         .setWindowPosition(0)
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index deaeb46..8b35af8 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -276,7 +276,7 @@
                 0 /* sourceUserId */, 0, "someTag",
                 invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis,
                 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
-                persistedExecutionTimesUTC, 0 /* innerFlagg */);
+                persistedExecutionTimesUTC, 0 /* innerFlag */, 0 /* dynamicConstraints */);
 
         mTaskStoreUnderTest.add(js);
         waitForPendingIo();
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index 7d9ab37..dd9ae65 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -19,6 +19,7 @@
 import static com.android.server.job.JobConcurrencyManager.NUM_WORK_TYPES;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER_IMPORTANT;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_FGS;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
@@ -29,6 +30,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.annotation.NonNull;
+import android.util.Log;
 import android.util.Pair;
 import android.util.SparseIntArray;
 
@@ -52,11 +54,11 @@
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class WorkCountTrackerTest {
-    private static final String TAG = "WorkerCountTrackerTest";
+    private static final String TAG = "WorkCountTrackerTest";
 
     private static final double[] EQUAL_PROBABILITY_CDF =
             buildWorkTypeCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES,
-                    1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES);
+                    1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES);
 
     private Random mRandom;
     private WorkCountTracker mWorkCountTracker;
@@ -69,12 +71,15 @@
 
     @NonNull
     private static double[] buildWorkTypeCdf(
-            double pTop, double pFgs, double pEj, double pBg, double pBgUser) {
-        return buildCdf(pTop, pFgs, pEj, pBg, pBgUser);
+            double pTop, double pFgs, double pEj, double pBg, double pBgUserImp, double pBgUser) {
+        return buildCdf(pTop, pFgs, pEj, pBg, pBgUserImp, pBgUser);
     }
 
     @NonNull
     private static double[] buildCdf(double... probs) {
+        if (probs.length == 0) {
+            throw new IllegalArgumentException("Must supply at least one probability");
+        }
         double[] cdf = new double[probs.length];
         double sum = 0;
 
@@ -84,7 +89,9 @@
         }
 
         if (Double.compare(1, sum) != 0) {
-            throw new IllegalArgumentException("probabilities don't sum to one: " + sum);
+            Log.e(TAG, "probabilities don't sum to one: " + sum);
+            // 1.0/6 doesn't work well in code :/
+            cdf[cdf.length - 1] = 1;
         }
         return cdf;
     }
@@ -111,6 +118,8 @@
             case 3:
                 return WORK_TYPE_BG;
             case 4:
+                return WORK_TYPE_BGUSER_IMPORTANT;
+            case 5:
                 return WORK_TYPE_BGUSER;
             default:
                 throw new IllegalStateException("Unknown work type");
@@ -312,7 +321,7 @@
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of();
         final double probStop = 0.5;
-        final double[] cdf = buildWorkTypeCdf(0.5, 0, 0, 0.5, 0);
+        final double[] cdf = buildWorkTypeCdf(0.5, 0, 0, 0.5, 0, 0);
         final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
         final double probStart = 0.5;
 
@@ -330,7 +339,7 @@
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.5;
-        final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 1.0 / 3);
+        final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 0, 1.0 / 3);
         final double[] numTypesCdf = buildCdf(.75, .2, .05);
         final double probStart = 0.5;
 
@@ -348,7 +357,7 @@
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of();
         final double probStop = 0.5;
-        final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 1.0 / 3);
+        final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 0, 1.0 / 3);
         final double[] numTypesCdf = buildCdf(.05, .95);
         final double probStart = 0.5;
 
@@ -366,7 +375,7 @@
                 List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.5;
-        final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.8, .1);
+        final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.8, 0.02, .08);
         final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
         final double probStart = 0.5;
 
@@ -384,7 +393,7 @@
                 List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.5;
-        final double[] cdf = buildWorkTypeCdf(0.85, 0.05, 0, 0.1, 0);
+        final double[] cdf = buildWorkTypeCdf(0.85, 0.05, 0, 0.1, 0, 0);
         final double[] numTypesCdf = buildCdf(1);
         final double probStart = 0.5;
 
@@ -402,7 +411,7 @@
                 List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.4;
-        final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.1, .8);
+        final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.1, 0.05, .75);
         final double[] numTypesCdf = buildCdf(0.5, 0.5);
         final double probStart = 0.5;
 
@@ -421,7 +430,7 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final double probStop = 0.4;
-        final double[] cdf = buildWorkTypeCdf(0.8, 0.1, 0, 0.05, 0.05);
+        final double[] cdf = buildWorkTypeCdf(0.8, 0.1, 0, 0.05, 0, 0.05);
         final double[] numTypesCdf = buildCdf(1);
         final double probStart = 0.5;
 
@@ -440,7 +449,7 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final double probStop = 0.5;
-        final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.5, 0.5);
+        final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.5, 0, 0.5);
         final double[] numTypesCdf = buildCdf(1);
         final double probStart = 0.5;
 
@@ -459,7 +468,7 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final double probStop = 0.5;
-        final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.1, 0.9);
+        final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.1, 0, 0.9);
         final double[] numTypesCdf = buildCdf(0.9, 0.1);
         final double probStart = 0.5;
 
@@ -478,7 +487,7 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final double probStop = 0.5;
-        final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.9, 0.1);
+        final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.9, 0, 0.1);
         final double[] numTypesCdf = buildCdf(1);
         final double probStart = 0.5;
 
@@ -496,7 +505,7 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.4;
-        final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0, 0);
+        final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0, 0, 0);
         final double[] numTypesCdf = buildCdf(0.1, 0.7, 0.2);
         final double probStart = 0.5;
 
@@ -519,7 +528,7 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1));
         final double probStop = 0.13;
-        final double[] numTypesCdf = buildCdf(0, 0.05, 0.1, 0.8, 0.05);
+        final double[] numTypesCdf = buildCdf(0, 0.05, 0.1, 0.7, 0.1, 0.05);
         final double probStart = 0.87;
 
         checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
@@ -536,7 +545,7 @@
                 List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.4;
-        final double[] cdf = buildWorkTypeCdf(.1, 0, 0.5, 0.35, 0.05);
+        final double[] cdf = buildWorkTypeCdf(.1, 0, 0.5, 0.35, 0, 0.05);
         final double[] numTypesCdf = buildCdf(1);
         final double probStart = 0.5;
 
@@ -556,7 +565,28 @@
         final List<Pair<Integer, Integer>> minLimits =
                 List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2));
         final double probStop = 0.4;
-        final double[] cdf = buildWorkTypeCdf(0.01, 0.09, 0.4, 0.1, 0.4);
+        final double[] cdf = buildWorkTypeCdf(0.01, 0.09, 0.4, 0.1, 0, 0.4);
+        final double[] numTypesCdf = buildCdf(0.7, 0.3);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                cdf, numTypesCdf, probStop);
+    }
+
+    @Test
+    public void testRandom16() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 7;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
+                        Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
+                        Pair.create(WORK_TYPE_BGUSER, 1));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2));
+        final double probStop = 0.4;
+        final double[] cdf = buildWorkTypeCdf(0.01, 0.09, 0.25, 0.05, 0.3, 0.3);
         final double[] numTypesCdf = buildCdf(0.7, 0.3);
         final double probStart = 0.5;
 
@@ -748,6 +778,7 @@
                 /* resPen */ List.of(
                         Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 2)));
 
+        Log.d(TAG, "START***#*#*#*#*#*#**#*");
         // Test multi-types
         checkSimple(6,
                 /* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
@@ -764,7 +795,10 @@
                 /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2),
                         Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
                 /* resPen */ List.of(
-                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)));
+                        // Not checking BG count because the test starts jobs in random order
+                        // and if it tries to start 4 BG jobs (2 will run as EJ from EJ|BG), but
+                        // the resulting pending will be 3 BG instead of 4 BG.
+                        Pair.create(WORK_TYPE_BGUSER, 1)));
     }
 
     /** Tests that the counter updates properly when jobs are stopped. */
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
index cc18317..a5fedef 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -17,6 +17,7 @@
 
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER_IMPORTANT;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_FGS;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
@@ -49,11 +50,13 @@
     private static final String KEY_MAX_FGS = "concurrency_max_fgs_test";
     private static final String KEY_MAX_EJ = "concurrency_max_ej_test";
     private static final String KEY_MAX_BG = "concurrency_max_bg_test";
+    private static final String KEY_MAX_BGUSER_IMPORTANT = "concurrency_max_bguser_important_test";
     private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_test";
     private static final String KEY_MIN_TOP = "concurrency_min_top_test";
     private static final String KEY_MIN_FGS = "concurrency_min_fgs_test";
     private static final String KEY_MIN_EJ = "concurrency_min_ej_test";
     private static final String KEY_MIN_BG = "concurrency_min_bg_test";
+    private static final String KEY_MIN_BGUSER_IMPORTANT = "concurrency_min_bguser_important_test";
     private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
 
     @After
@@ -68,11 +71,15 @@
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_FGS, null, false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, null, false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, null, false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                KEY_MAX_BGUSER_IMPORTANT, null, false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, null, false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, null, false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_FGS, null, false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, null, false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, null, false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                KEY_MIN_BGUSER_IMPORTANT, null, false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, null, false);
     }
 
@@ -316,19 +323,23 @@
                         .setInt(KEY_MIN_EJ, 3)
                         .setInt(KEY_MAX_BG, 13)
                         .setInt(KEY_MIN_BG, 4)
-                        .setInt(KEY_MAX_BGUSER, 12)
-                        .setInt(KEY_MIN_BGUSER, 5)
+                        .setInt(KEY_MAX_BGUSER_IMPORTANT, 12)
+                        .setInt(KEY_MIN_BGUSER_IMPORTANT, 5)
+                        .setInt(KEY_MAX_BGUSER, 11)
+                        .setInt(KEY_MIN_BGUSER, 6)
                         .build(),
                 /*default*/ 9,
                 /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
                 /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
                 /*expected*/ true, 16,
                 /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_FGS, 2),
-                        Pair.create(WORK_TYPE_EJ, 3),
-                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 5)),
+                        Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 4),
+                        Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 5),
+                        Pair.create(WORK_TYPE_BGUSER, 6)),
                 /* max */
                 List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_FGS, 15),
-                        Pair.create(WORK_TYPE_EJ, 14),
-                        Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 12)));
+                        Pair.create(WORK_TYPE_EJ, 14), Pair.create(WORK_TYPE_BG, 13),
+                        Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 12),
+                        Pair.create(WORK_TYPE_BGUSER, 11)));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index a896f1b..b51f4df 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -44,6 +44,7 @@
 import android.content.pm.UserInfo;
 import android.hardware.rebootescrow.IRebootEscrow;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.os.UserManager;
@@ -62,6 +63,7 @@
 import org.mockito.ArgumentCaptor;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 
 import javax.crypto.SecretKey;
@@ -181,6 +183,18 @@
         }
 
         @Override
+        public int getLoadEscrowDataRetryLimit() {
+            // Try two times
+            return 2;
+        }
+
+        @Override
+        public int getLoadEscrowDataRetryIntervalSeconds() {
+            // Retry in 1 seconds
+            return 1;
+        }
+
+        @Override
         public void reportMetric(boolean success) {
             mInjected.reportMetric(success);
         }
@@ -448,6 +462,46 @@
     }
 
     @Test
+    public void loadRebootEscrowDataIfAvailable_ServerBased_RetrySuccess() throws Exception {
+        setServerBasedRebootEscrowProvider();
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertTrue(mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+
+        when(mServiceConnection.unwrap(any(), anyLong()))
+                .thenThrow(new IOException())
+                .thenAnswer(invocation -> invocation.getArgument(0));
+
+        HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
+        thread.start();
+        mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper()));
+        // Sleep 5s for the retry to complete
+        Thread.sleep(5 * 1000);
+        verify(mServiceConnection, times(2)).unwrap(any(), anyLong());
+        assertTrue(metricsSuccessCaptor.getValue());
+        verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
+    }
+
+    @Test
     public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
         when(mInjected.getBootCount()).thenReturn(0);
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 2d6605a..c0a38b8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -239,8 +239,8 @@
         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
         assertTrue(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
 
-        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
-                .getResponseCode();
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
         assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
 
@@ -268,8 +268,8 @@
         long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
-        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
-                .getResponseCode();
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
         mLocalService.setLockCredentialWithToken(nonePassword(), handle, token, PRIMARY_USER_ID);
@@ -294,8 +294,8 @@
         long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
         assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
-        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
-                .getResponseCode();
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
         mService.setLockCredential(pattern, password, PRIMARY_USER_ID);
@@ -369,6 +369,36 @@
     }
 
     @Test
+    public void testActivateMultipleEscrowTokens() throws Exception {
+        byte[] token0 = "some-high-entropy-secure-token-0".getBytes();
+        byte[] token1 = "some-high-entropy-secure-token-1".getBytes();
+        byte[] token2 = "some-high-entropy-secure-token-2".getBytes();
+
+        LockscreenCredential password = newPassword("password");
+        LockscreenCredential pattern = newPattern("123654");
+        initializeCredentialUnderSP(password, PRIMARY_USER_ID);
+
+        long handle0 = mLocalService.addEscrowToken(token0, PRIMARY_USER_ID, null);
+        long handle1 = mLocalService.addEscrowToken(token1, PRIMARY_USER_ID, null);
+        long handle2 = mLocalService.addEscrowToken(token2, PRIMARY_USER_ID, null);
+
+        // Activate token
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
+
+        // Verify tokens work
+        assertTrue(mLocalService.isEscrowTokenActive(handle0, PRIMARY_USER_ID));
+        assertTrue(mLocalService.setLockCredentialWithToken(
+                pattern, handle0, token0, PRIMARY_USER_ID));
+        assertTrue(mLocalService.isEscrowTokenActive(handle1, PRIMARY_USER_ID));
+        assertTrue(mLocalService.setLockCredentialWithToken(
+                pattern, handle1, token1, PRIMARY_USER_ID));
+        assertTrue(mLocalService.isEscrowTokenActive(handle2, PRIMARY_USER_ID));
+        assertTrue(mLocalService.setLockCredentialWithToken(
+                pattern, handle2, token2, PRIMARY_USER_ID));
+    }
+
+    @Test
     public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception {
         LockscreenCredential password = newPassword("password");
         LockscreenCredential pattern = newPattern("123654");
@@ -494,8 +524,8 @@
         reset(mDevicePolicyManager);
 
         long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
-        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
-                .getResponseCode();
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
         mService.onCleanupUser(PRIMARY_USER_ID);
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 13c3919..fb01ff6 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -108,14 +108,13 @@
 import android.content.pm.Signature;
 import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicy;
-import android.net.NetworkState;
+import android.net.NetworkStateSnapshot;
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
@@ -242,8 +241,7 @@
 
     private @Mock IActivityManager mActivityManager;
     private @Mock INetworkManagementService mNetworkManager;
-    private @Mock IConnectivityManager mConnManager;
-    private @Mock ConnectivityManager mConnectivityManager;
+    private @Mock ConnectivityManager mConnManager;
     private @Mock NotificationManager mNotifManager;
     private @Mock PackageManager mPackageManager;
     private @Mock IPackageManager mIpm;
@@ -361,7 +359,7 @@
                     case Context.NOTIFICATION_SERVICE:
                         return mNotifManager;
                     case Context.CONNECTIVITY_SERVICE:
-                        return mConnectivityManager;
+                        return mConnManager;
                     case Context.USER_SERVICE:
                         return mUserManager;
                     default:
@@ -390,7 +388,7 @@
         mFutureIntent = newRestrictBackgroundChangedFuture();
         mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
                 mNetworkManager, mIpm, mClock, mPolicyDir, true);
-        mService.bindConnectivityManager(mConnManager);
+        mService.bindConnectivityManager();
         mPolicyListener = new NetworkPolicyListenerAnswer(mService);
 
         // Sets some common expectations.
@@ -429,7 +427,7 @@
         when(mUserManager.getUsers()).thenReturn(buildUserInfoList());
         when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true);
         when(mNetworkManager.setDataSaverModeEnabled(anyBoolean())).thenReturn(true);
-        doNothing().when(mConnectivityManager)
+        doNothing().when(mConnManager)
                 .registerNetworkCallback(any(), mNetworkCallbackCaptor.capture());
 
         // Create the expected carrier config
@@ -1072,7 +1070,7 @@
     @FlakyTest
     @Test
     public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
-        NetworkState[] state = null;
+        List<NetworkStateSnapshot> snapshots = null;
         NetworkStats stats = null;
 
         final int CYCLE_DAY = 15;
@@ -1084,8 +1082,8 @@
 
         // first, pretend that wifi network comes online. no policy active,
         // which means we shouldn't push limit to interface.
-        state = new NetworkState[] { buildWifi() };
-        when(mConnManager.getAllNetworkState()).thenReturn(state);
+        snapshots = List.of(buildWifi());
+        when(mConnManager.getAllNetworkStateSnapshot()).thenReturn(snapshots);
 
         mPolicyListener.expect().onMeteredIfacesChanged(any());
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -1093,7 +1091,7 @@
 
         // now change cycle to be on 15th, and test in early march, to verify we
         // pick cycle day in previous month.
-        when(mConnManager.getAllNetworkState()).thenReturn(state);
+        when(mConnManager.getAllNetworkStateSnapshot()).thenReturn(snapshots);
 
         // pretend that 512 bytes total have happened
         stats = new NetworkStats(getElapsedRealtime(), 1)
@@ -1339,7 +1337,7 @@
 
     @Test
     public void testMeteredNetworkWithoutLimit() throws Exception {
-        NetworkState[] state = null;
+        List<NetworkStateSnapshot> snapshots = null;
         NetworkStats stats = null;
 
         final long TIME_FEB_15 = 1171497600000L;
@@ -1349,12 +1347,12 @@
         setCurrentTimeMillis(TIME_MAR_10);
 
         // bring up wifi network with metered policy
-        state = new NetworkState[] { buildWifi() };
+        snapshots = List.of(buildWifi());
         stats = new NetworkStats(getElapsedRealtime(), 1)
                 .insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
 
         {
-            when(mConnManager.getAllNetworkState()).thenReturn(state);
+            when(mConnManager.getAllNetworkStateSnapshot()).thenReturn(snapshots);
             when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
                     currentTimeMillis())).thenReturn(stats.getTotalBytes());
 
@@ -1477,7 +1475,8 @@
     }
 
     private PersistableBundle setupUpdateMobilePolicyCycleTests() throws RemoteException {
-        when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
+        when(mConnManager.getAllNetworkStateSnapshot())
+                .thenReturn(new ArrayList<NetworkStateSnapshot>());
 
         setupTelephonySubscriptionManagers(FAKE_SUB_ID, FAKE_SUBSCRIBER_ID);
 
@@ -1489,7 +1488,8 @@
 
     @Test
     public void testUpdateMobilePolicyCycleWithNullConfig() throws RemoteException {
-        when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
+        when(mConnManager.getAllNetworkStateSnapshot())
+                .thenReturn(new ArrayList<NetworkStateSnapshot>());
 
         setupTelephonySubscriptionManagers(FAKE_SUB_ID, FAKE_SUBSCRIBER_ID);
 
@@ -1720,7 +1720,7 @@
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             expectMobileDefaults();
-            expectNetworkState(true /* roaming */);
+            expectNetworkStateSnapshot(true /* roaming */);
 
             mService.updateNetworks();
 
@@ -1749,7 +1749,7 @@
             // Capabilities change to roaming
             final ConnectivityManager.NetworkCallback callback = mNetworkCallbackCaptor.getValue();
             assertNotNull(callback);
-            expectNetworkState(true /* roaming */);
+            expectNetworkStateSnapshot(true /* roaming */);
             callback.onCapabilitiesChanged(
                     new Network(TEST_NET_ID),
                     buildNetworkCapabilities(TEST_SUB_ID, true /* roaming */));
@@ -2035,14 +2035,14 @@
         mService.setNetworkPolicies(policies);
     }
 
-    private static NetworkState buildWifi() {
+    private static NetworkStateSnapshot buildWifi() {
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
         final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
         networkCapabilities.addTransportType(TRANSPORT_WIFI);
         networkCapabilities.setSSID(TEST_SSID);
-        return new NetworkState(TYPE_WIFI, prop, networkCapabilities, new Network(TEST_NET_ID),
-                null);
+        return new NetworkStateSnapshot(new Network(TEST_NET_ID), networkCapabilities, prop,
+                null /*subscriberId*/, TYPE_WIFI);
     }
 
     private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception {
@@ -2059,15 +2059,14 @@
                 PackageManager.PERMISSION_DENIED);
     }
 
-    private void expectNetworkState(boolean roaming) throws Exception {
+    private void expectNetworkStateSnapshot(boolean roaming) throws Exception {
         when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
                 .thenReturn(mCarrierConfig);
-        when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
-                new NetworkState(TYPE_MOBILE,
-                        buildLinkProperties(TEST_IFACE),
-                        buildNetworkCapabilities(TEST_SUB_ID, roaming),
-                        new Network(TEST_NET_ID), TEST_IMSI)
-        });
+        List<NetworkStateSnapshot> snapshots = List.of(new NetworkStateSnapshot(
+                new Network(TEST_NET_ID),
+                buildNetworkCapabilities(TEST_SUB_ID, roaming),
+                buildLinkProperties(TEST_IFACE), TEST_IMSI, TYPE_MOBILE));
+        when(mConnManager.getAllNetworkStateSnapshot()).thenReturn(snapshots);
     }
 
     private void expectDefaultCarrierConfig() throws Exception {
@@ -2078,7 +2077,7 @@
     private TelephonyManager expectMobileDefaults() throws Exception {
         TelephonyManager tmSub = setupTelephonySubscriptionManagers(TEST_SUB_ID, TEST_IMSI);
         doNothing().when(tmSub).setPolicyDataEnabled(anyBoolean());
-        expectNetworkState(false /* roaming */);
+        expectNetworkStateSnapshot(false /* roaming */);
         return tmSub;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index ea27331..6e5fbd0 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -764,7 +764,7 @@
         createService();
         startSystem();
 
-        mService.getBinderServiceInstance().userActivity(mClock.now(),
+        mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(),
                 PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
         verify(mInattentiveSleepWarningControllerMock, never()).show();
 
@@ -773,7 +773,7 @@
         verify(mInattentiveSleepWarningControllerMock, never()).dismiss(anyBoolean());
         when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true);
 
-        mService.getBinderServiceInstance().userActivity(mClock.now(),
+        mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(),
                 PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
         verify(mInattentiveSleepWarningControllerMock, times(1)).dismiss(true);
     }
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 4634e12..2a3c2c4 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -100,16 +100,17 @@
             return EFFECT_DURATION;
         }
 
-        public void compose(VibrationEffect.Composition.PrimitiveEffect[] effect,
+        public long compose(VibrationEffect.Composition.PrimitiveEffect[] effect,
                 long vibrationId) {
             VibrationEffect.Composed composed = new VibrationEffect.Composed(Arrays.asList(effect));
             mEffects.add(composed);
             applyLatency();
-            long duration = EFFECT_DURATION * effect.length;
+            long duration = 0;
             for (VibrationEffect.Composition.PrimitiveEffect e : effect) {
-                duration += e.delay;
+                duration += EFFECT_DURATION + e.delay;
             }
             scheduleListener(duration, vibrationId);
+            return duration;
         }
 
         public void setExternalControl(boolean enabled) {
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
index 815aa8e..bad3e4c 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -202,7 +203,7 @@
 
         VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked)
                 VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
-        controller.on(effect, 11);
+        assertEquals(10L, controller.on(effect, 11));
 
         assertTrue(controller.isVibrating());
         verify(mNativeWrapperMock).perform(eq((long) VibrationEffect.EFFECT_CLICK),
@@ -212,13 +213,14 @@
     @Test
     public void on_withComposed_performsEffect() {
         mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        when(mNativeWrapperMock.compose(any(), anyLong())).thenReturn(15L);
         VibratorController controller = createController();
 
         VibrationEffect.Composed effect = (VibrationEffect.Composed)
                 VibrationEffect.startComposition()
                         .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
                         .compose();
-        controller.on(effect, 12);
+        assertEquals(15L, controller.on(effect, 12));
 
         ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor =
                 ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class);
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/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 137cf65..09a436c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -518,6 +518,7 @@
                 TYPE_WALLPAPER, TYPE_APPLICATION);
         final WindowState wallpaper = windows[0];
         assertTrue(wallpaper.mIsWallpaper);
+        wallpaper.mToken.asWallpaperToken().setVisibility(false);
         // By default WindowState#mWallpaperVisible is false.
         assertFalse(wallpaper.isVisible());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index b73c664..2f1d7eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -655,7 +655,7 @@
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
 
         // Portrait fixed app without max aspect.
-        prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
+        prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT);
 
         final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
         final Rect activityBounds = new Rect(mActivity.getBounds());
@@ -676,6 +676,96 @@
     }
 
     @Test
+    public void testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMinAspectRatio() {
+        // Set up a display in landscape and ignoring orientation request.
+        setUpDisplaySizeWithApp(2800, 1400);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
+        // orientation letterbox.
+        mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(1.1f);
+        mActivity.info.minAspectRatio = 3;
+        prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT);
+
+        final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+        final Rect activityBounds = new Rect(mActivity.getBounds());
+
+        // Display shouldn't be rotated.
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
+                mActivity.mDisplayContent.getLastOrientation());
+        assertTrue(displayBounds.width() > displayBounds.height());
+
+        // App should launch in fixed orientation letterbox.
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        assertFalse(mActivity.inSizeCompatMode());
+
+        // Activity bounds should respect minimum aspect ratio for activity.
+        assertEquals(displayBounds.height(), activityBounds.height());
+        assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.minAspectRatio),
+                activityBounds.width());
+    }
+
+    @Test
+    public void testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMaxAspectRatio() {
+        // Set up a display in landscape and ignoring orientation request.
+        setUpDisplaySizeWithApp(2800, 1400);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        // Portrait fixed app with max aspect ratio lower that aspect ratio override for fixed
+        // orientation letterbox.
+        mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(3);
+        prepareUnresizable(mActivity, /* maxAspect= */ 2, SCREEN_ORIENTATION_PORTRAIT);
+
+        final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+        final Rect activityBounds = new Rect(mActivity.getBounds());
+
+        // Display shouldn't be rotated.
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
+                mActivity.mDisplayContent.getLastOrientation());
+        assertTrue(displayBounds.width() > displayBounds.height());
+
+        // App should launch in fixed orientation letterbox.
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        assertFalse(mActivity.inSizeCompatMode());
+
+        // Activity bounds should respect maximum aspect ratio for activity.
+        assertEquals(displayBounds.height(), activityBounds.height());
+        assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.maxAspectRatio),
+                activityBounds.width());
+    }
+
+    @Test
+    public void testDisplayIgnoreOrientationRequest_fixedOrientationAppWithAspectRatioOverride() {
+        // Set up a display in landscape and ignoring orientation request.
+        setUpDisplaySizeWithApp(2800, 1400);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
+        // orientation letterbox.
+        final float fixedOrientationLetterboxAspectRatio = 1.1f;
+        mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(
+                fixedOrientationLetterboxAspectRatio);
+        prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
+
+        final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+        final Rect activityBounds = new Rect(mActivity.getBounds());
+
+        // Display shouldn't be rotated.
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
+                mActivity.mDisplayContent.getLastOrientation());
+        assertTrue(displayBounds.width() > displayBounds.height());
+
+        // App should launch in fixed orientation letterbox.
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        assertFalse(mActivity.inSizeCompatMode());
+
+        // Activity bounds should respect aspect ratio override for fixed orientation letterbox.
+        assertEquals(displayBounds.height(), activityBounds.height());
+        assertEquals((int) Math.rint(displayBounds.height() / fixedOrientationLetterboxAspectRatio),
+                activityBounds.width());
+    }
+
+    @Test
     public void
             testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() {
         // Set up a display in landscape and ignoring orientation request.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index fb2272e..5239462 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -473,6 +473,68 @@
                 mDefaultDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea);
     }
 
+    @Test
+    public void testRecalculateFreeformInitialBoundsWithOverrideDisplayArea() {
+        final TestDisplayContent freeformDisplay = createNewDisplayContent(
+                WINDOWING_MODE_FREEFORM);
+        final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(freeformDisplay,
+                mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
+        secondaryDisplayArea.setBounds(DISPLAY_BOUNDS.width() / 2, 0,
+                        DISPLAY_BOUNDS.width(), DISPLAY_BOUNDS.height());
+        final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+        launchRoot.mCreatedByOrganizer = true;
+        secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM },
+                new int[] { ACTIVITY_TYPE_STANDARD });
+        final Rect secondaryDAStableBounds = new Rect();
+        secondaryDisplayArea.getStableRect(secondaryDAStableBounds);
+
+        // Specify the display and provide a layout so that it will be set to freeform bounds.
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchDisplayId(freeformDisplay.getDisplayId());
+        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+                .setGravity(Gravity.LEFT).build();
+
+        assertEquals(RESULT_CONTINUE,
+                new CalculateRequestBuilder().setOptions(options).setLayout(layout).calculate());
+
+        assertEquals(secondaryDisplayArea, mResult.mPreferredTaskDisplayArea);
+        assertTrue(secondaryDAStableBounds.contains(mResult.mBounds));
+    }
+
+    @Test
+    public void testRecalculateFreeformInitialBoundsWithOverrideDisplayArea_unresizableApp() {
+        mAtm.mSupportsNonResizableMultiWindow = true;
+
+        final TestDisplayContent freeformDisplay = createNewDisplayContent(
+                WINDOWING_MODE_FREEFORM);
+        final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(freeformDisplay,
+                mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
+        secondaryDisplayArea.setBounds(DISPLAY_BOUNDS.width() / 2, 0,
+                DISPLAY_BOUNDS.width(), DISPLAY_BOUNDS.height());
+        final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+        launchRoot.mCreatedByOrganizer = true;
+        secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM },
+                new int[] { ACTIVITY_TYPE_STANDARD });
+        final Rect secondaryDAStableBounds = new Rect();
+        secondaryDisplayArea.getStableRect(secondaryDAStableBounds);
+
+        // The bounds will get updated for unresizable with opposite orientation on freeform display
+        final Rect displayBounds = new Rect(freeformDisplay.getBounds());
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+        mActivity.info.screenOrientation = displayBounds.width() > displayBounds.height()
+                ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE;
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchDisplayId(freeformDisplay.getDisplayId());
+
+        assertEquals(RESULT_CONTINUE,
+                new CalculateRequestBuilder().setOptions(options).calculate());
+
+        assertEquals(secondaryDisplayArea, mResult.mPreferredTaskDisplayArea);
+        assertTrue(secondaryDAStableBounds.contains(mResult.mBounds));
+    }
+
     // =====================================
     // Launch Windowing Mode Related Tests
     // =====================================
@@ -1365,8 +1427,8 @@
         // This test case requires a relatively big app bounds to ensure the default size calculated
         // by letterbox won't be too small to hold the minimum width/height.
         configInsetsState(
-                freeformDisplay.getInsetsStateController().getRawInsetsState(),
-                DISPLAY_BOUNDS, new Rect(10, 10, 1910, 1070));
+                freeformDisplay.getInsetsStateController().getRawInsetsState(), freeformDisplay,
+                new Rect(10, 10, 1910, 1070));
 
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchDisplayId(freeformDisplay.mDisplayId);
@@ -1587,15 +1649,17 @@
         display.setBounds(DISPLAY_BOUNDS);
         display.getConfiguration().densityDpi = DENSITY_DEFAULT;
         display.getConfiguration().orientation = ORIENTATION_LANDSCAPE;
-        configInsetsState(display.getInsetsStateController().getRawInsetsState(),
-                DISPLAY_BOUNDS, DISPLAY_STABLE_BOUNDS);
+        configInsetsState(display.getInsetsStateController().getRawInsetsState(), display,
+                DISPLAY_STABLE_BOUNDS);
         return display;
     }
 
     /**
      * Creates insets sources so that we can get the expected stable frame.
      */
-    private static void configInsetsState(InsetsState state, Rect displayFrame, Rect stableFrame) {
+    private static void configInsetsState(InsetsState state, DisplayContent display,
+            Rect stableFrame) {
+        final Rect displayFrame = display.getBounds();
         final int dl = displayFrame.left;
         final int dt = displayFrame.top;
         final int dr = displayFrame.right;
@@ -1618,6 +1682,8 @@
         if (sb < db) {
             state.getSource(ITYPE_NAVIGATION_BAR).setFrame(dl, sb, dr, db);
         }
+        // Recompute config and push to children.
+        display.onRequestedOverrideConfigurationChanged(display.getConfiguration());
     }
 
     private ActivityRecord createSourceActivity(TestDisplayContent display) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index c3eb5c4..ecb8b60 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -16,8 +16,11 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -257,4 +260,21 @@
         task.resolveOverrideConfiguration(parentConfig);
         assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
     }
+
+    @Test
+    public void testHandlesOrientationChangeFromDescendant() {
+        final Task rootTask = createTaskStackOnDisplay(WINDOWING_MODE_MULTI_WINDOW,
+                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task leafTask1 = createTaskInStack(rootTask, 0 /* userId */);
+        final Task leafTask2 = createTaskInStack(rootTask, 0 /* userId */);
+        leafTask1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_HOME);
+        leafTask2.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_STANDARD);
+
+        assertEquals(leafTask2, rootTask.getTopChild());
+        assertTrue(rootTask.handlesOrientationChangeFromDescendant());
+        // Treat orientation request from home as handled.
+        assertTrue(leafTask1.handlesOrientationChangeFromDescendant());
+        // Orientation request from standard activity in multi window will not be handled.
+        assertFalse(leafTask2.handlesOrientationChangeFromDescendant());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 401ace03c..79ef868 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -25,11 +25,14 @@
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
+import static android.window.TransitionInfo.isIndependent;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
@@ -316,9 +319,9 @@
                 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */));
         final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
                 "wallpaperWindow");
-        wallpaperWindow.mWallpaperVisible = false;
+        wallpaperWindowToken.setVisibleRequested(false);
         transition.collect(wallpaperWindowToken);
-        wallpaperWindow.mWallpaperVisible = true;
+        wallpaperWindowToken.setVisibleRequested(true);
         wallpaperWindow.mHasSurface = true;
 
         // doesn't matter which order collected since participants is a set
@@ -343,6 +346,76 @@
                 tasks[showWallpaperTask].mRemoteToken.toWindowContainerToken()).getFlags());
     }
 
+    @Test
+    public void testIndependent() {
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
+        ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+        ArraySet<WindowContainer> participants = transition.mParticipants;
+        ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
+
+        final Task openTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task openInOpenTask = createTaskInStack(openTask, 0);
+        final ActivityRecord openInOpen = createActivityRecord(openInOpenTask);
+
+        final Task changeTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task changeInChangeTask = createTaskInStack(changeTask, 0);
+        final Task openInChangeTask = createTaskInStack(changeTask, 0);
+        final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask);
+        final ActivityRecord openInChange = createActivityRecord(openInChangeTask);
+        // set organizer for everything so that they all get added to transition info
+        for (Task t : new Task[]{
+                openTask, openInOpenTask, changeTask, changeInChangeTask, openInChangeTask}) {
+            t.mTaskOrganizer = mockOrg;
+        }
+
+        // Start states.
+        changes.put(openTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+        changes.put(changeTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+        changes.put(openInOpenTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+        changes.put(openInChangeTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+        changes.put(changeInChangeTask,
+                new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+        changes.put(openInOpen, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+        changes.put(openInChange, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+        changes.put(changeInChange, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+        fillChangeMap(changes, openTask);
+        // End states.
+        changeInChange.mVisibleRequested = true;
+        openInOpen.mVisibleRequested = true;
+        openInChange.mVisibleRequested = true;
+
+        int transit = TRANSIT_OLD_TASK_OPEN;
+        int flags = 0;
+
+        // Check full promotion from leaf
+        participants.add(changeInChange);
+        participants.add(openInOpen);
+        participants.add(openInChange);
+        // Explicitly add changeTask (to test independence with parents)
+        participants.add(changeTask);
+        ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes);
+        TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+        // Root changes should always be considered independent
+        assertTrue(isIndependent(
+                info.getChange(openTask.mRemoteToken.toWindowContainerToken()), info));
+        assertTrue(isIndependent(
+                info.getChange(changeTask.mRemoteToken.toWindowContainerToken()), info));
+
+        // Children of a open/close change are not independent
+        assertFalse(isIndependent(
+                info.getChange(openInOpenTask.mRemoteToken.toWindowContainerToken()), info));
+
+        // Non-root changes are not independent
+        assertFalse(isIndependent(
+                info.getChange(changeInChangeTask.mRemoteToken.toWindowContainerToken()), info));
+
+        // open/close within a change are independent
+        assertTrue(isIndependent(
+                info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info));
+    }
+
     /** Fill the change map with all the parents of top. Change maps are usually fully populated */
     private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes,
             WindowContainer top) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index d1d0ac6..8b4e947 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -24,6 +24,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OPEN;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -49,7 +51,9 @@
 import android.view.InsetsState;
 import android.view.RoundedCorners;
 import android.view.Surface;
+import android.view.SurfaceControl;
 import android.view.WindowManager;
+import android.window.ITransitionPlayer;
 
 import androidx.test.filters.SmallTest;
 
@@ -135,7 +139,8 @@
         int expectedWidth = (int) (wallpaperWidth * (displayHeight / (double) wallpaperHeight));
 
         // Check that the wallpaper is correctly scaled
-        assertEquals(new Rect(0, 0, expectedWidth, displayHeight), wallpaperWindow.getFrame());
+        assertEquals(expectedWidth, wallpaperWindow.getFrame().width());
+        assertEquals(displayHeight, wallpaperWindow.getFrame().height());
         Rect portraitFrame = wallpaperWindow.getFrame();
 
         // Rotate the display
@@ -297,6 +302,46 @@
         assertFalse(mAppWindow.mActivityRecord.hasFixedRotationTransform());
     }
 
+    @Test
+    public void testWallpaperTokenVisibility() {
+        final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
+        final WallpaperWindowToken token = new WallpaperWindowToken(mWm, mock(IBinder.class),
+                true, dc, true /* ownerCanManageAppTokens */);
+        final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, token,
+                "wallpaperWindow");
+        wallpaperWindow.setHasSurface(true);
+
+        // Set-up mock shell transitions
+        final IBinder mockBinder = mock(IBinder.class);
+        final ITransitionPlayer mockPlayer = mock(ITransitionPlayer.class);
+        doReturn(mockBinder).when(mockPlayer).asBinder();
+        mWm.mAtmService.getTransitionController().registerTransitionPlayer(mockPlayer);
+
+        Transition transit =
+                mWm.mAtmService.getTransitionController().createTransition(TRANSIT_OPEN);
+
+        // wallpaper windows are immediately visible when set to visible even during a transition
+        token.setVisibility(true);
+        assertTrue(wallpaperWindow.isVisible());
+        assertTrue(token.isVisibleRequested());
+        assertTrue(token.isVisible());
+        mWm.mAtmService.getTransitionController().abort(transit);
+
+        // In a transition, setting invisible should ONLY set requestedVisible false; otherwise
+        // wallpaper should remain "visible" until transition is over.
+        transit = mWm.mAtmService.getTransitionController().createTransition(TRANSIT_CLOSE);
+        transit.start();
+        token.setVisibility(false);
+        assertTrue(wallpaperWindow.isVisible());
+        assertFalse(token.isVisibleRequested());
+        assertTrue(token.isVisible());
+
+        transit.onTransactionReady(transit.getSyncId(), mock(SurfaceControl.Transaction.class));
+        transit.finishTransition();
+        assertFalse(wallpaperWindow.isVisible());
+        assertFalse(token.isVisible());
+    }
+
     private WindowState createWallpaperTargetWindow(DisplayContent dc) {
         final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
                 .setTask(dc.getDefaultTaskDisplayArea().getRootHomeTask())
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ebc5c4f..1f38f46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -245,6 +245,9 @@
 
     private WindowToken createWindowToken(
             DisplayContent dc, int windowingMode, int activityType, int type) {
+        if (type == TYPE_WALLPAPER) {
+            return createWallpaperToken(dc);
+        }
         if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
             return createTestWindowToken(type, dc);
         }
@@ -252,6 +255,11 @@
         return createActivityRecord(dc, windowingMode, activityType);
     }
 
+    private WindowToken createWallpaperToken(DisplayContent dc) {
+        return new WallpaperWindowToken(mWm, mock(IBinder.class), true /* explicit */, dc,
+                true /* ownerCanManageAppTokens */);
+    }
+
     WindowState createAppWindow(Task task, int type, String name) {
         final ActivityRecord activity = createNonAttachedActivityRecord(task.getDisplayContent());
         task.addChild(activity, 0);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index b1e6683..f35b9e2 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -303,7 +303,9 @@
                 // FLUSH_TO_DISK is a private event.
                 && event.mEventType != Event.FLUSH_TO_DISK
                 // DEVICE_SHUTDOWN is added to event list after reboot.
-                && event.mEventType != Event.DEVICE_SHUTDOWN) {
+                && event.mEventType != Event.DEVICE_SHUTDOWN
+                // We aren't interested in every instance of the APP_COMPONENT_USED event.
+                && event.mEventType != Event.APP_COMPONENT_USED) {
             currentDailyStats.addEvent(event);
         }
 
@@ -1176,6 +1178,8 @@
                 return "USER_STOPPED";
             case Event.LOCUS_ID_SET:
                 return "LOCUS_ID_SET";
+            case Event.APP_COMPONENT_USED:
+                return "APP_COMPONENT_USED";
             default:
                 return "UNKNOWN_TYPE_" + eventType;
         }
diff --git a/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
index c7e7cd5..179248d 100644
--- a/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
+++ b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
@@ -65,6 +65,11 @@
     // compact form of the via header key
     private static final String VIA_SIP_HEADER_KEY_COMPACT = "v";
 
+    // call-id header key
+    private static final String CALL_ID_SIP_HEADER_KEY = "call-id";
+    // compact form of the call-id header key
+    private static final String CALL_ID_SIP_HEADER_KEY_COMPACT = "i";
+
     /**
      * @return true if the SIP message start line is considered a request (based on known request
      * methods).
@@ -124,6 +129,17 @@
         return null;
     }
 
+    /**
+     * Return the call-id header key's associated value.
+     * @param headerString The string containing the headers of the SIP message.
+     */
+    public static String getCallId(String headerString) {
+        // search for the call-Id header, there should only be one in the header.
+        List<Pair<String, String>> headers = parseHeaders(headerString, true,
+                CALL_ID_SIP_HEADER_KEY, CALL_ID_SIP_HEADER_KEY_COMPACT);
+        return !headers.isEmpty() ? headers.get(0).second : null;
+    }
+
     private static String[] splitStartLineAndVerify(String startLine) {
         String[] splitLine = startLine.split(" ");
         if (isStartLineMalformed(splitLine)) return null;
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index 5b5570b..2f89bfb 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -30,8 +30,8 @@
  * necessarily a precise or accurate representation of the current state and should be treated
  * accordingly.
  * To be notified of changes in TelephonyDisplayInfo, use
- * {@link TelephonyManager#registerPhoneStateListener} with a {@link PhoneStateListener}
- * that implements {@link PhoneStateListener.DisplayInfoChangedListener}.
+ * {@link TelephonyManager#registerTelephonyCallback} with a {@link TelephonyCallback}
+ * that implements {@link TelephonyCallback.DisplayInfoListener}.
  * Override the onDisplayInfoChanged() method to handle the broadcast.
  */
 public final class TelephonyDisplayInfo implements Parcelable {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b46440d..c48bd21 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5949,25 +5949,20 @@
      * @param events The telephony state(s) of interest to the listener,
      *               as a bitwise-OR combination of {@link PhoneStateListener}
      *               LISTEN_ flags.
-     * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}.
+     * @deprecated Use {@link #registerTelephonyCallback(Executor, TelephonyCallback)}.
      */
     @Deprecated
     public void listen(PhoneStateListener listener, int events) {
-        if (!listener.isExecutorSet()) {
-            throw new IllegalStateException("PhoneStateListener should be created on a thread "
-                    + "with Looper.myLooper() != null");
-        }
-        boolean notifyNow = getITelephony() != null;
-        mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
-        if (mTelephonyRegistryMgr != null) {
-            if (events != PhoneStateListener.LISTEN_NONE) {
-                mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(mSubId,
-                        getOpPackageName(), getAttributionTag(), listener, events, notifyNow);
-            } else {
-                unregisterPhoneStateListener(listener);
-            }
+        if (mContext == null) return;
+        boolean notifyNow = (getITelephony() != null);
+        TelephonyRegistryManager telephonyRegistry =
+                (TelephonyRegistryManager)
+                        mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+        if (telephonyRegistry != null) {
+            telephonyRegistry.listenFromListener(mSubId, getOpPackageName(),
+                    getAttributionTag(), listener, events, notifyNow);
         } else {
-            throw new IllegalStateException("telephony service is null.");
+            Rlog.w(TAG, "telephony registry not ready.");
         }
     }
 
@@ -15049,66 +15044,69 @@
     }
 
     /**
-     * Registers a listener object to receive notification of changes
-     * in specified telephony states.
+     * Registers a callback object to receive notification of changes in specified telephony states.
      * <p>
-     * To register a listener, pass a {@link PhoneStateListener} which implements
+     * To register a callback, pass a {@link TelephonyCallback} which implements
      * interfaces of events. For example,
-     * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements
-     * {@link PhoneStateListener.ServiceStateChangedListener}.
+     * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+     * {@link TelephonyCallback.ServiceStateListener}.
      *
      * At registration, and when a specified telephony state changes, the telephony manager invokes
-     * the appropriate callback method on the listener object and passes the current (updated)
+     * the appropriate callback method on the callback object and passes the current (updated)
      * values.
      * <p>
      *
      * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
      * applies to the given subId. Otherwise, applies to
-     * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds,
-     * pass a separate listener object to each TelephonyManager object created with
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}. To register events for multiple
+     * subIds, pass a separate callback object to each TelephonyManager object created with
      * {@link #createForSubscriptionId}.
      *
      * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
      * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
      * {@link SecurityException} will be thrown otherwise.
      *
-     * This API should be used sparingly -- large numbers of listeners will cause system
-     * instability. If a process has registered too many listeners without unregistering them, it
-     * may encounter an {@link IllegalStateException} when trying to register more listeners.
+     * This API should be used sparingly -- large numbers of callbacks will cause system
+     * instability. If a process has registered too many callbacks without unregistering them, it
+     * may encounter an {@link IllegalStateException} when trying to register more callbacks.
      *
      * @param executor The executor of where the callback will execute.
-     * @param listener The {@link PhoneStateListener} object to register.
+     * @param callback The {@link TelephonyCallback} object to register.
      */
-    public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor,
-            @NonNull PhoneStateListener listener) {
-        if (executor == null || listener == null) {
-            throw new IllegalArgumentException("PhoneStateListener and executor must be non-null");
+    public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull TelephonyCallback callback) {
+        if (executor == null || callback == null) {
+            throw new IllegalArgumentException("TelephonyCallback and executor must be non-null");
         }
         mTelephonyRegistryMgr = (TelephonyRegistryManager)
                 mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
         if (mTelephonyRegistryMgr != null) {
-            mTelephonyRegistryMgr.registerPhoneStateListener(executor, mSubId,
-                    getOpPackageName(), getAttributionTag(), listener, getITelephony() != null);
+            mTelephonyRegistryMgr.registerTelephonyCallback(executor, mSubId, getOpPackageName(),
+                    getAttributionTag(), callback, getITelephony() != null);
         } else {
             throw new IllegalStateException("telephony service is null.");
         }
     }
 
     /**
-     * Unregister an existing {@link PhoneStateListener}.
+     * Unregister an existing {@link TelephonyCallback}.
      *
-     * @param listener The {@link PhoneStateListener} object to unregister.
+     * @param callback The {@link TelephonyCallback} object to unregister.
      */
-    public void unregisterPhoneStateListener(@NonNull PhoneStateListener listener) {
+    public void unregisterTelephonyCallback(@NonNull TelephonyCallback callback) {
 
         if (mContext == null) {
             throw new IllegalStateException("telephony service is null.");
         }
 
+        if (callback.callback == null) {
+            return;
+        }
+
         mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
         if (mTelephonyRegistryMgr != null) {
-            mTelephonyRegistryMgr.unregisterPhoneStateListener(mSubId, getOpPackageName(),
-                    getAttributionTag(), listener, getITelephony() != null);
+            mTelephonyRegistryMgr.unregisterTelephonyCallback(mSubId, getOpPackageName(),
+                    getAttributionTag(), callback, getITelephony() != null);
         } else {
             throw new IllegalStateException("telephony service is null.");
         }
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index bdf628b..cedf48b 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -24,9 +24,14 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -39,6 +44,8 @@
 @SystemApi
 public final class RcsContactPresenceTuple implements Parcelable {
 
+    private static final String LOG_TAG = "RcsContactPresenceTuple";
+
     /**
      * The service ID used to indicate that service discovery via presence is available.
      * <p>
@@ -370,7 +377,8 @@
         }
 
         /**
-         * The optional SIP Contact URI associated with the PIDF tuple element.
+         * The optional SIP Contact URI associated with the PIDF tuple element if the network
+         * expects the user to use the URI instead of the contact URI to contact it.
          */
         public @NonNull Builder setContactUri(@NonNull Uri contactUri) {
             mPresenceTuple.mContactUri = contactUri;
@@ -381,8 +389,24 @@
          * The optional timestamp indicating the data and time of the status change of this tuple.
          * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
          * string per RFC3339.
+         * @hide
          */
         public @NonNull Builder setTimestamp(@NonNull String timestamp) {
+            try {
+                mPresenceTuple.mTimestamp =
+                        DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from);
+            } catch (DateTimeParseException e) {
+                Log.d(LOG_TAG, "Parse timestamp failed " + e);
+            }
+            return this;
+        }
+
+        /**
+         * The optional timestamp indicating the data and time of the status change of this tuple.
+         * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
+         * string per RFC3339.
+         */
+        public @NonNull Builder setTime(@NonNull Instant timestamp) {
             mPresenceTuple.mTimestamp = timestamp;
             return this;
         }
@@ -414,7 +438,7 @@
     }
 
     private Uri mContactUri;
-    private String mTimestamp;
+    private Instant mTimestamp;
     private @BasicStatus String mStatus;
 
     // The service information in the service-description element.
@@ -433,7 +457,7 @@
 
     private RcsContactPresenceTuple(Parcel in) {
         mContactUri = in.readParcelable(Uri.class.getClassLoader());
-        mTimestamp = in.readString();
+        mTimestamp = convertStringFormatTimeToInstant(in.readString());
         mStatus = in.readString();
         mServiceId = in.readString();
         mServiceVersion = in.readString();
@@ -444,7 +468,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeParcelable(mContactUri, flags);
-        out.writeString(mTimestamp);
+        out.writeString(convertInstantToStringFormat(mTimestamp));
         out.writeString(mStatus);
         out.writeString(mServiceId);
         out.writeString(mServiceVersion);
@@ -470,6 +494,26 @@
                 }
             };
 
+    // Convert the Instant to the string format
+    private String convertInstantToStringFormat(Instant instant) {
+        if (instant == null) {
+            return "";
+        }
+        return instant.toString();
+    }
+
+    // Convert the time string format to Instant
+    private @Nullable Instant convertStringFormatTimeToInstant(String timestamp) {
+        if (TextUtils.isEmpty(timestamp)) {
+            return null;
+        }
+        try {
+            return DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from);
+        } catch (DateTimeParseException e) {
+            return null;
+        }
+    }
+
     /** @return the status of the tuple element. */
     public @NonNull @BasicStatus String getStatus() {
         return mStatus;
@@ -490,8 +534,16 @@
         return mContactUri;
     }
 
-    /** @return the timestamp element contained in the tuple if it exists */
+    /**
+     * @return the timestamp element contained in the tuple if it exists
+     * @hide
+     */
     public @Nullable String getTimestamp() {
+        return (mTimestamp == null) ? null : mTimestamp.toString();
+    }
+
+    /** @return the timestamp element contained in the tuple if it exists */
+    public @Nullable Instant getTime() {
         return mTimestamp;
     }
 
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 9299fed..52d0f03 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -340,6 +340,7 @@
     }
 
     /**
+     * Retrieve the contact URI requested by the applications.
      * @return the URI representing the contact associated with the capabilities.
      */
     public @NonNull Uri getContactUri() {
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 09c07d3..815c08d 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -32,11 +32,12 @@
 import android.telephony.ims.aidl.IImsRcsController;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
 import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
-import android.telephony.ims.feature.RcsFeature;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -431,13 +432,15 @@
 
         /**
          * The pending request has completed successfully due to all requested contacts information
-         * being delivered.
+         * being delivered. The callback {@link #onCapabilitiesReceived(List)}
+         * for each contacts is required to be called before {@link #onComplete} is called.
          */
         void onComplete();
 
         /**
          * The pending request has resulted in an error and may need to be retried, depending on the
-         * error code.
+         * error code. The callback {@link #onCapabilitiesReceived(List)}
+         * for each contacts is required to be called before {@link #onError} is called.
          * @param errorCode The reason for the framework being unable to process the request.
          * @param retryIntervalMillis The time in milliseconds the requesting application should
          * wait before retrying, if non-zero.
@@ -484,7 +487,6 @@
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
             Manifest.permission.READ_CONTACTS})
     public void requestCapabilities(@NonNull List<Uri> contactNumbers,
@@ -550,6 +552,94 @@
     }
 
     /**
+     * Request the User Capability Exchange capabilities for one or more contacts.
+     * <p>
+     * This will return the cached capabilities of the contact and will not perform a capability
+     * poll on the network unless there are contacts being queried with stale information.
+     * <p>
+     * Be sure to check the availability of this feature using
+     * {@link ImsRcsManager#isAvailable(int, int)} and ensuring
+     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
+     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
+     * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
+     *
+     * @param contactNumbers A list of numbers that the capabilities are being requested for.
+     * @param executor The executor that will be used when the request is completed and the
+     *         {@link CapabilitiesCallback} is called.
+     * @param c A one-time callback for when the request for capabilities completes or there is an
+     *         error processing the request.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
+            Manifest.permission.READ_CONTACTS})
+    public void requestCapabilities(@NonNull Collection<Uri> contactNumbers,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull CapabilitiesCallback c) throws ImsException {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        if (contactNumbers == null) {
+            throw new IllegalArgumentException("Must include non-null contact number list.");
+        }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "requestCapabilities: IImsRcsController is null");
+            throw new ImsException("Can not find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+            @Override
+            public void onComplete() {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> c.onComplete());
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+        };
+
+        try {
+            imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
+                    mContext.getAttributionTag(), new ArrayList(contactNumbers), internalCallback);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.toString(), e.errorCode);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
      * Ignore the device cache and perform a capability discovery for one contact, also called
      * "availability fetch."
      * <p>
@@ -570,6 +660,10 @@
      * {@link CapabilitiesCallback} is called.
      * @param c A one-time callback for when the request for capabilities completes or there is
      * an error processing the request.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
     @SystemApi
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index 9cfa640..ad6d73c 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -19,6 +19,7 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Build;
 import android.os.Parcel;
@@ -46,6 +47,8 @@
     private final String mStartLine;
     private final String mHeaderSection;
     private final byte[] mContent;
+    private final String mViaBranchParam;
+    private final String mCallIdParam;
 
     /**
      * Represents a partially encoded SIP message.
@@ -63,6 +66,9 @@
         mStartLine = startLine;
         mHeaderSection = headerSection;
         mContent = content;
+
+        mViaBranchParam = SipMessageParsingUtils.getTransactionId(mHeaderSection);
+        mCallIdParam = SipMessageParsingUtils.getCallId(mHeaderSection);
     }
 
     /**
@@ -73,6 +79,8 @@
         mHeaderSection = source.readString();
         mContent = new byte[source.readInt()];
         source.readByteArray(mContent);
+        mViaBranchParam = source.readString();
+        mCallIdParam = source.readString();
     }
 
     /**
@@ -97,6 +105,25 @@
         return mContent;
     }
 
+    /**
+     * @return the branch parameter enclosed in the Via header key's value. See RFC 3261 section
+     * 20.42 for more information on the Via header. If {@code null}, then there was either no
+     * Via parameter found in this SIP message's headers or no branch parameter found in the
+     * Via header.
+     */
+    public @Nullable String getViaBranchParameter() {
+        return mViaBranchParam;
+    }
+
+    /**
+     * @return the value associated with the call-id header of this SIP message. See RFC 3261
+     * section 20.8 for more information on the call-id header. If {@code null}, then there was no
+     * call-id header found in this SIP message's headers.
+     */
+    public @Nullable String getCallIdParameter() {
+        return mCallIdParam;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -108,6 +135,8 @@
         dest.writeString(mHeaderSection);
         dest.writeInt(mContent.length);
         dest.writeByteArray(mContent);
+        dest.writeString(mViaBranchParam);
+        dest.writeString(mCallIdParam);
     }
 
     public static final @NonNull Creator<SipMessage> CREATOR = new Creator<SipMessage>() {
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index 9d91901..739946b 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -31,8 +31,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.internal.telephony.SipMessageParsingUtils;
-
 import java.util.ArrayList;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -188,7 +186,7 @@
     }
 
     private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) {
-        String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection());
+        String transactionId = m.getViaBranchParameter();
         if (TextUtils.isEmpty(transactionId)) {
             Log.w(LOG_TAG, "failure to parse SipMessage.");
             throw new IllegalArgumentException("Malformed SipMessage, can not determine "
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
index c877aca..3cd2726 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -32,8 +32,6 @@
 import android.util.ArraySet;
 import android.util.Log;
 
-import com.android.internal.telephony.SipMessageParsingUtils;
-
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.concurrent.Executor;
@@ -268,7 +266,7 @@
     }
 
     private void notifyLocalMessageFailedToSend(SipMessage m, int reason) {
-        String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection());
+        String transactionId = m.getViaBranchParameter();
         if (TextUtils.isEmpty(transactionId)) {
             Log.w(LOG_TAG, "sendMessage detected a malformed SipMessage and can not get a "
                     + "transaction ID.");
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 908869b..00c9168 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -31,6 +31,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -241,7 +242,7 @@
 
         /**
          * Notify the framework of the response to the SUBSCRIBE request from
-         * {@link #subscribeForCapabilities(List, SubscribeResponseCallback)}.
+         * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}.
          * <p>
          * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the
          * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate},
@@ -266,7 +267,7 @@
 
         /**
          * Notify the framework  of the response to the SUBSCRIBE request from
-         * {@link #subscribeForCapabilities(List, SubscribeResponseCallback)} that also
+         * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)} that also
          * includes a reason provided in the “reason” header. See RFC3326 for more
          * information.
          *
@@ -388,6 +389,7 @@
      * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
      * capabilities for.
      * @param cb The callback of the subscribe request.
+     * @hide
      */
     // executor used is defined in the constructor.
     @SuppressLint("ExecutorRegistration")
@@ -403,6 +405,40 @@
     }
 
     /**
+     * The user capabilities of one or multiple contacts have been requested by the framework.
+     * <p>
+     * The implementer must follow up this call with an
+     * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed.
+     * The response from the network to the SUBSCRIBE request must be sent back to the framework
+     * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}.
+     * As NOTIFY requests come in from the network, the requested contact’s capabilities should be
+     * sent back to the framework using
+     * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and
+     * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)}
+     * should be called with the presence information for the contacts specified.
+     * <p>
+     * Once the subscription is terminated,
+     * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
+     * framework to finish listening for NOTIFY responses.
+     *
+     * @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the
+     * UCE capabilities for.
+     * @param cb The callback of the subscribe request.
+     */
+    // executor used is defined in the constructor.
+    @SuppressLint("ExecutorRegistration")
+    public void subscribeForCapabilities(@NonNull Collection<Uri> uris,
+            @NonNull SubscribeResponseCallback cb) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation.");
+        try {
+            cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
+        } catch (ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
+    }
+
+    /**
      * The capabilities of this device have been updated and should be published to the network.
      * <p>
      * If this operation succeeds, network response updates should be sent to the framework using
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e87f3d9..7e04baa 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1200,15 +1200,6 @@
     void shutdownMobileRadios();
 
     /**
-     * Set phone radio type and access technology.
-     *
-     * @param rafs an RadioAccessFamily array to indicate all phone's
-     *        new radio access family. The length of RadioAccessFamily
-     *        must equ]]al to phone count.
-     */
-    void setRadioCapability(in RadioAccessFamily[] rafs);
-
-    /**
      * Get phone radio type and access technology.
      *
      * @param phoneId which phone you want to get
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 62ccb1a0..6bf4492 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -771,6 +771,24 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="StretchShaderActivity"
+                  android:label="RenderEffect/Stretch"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="com.android.test.hwui.TEST"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name="EdgeEffectStretchActivity"
+                  android:label="RenderEffect/EdgeEffect stretch"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="com.android.test.hwui.TEST"/>
+            </intent-filter>
+        </activity>
+
         <activity android:name="TextActivity"
              android:label="Text/Simple Text"
              android:theme="@android:style/Theme.NoTitleBar"
diff --git a/tests/HwAccelerationTest/res/layout/stretch_layout.xml b/tests/HwAccelerationTest/res/layout/stretch_layout.xml
new file mode 100644
index 0000000..df5f297
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/stretch_layout.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/scroll_view"
+    android:edgeEffectType="stretch"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <HorizontalScrollView
+            android:id="@+id/horizontal_scroll_view"
+            android:edgeEffectType="stretch"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+            <LinearLayout
+                android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent">
+                <ImageView
+                    android:layout_width="match_parent"
+                    android:layout_height="200dp"
+                    android:src="@drawable/sunset1"/>
+                <ImageView
+                    android:layout_width="match_parent"
+                    android:layout_height="200dp"
+                    android:src="@drawable/sunset1"/>
+                <ImageView
+                    android:layout_width="match_parent"
+                    android:layout_height="200dp"
+                    android:src="@drawable/sunset1"/>
+                <ImageView
+                    android:layout_width="match_parent"
+                    android:layout_height="200dp"
+                    android:src="@drawable/sunset1"/>
+                <ImageView
+                    android:layout_width="match_parent"
+                    android:layout_height="200dp"
+                    android:src="@drawable/sunset1"/>
+                <ImageView
+                    android:layout_width="match_parent"
+                    android:layout_height="200dp"
+                    android:src="@drawable/sunset1"/>
+            </LinearLayout>
+        </HorizontalScrollView>
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="200dp"
+            android:src="@drawable/sunset1"/>
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="200dp"
+            android:src="@drawable/sunset1"/>
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="200dp"
+            android:src="@drawable/sunset1"/>
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="200dp"
+            android:src="@drawable/sunset1"/>
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="200dp"
+            android:src="@drawable/sunset1"/>
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="200dp"
+            android:src="@drawable/sunset1"/>
+
+    </LinearLayout>
+</ScrollView>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java
new file mode 100644
index 0000000..f0e6299
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.HorizontalScrollView;
+import android.widget.ScrollView;
+
+public class EdgeEffectStretchActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.stretch_layout);
+        HorizontalScrollView hsv = findViewById(R.id.horizontal_scroll_view);
+        hsv.setStretchDistance(50f);
+
+        ScrollView sv = findViewById(R.id.scroll_view);
+        sv.setStretchDistance(50f);
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
new file mode 100644
index 0000000..9bd933a
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
@@ -0,0 +1,540 @@
+/*
+ * 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.test.hwui;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.RecordingCanvas;
+import android.graphics.Rect;
+import android.graphics.RenderEffect;
+import android.graphics.RuntimeShader;
+import android.graphics.Shader;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class StretchShaderActivity extends Activity {
+
+    private static final float MAX_STRETCH_INTENSITY = 1.5f;
+    private static final float STRETCH_AFFECTED_DISTANCE = 1.0f;
+
+    private float mScrollX = 0f;
+    private float mScrollY = 0f;
+
+    private float mMaxStretchIntensity = MAX_STRETCH_INTENSITY;
+    private float mStretchAffectedDistance = STRETCH_AFFECTED_DISTANCE;
+
+    private float mOverscrollX = 25f;
+    private float mOverscrollY = 25f;
+
+    private RuntimeShader mRuntimeShader;
+    private ImageView mImageView;
+    private ImageView mTestImageView;
+
+    private Bitmap mBitmap;
+
+    private StretchDrawable mStretchDrawable = new StretchDrawable();
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        LinearLayout linearLayout = new LinearLayout(this);
+        linearLayout.setOrientation(LinearLayout.VERTICAL);
+
+        mBitmap = ((BitmapDrawable) getDrawable(R.drawable.sunset1)).getBitmap();
+        mRuntimeShader = new RuntimeShader(SKSL, false);
+
+        BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP,
+                Shader.TileMode.CLAMP);
+        mRuntimeShader.setInputShader("uContentTexture", bitmapShader);
+
+        mImageView = new ImageView(this);
+
+        mImageView.setRenderEffect(RenderEffect.createShaderEffect(mRuntimeShader));
+        mImageView.setImageDrawable(new ColorDrawable(Color.CYAN));
+
+        TextView overscrollXText = new TextView(this);
+        overscrollXText.setText("Overscroll X");
+
+        SeekBar overscrollXBar = new SeekBar(this);
+        overscrollXBar.setProgress(0);
+        overscrollXBar.setMin(-50);
+        overscrollXBar.setMax(50);
+        overscrollXBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                mOverscrollX = progress;
+                overscrollXText.setText("Overscroll X: " + mOverscrollX);
+                updateShader();
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+
+            }
+        });
+
+        TextView overscrollYText = new TextView(this);
+        overscrollYText.setText("Overscroll Y");
+
+        SeekBar overscrollYBar = new SeekBar(this);
+        overscrollYBar.setProgress(0);
+        overscrollYBar.setMin(-50);
+        overscrollYBar.setMax(50);
+        overscrollYBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                mOverscrollY = progress;
+                overscrollYText.setText("Overscroll Y: " + mOverscrollY);
+                updateShader();
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+
+            }
+        });
+
+        TextView scrollXText = new TextView(this);
+        scrollXText.setText("Scroll X");
+        SeekBar scrollXSeekBar = new SeekBar(this);
+        scrollXSeekBar.setMin(0);
+        scrollXSeekBar.setMax(100);
+        scrollXSeekBar.setProgress(0);
+        scrollXSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                mScrollX = (progress / 100f);
+                scrollXText.setText("Scroll X: " + mScrollY);
+                updateShader();
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+
+            }
+        });
+
+        TextView scrollYText = new TextView(this);
+        scrollYText.setText("Scroll Y");
+        SeekBar scrollYSeekBar = new SeekBar(this);
+        scrollYSeekBar.setMin(0);
+        scrollYSeekBar.setMax(100);
+        scrollYSeekBar.setProgress(0);
+        scrollYSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                mScrollY = (progress / 100f);
+                scrollYText.setText("Scroll Y: " + mScrollY);
+                updateShader();
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+
+            }
+        });
+
+        TextView stretchIntensityText = new TextView(this);
+        int stretchProgress = (int) (mMaxStretchIntensity * 100);
+        stretchIntensityText.setText("StretchIntensity: " + mMaxStretchIntensity);
+        SeekBar stretchIntensitySeekbar = new SeekBar(this);
+        stretchIntensitySeekbar.setProgress(stretchProgress);
+        stretchIntensitySeekbar.setMin(1);
+        stretchIntensitySeekbar.setMax((int) (MAX_STRETCH_INTENSITY * 100));
+        stretchIntensitySeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                mMaxStretchIntensity = progress / 100f;
+                stretchIntensityText.setText("StretchIntensity: " + mMaxStretchIntensity);
+                updateShader();
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+
+            }
+        });
+
+        TextView stretchDistanceText = new TextView(this);
+        stretchDistanceText.setText("StretchDistance");
+        SeekBar stretchDistanceSeekbar = new SeekBar(this);
+        stretchDistanceSeekbar.setMin(0);
+        stretchDistanceSeekbar.setProgress((int) (mStretchAffectedDistance * 100));
+        stretchDistanceSeekbar.setMax(100);
+        stretchDistanceSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                mStretchAffectedDistance = progress / 100f;
+                stretchDistanceText.setText("StretchDistance: " + mStretchAffectedDistance);
+                updateShader();
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+
+            }
+        });
+
+
+        linearLayout.addView(mImageView,
+                new LinearLayout.LayoutParams(
+                        mBitmap.getWidth(),
+                        mBitmap.getHeight())
+        );
+
+        linearLayout.addView(overscrollXText,
+                new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.WRAP_CONTENT,
+                        LinearLayout.LayoutParams.WRAP_CONTENT
+                ));
+        linearLayout.addView(overscrollXBar,
+                new LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT
+                )
+        );
+
+        linearLayout.addView(overscrollYText,
+                new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.WRAP_CONTENT,
+                        LinearLayout.LayoutParams.WRAP_CONTENT
+                ));
+        linearLayout.addView(overscrollYBar,
+                new LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT
+                )
+        );
+
+        linearLayout.addView(scrollXText,
+                new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.WRAP_CONTENT,
+                        LinearLayout.LayoutParams.WRAP_CONTENT
+                ));
+
+        linearLayout.addView(scrollXSeekBar,
+                new LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT
+                ));
+
+        linearLayout.addView(scrollYText,
+                new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.WRAP_CONTENT,
+                        LinearLayout.LayoutParams.WRAP_CONTENT
+                ));
+
+        linearLayout.addView(scrollYSeekBar,
+                new LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT
+                ));
+
+        linearLayout.addView(stretchIntensityText,
+                new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.WRAP_CONTENT,
+                        LinearLayout.LayoutParams.WRAP_CONTENT
+                )
+        );
+
+        linearLayout.addView(stretchIntensitySeekbar,
+                new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.MATCH_PARENT,
+                        LinearLayout.LayoutParams.WRAP_CONTENT
+                )
+        );
+
+        linearLayout.addView(stretchDistanceText,
+                new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.WRAP_CONTENT,
+                        LinearLayout.LayoutParams.WRAP_CONTENT
+                ));
+
+        linearLayout.addView(stretchDistanceSeekbar,
+                new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.MATCH_PARENT,
+                        LinearLayout.LayoutParams.WRAP_CONTENT
+                ));
+
+        ImageView test = new ImageView(this);
+        mStretchDrawable.setBitmap(mBitmap);
+        test.setImageDrawable(mStretchDrawable);
+
+        mTestImageView = test;
+        linearLayout.addView(test,
+                new LinearLayout.LayoutParams(mBitmap.getWidth(), mBitmap.getHeight()));
+
+        setContentView(linearLayout);
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mImageView.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        updateShader();
+                        mImageView.getViewTreeObserver().removeOnPreDrawListener(this);
+                        return false;
+                    }
+                });
+    }
+
+    private void updateShader() {
+        final float width = mImageView.getWidth();
+        final float height = mImageView.getHeight();
+        final float distanceNotStretched = mStretchAffectedDistance;
+        final float normOverScrollDistX = mOverscrollX / width;
+        final float normOverScrollDistY = mOverscrollY / height;
+        final float distanceStretchedX =
+                mStretchAffectedDistance
+                        / (1 + Math.abs(normOverScrollDistX) * mMaxStretchIntensity);
+        final float distanceStretchedY =
+                mStretchAffectedDistance
+                        / (1 + Math.abs(normOverScrollDistY) * mMaxStretchIntensity);
+        final float diffX = distanceStretchedX - distanceNotStretched;
+        final float diffY = distanceStretchedY - distanceNotStretched;
+        float uScrollX = mScrollX;
+        float uScrollY = mScrollY;
+
+        mRuntimeShader.setUniform("uMaxStretchIntensity", mMaxStretchIntensity);
+        mRuntimeShader.setUniform("uStretchAffectedDist", mStretchAffectedDistance);
+        mRuntimeShader.setUniform("uDistanceStretchedX", distanceStretchedX);
+        mRuntimeShader.setUniform("uDistanceStretchedY", distanceStretchedY);
+        mRuntimeShader.setUniform("uDistDiffX", diffX);
+        mRuntimeShader.setUniform("uDistDiffY", diffY);
+        mRuntimeShader.setUniform("uOverscrollX", normOverScrollDistX);
+        mRuntimeShader.setUniform("uOverscrollY", normOverScrollDistY);
+        mRuntimeShader.setUniform("uScrollX", uScrollX);
+        mRuntimeShader.setUniform("uScrollY", uScrollY);
+        mRuntimeShader.setUniform("viewportWidth", width);
+        mRuntimeShader.setUniform("viewportHeight", height);
+
+        mImageView.setRenderEffect(RenderEffect.createShaderEffect(mRuntimeShader));
+
+        mStretchDrawable.setStretchDistance(mStretchAffectedDistance);
+        mStretchDrawable.setOverscrollX(normOverScrollDistX);
+        mStretchDrawable.setOverscrollY(normOverScrollDistY);
+    }
+
+    private static class StretchDrawable extends Drawable {
+
+        private float mStretchDistance = 0;
+        private float mOverScrollX = 0f;
+        private float mOverScrollY = 0f;
+        private Bitmap mBitmap = null;
+
+        public void setStretchDistance(float stretchDistance) {
+            mStretchDistance = stretchDistance;
+            invalidateSelf();
+        }
+
+        public void setOverscrollX(float overscrollX) {
+            mOverScrollX = overscrollX;
+            invalidateSelf();
+        }
+
+        public void setOverscrollY(float overscrollY) {
+            mOverScrollY = overscrollY;
+            invalidateSelf();
+        }
+
+        public void setBitmap(Bitmap bitmap) {
+            mBitmap = bitmap;
+            invalidateSelf();
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            if (mStretchDistance > 0 && canvas instanceof RecordingCanvas) {
+                Rect bounds = getBounds();
+                ((RecordingCanvas) canvas).mNode.stretch(
+                        0,
+                        0,
+                        bounds.width(),
+                        bounds.height(),
+                        mOverScrollX,
+                        mOverScrollY,
+                        mStretchDistance
+                );
+            }
+            if (mBitmap != null) {
+                canvas.drawBitmap(mBitmap, 0f, 0f, null);
+            }
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) {
+
+        }
+
+        @Override
+        public int getOpacity() {
+            return 0;
+        }
+    }
+
+    private static final String SKSL = "in shader uContentTexture;\n"
+            + "uniform float uMaxStretchIntensity; // multiplier to apply to scale effect\n"
+            + "uniform float uStretchAffectedDist; // Maximum percentage to stretch beyond bounds"
+            + " of target\n"
+            + "\n"
+            + "// Distance stretched as a function of the normalized overscroll times scale "
+            + "intensity\n"
+            + "uniform float uDistanceStretchedX;\n"
+            + "uniform float uDistanceStretchedY;\n"
+            + "uniform float uDistDiffX;\n"
+            + "uniform float uDistDiffY; // Difference between the peak stretch amount and "
+            + "overscroll amount normalized\n"
+            + "uniform float uScrollX; // Horizontal offset represented as a ratio of pixels "
+            + "divided by the target width\n"
+            + "uniform float uScrollY; // Vertical offset represented as a ratio of pixels "
+            + "divided by the target height\n"
+            + "uniform float uOverscrollX; // Normalized overscroll amount in the horizontal "
+            + "direction\n"
+            + "uniform float uOverscrollY; // Normalized overscroll amount in the vertical "
+            + "direction\n"
+            + "\n"
+            + "uniform float viewportWidth; // target height in pixels\n"
+            + "uniform float viewportHeight; // target width in pixels\n"
+            + "\n"
+            + "vec4 main(vec2 coord) {\n"
+            + "\n"
+            + "    // Normalize SKSL pixel coordinate into a unit vector\n"
+            + "    vec2 uv = vec2(coord.x / viewportWidth, coord.y / viewportHeight);\n"
+            + "    float inU = uv.x;\n"
+            + "    float inV = uv.y;\n"
+            + "    float outU;\n"
+            + "    float outV;\n"
+            + "    float stretchIntensity;\n"
+            + "\n"
+            + "    // Add the normalized scroll position within scrolling list\n"
+            + "    inU += uScrollX;\n"
+            + "    inV += uScrollY;\n"
+            + "\n"
+            + "    outU = inU;\n"
+            + "    outV = inV;\n"
+            + "    if (uOverscrollX > 0) {\n"
+            + "        if (inU <= uStretchAffectedDist) {\n"
+            + "            inU = uStretchAffectedDist - inU;\n"
+            + "            float posBasedVariation = smoothstep(0., uStretchAffectedDist, inU);\n"
+            + "            stretchIntensity = uMaxStretchIntensity * uOverscrollX * "
+            + "posBasedVariation;\n"
+            + "            outU = uDistanceStretchedX - (inU / (1. + stretchIntensity));\n"
+            + "        } else {\n"
+            + "            outU = uDistDiffX + inU;\n"
+            + "        }\n"
+            + "    }\n"
+            + "\n"
+            + "     if (uOverscrollX < 0) {\n"
+            + "            float stretchAffectedDist = 1. - uStretchAffectedDist;\n"
+            + "            if (inU >= stretchAffectedDist) {\n"
+            + "                inU = inU - stretchAffectedDist;\n"
+            + "                float posBasedVariation = (smoothstep(0., uStretchAffectedDist, "
+            + "inU));\n"
+            + "                stretchIntensity = uMaxStretchIntensity * (-uOverscrollX) * "
+            + "posBasedVariation;\n"
+            + "                outU = 1 - (uDistanceStretchedX - (inU / (1. + stretchIntensity)))"
+            + ";\n"
+            + "            } else if (inU < stretchAffectedDist) {\n"
+            + "                outU = -uDistDiffX + inU;\n"
+            + "            }\n"
+            + "        }\n"
+            + "\n"
+            + "    if (uOverscrollY > 0) {\n"
+            + "        if (inV <= uStretchAffectedDist) {\n"
+            + "            inV = uStretchAffectedDist - inV;\n"
+            + "            float posBasedVariation = smoothstep(0., uStretchAffectedDist, inV);\n"
+            + "            stretchIntensity = uMaxStretchIntensity * uOverscrollY * "
+            + "posBasedVariation;\n"
+            + "            outV = uDistanceStretchedY - (inV / (1. + stretchIntensity));\n"
+            + "        } else if (inV >= uStretchAffectedDist) {\n"
+            + "            outV = uDistDiffY + inV;\n"
+            + "        }\n"
+            + "    }\n"
+            + "\n"
+            + "    if (uOverscrollY < 0) {\n"
+            + "        float stretchAffectedDist = 1. - uStretchAffectedDist;\n"
+            + "        if (inV >= stretchAffectedDist) {\n"
+            + "            inV = inV - stretchAffectedDist;\n"
+            + "            float posBasedVariation = (smoothstep(0., uStretchAffectedDist, inV));\n"
+            + "            stretchIntensity = uMaxStretchIntensity * (-uOverscrollY) * "
+            + "posBasedVariation;\n"
+            + "            outV = 1 - (uDistanceStretchedY - (inV / (1. + stretchIntensity)));\n"
+            + "        } else if (inV < stretchAffectedDist) {\n"
+            + "            outV = -uDistDiffY + inV;\n"
+            + "        }\n"
+            + "    }\n"
+            + "\n"
+            + "    uv.x = outU;\n"
+            + "    uv.y = outV;\n"
+            + "    coord.x = uv.x * viewportWidth;\n"
+            + "    coord.y = uv.y * viewportHeight;\n"
+            + "    return sample(uContentTexture, coord);\n"
+            + "}";
+}
diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
new file mode 100644
index 0000000..2e985fb
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
@@ -0,0 +1,130 @@
+/*
+ * 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.test.input
+
+import android.view.InputDevice.SOURCE_MOUSE
+import android.view.InputDevice.SOURCE_TOUCHSCREEN
+import android.view.InputEventAssigner
+import android.view.KeyEvent
+import android.view.MotionEvent
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+/**
+ * Create a MotionEvent with the provided action, eventTime, and source
+ */
+fun createMotionEvent(action: Int, eventTime: Long, source: Int): MotionEvent {
+    val downTime: Long = 10
+    val x = 1f
+    val y = 2f
+    val pressure = 3f
+    val size = 1f
+    val metaState = 0
+    val xPrecision = 0f
+    val yPrecision = 0f
+    val deviceId = 1
+    val edgeFlags = 0
+    val displayId = 0
+    return MotionEvent.obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
+            xPrecision, yPrecision, deviceId, edgeFlags, source, displayId)
+}
+
+fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
+    val code = KeyEvent.KEYCODE_A
+    val repeat = 0
+    return KeyEvent(eventTime, eventTime, action, code, repeat)
+}
+
+class InputEventAssignerTest {
+    companion object {
+        private const val TAG = "InputEventAssignerTest"
+    }
+
+    /**
+     * A single MOVE event should be assigned to the next available frame.
+     */
+    @Test
+    fun testTouchGesture() {
+        val assigner = InputEventAssigner()
+        val event = createMotionEvent(MotionEvent.ACTION_MOVE, 10, SOURCE_TOUCHSCREEN)
+        val eventId = assigner.processEvent(event)
+        assertEquals(event.id, eventId)
+    }
+
+    /**
+     * DOWN event should be used until a vsync comes in. After vsync, the latest event should be
+     * produced.
+     */
+    @Test
+    fun testTouchDownWithMove() {
+        val assigner = InputEventAssigner()
+        val down = createMotionEvent(MotionEvent.ACTION_DOWN, 10, SOURCE_TOUCHSCREEN)
+        val move1 = createMotionEvent(MotionEvent.ACTION_MOVE, 12, SOURCE_TOUCHSCREEN)
+        val move2 = createMotionEvent(MotionEvent.ACTION_MOVE, 13, SOURCE_TOUCHSCREEN)
+        val move3 = createMotionEvent(MotionEvent.ACTION_MOVE, 14, SOURCE_TOUCHSCREEN)
+        val move4 = createMotionEvent(MotionEvent.ACTION_MOVE, 15, SOURCE_TOUCHSCREEN)
+        var eventId = assigner.processEvent(down)
+        assertEquals(down.id, eventId)
+        eventId = assigner.processEvent(move1)
+        assertEquals(down.id, eventId)
+        eventId = assigner.processEvent(move2)
+        // Even though we already had 2 move events, there was no choreographer callback yet.
+        // Therefore, we should still get the id of the down event
+        assertEquals(down.id, eventId)
+
+        // Now send CALLBACK_INPUT to the assigner. It should provide the latest motion event
+        assigner.onChoreographerCallback()
+        eventId = assigner.processEvent(move3)
+        assertEquals(move3.id, eventId)
+        eventId = assigner.processEvent(move4)
+        assertEquals(move4.id, eventId)
+    }
+
+    /**
+     * Similar to the above test, but with SOURCE_MOUSE. Since we don't have down latency
+     * concept for non-touchscreens, the latest input event will be used.
+     */
+    @Test
+    fun testMouseDownWithMove() {
+        val assigner = InputEventAssigner()
+        val down = createMotionEvent(MotionEvent.ACTION_DOWN, 10, SOURCE_MOUSE)
+        val move1 = createMotionEvent(MotionEvent.ACTION_MOVE, 12, SOURCE_MOUSE)
+        var eventId = assigner.processEvent(down)
+        assertEquals(down.id, eventId)
+        eventId = assigner.processEvent(move1)
+        assertEquals(move1.id, eventId)
+    }
+
+    /**
+     * KeyEvents are processed immediately, so the latest event should be returned.
+     */
+    @Test
+    fun testKeyEvent() {
+        val assigner = InputEventAssigner()
+        val down = createKeyEvent(KeyEvent.ACTION_DOWN, 20)
+        var eventId = assigner.processEvent(down)
+        assertEquals(down.id, eventId)
+        val up = createKeyEvent(KeyEvent.ACTION_UP, 21)
+        eventId = assigner.processEvent(up)
+        // DOWN is only sticky for Motions, not for keys
+        assertEquals(up.id, eventId)
+        assigner.onChoreographerCallback()
+        val down2 = createKeyEvent(KeyEvent.ACTION_DOWN, 22)
+        eventId = assigner.processEvent(down2)
+        assertEquals(down2.id, eventId)
+    }
+}
diff --git a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
index c19e5cc..c01d32b 100644
--- a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
+++ b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
@@ -17,6 +17,7 @@
 package com.android.test.input
 
 import android.graphics.FrameInfo
+import android.os.IInputConstants.INVALID_INPUT_EVENT_ID
 import android.os.SystemClock
 import android.view.ViewFrameInfo
 import com.google.common.truth.Truth.assertThat
@@ -33,8 +34,7 @@
     @Before
     fun setUp() {
         mViewFrameInfo.reset()
-        mViewFrameInfo.updateOldestInputEvent(10)
-        mViewFrameInfo.updateNewestInputEvent(20)
+        mViewFrameInfo.setInputEvent(139)
         mViewFrameInfo.flags = mViewFrameInfo.flags or FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED
         mTimeStarted = SystemClock.uptimeNanos()
         mViewFrameInfo.markDrawStart()
@@ -43,8 +43,6 @@
     @Test
     fun testPopulateFields() {
         assertThat(mViewFrameInfo.drawStart).isGreaterThan(mTimeStarted)
-        assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(10)
-        assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(20)
         assertThat(mViewFrameInfo.flags).isEqualTo(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED)
     }
 
@@ -53,8 +51,6 @@
         mViewFrameInfo.reset()
         // Ensure that the original object is reset correctly
         assertThat(mViewFrameInfo.drawStart).isEqualTo(0)
-        assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(0)
-        assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(0)
         assertThat(mViewFrameInfo.flags).isEqualTo(0)
     }
 
@@ -62,12 +58,13 @@
     fun testUpdateFrameInfoFromViewFrameInfo() {
         val frameInfo = FrameInfo()
         // By default, all values should be zero
-        // TODO(b/169866723): Use InputEventAssigner and assert INPUT_EVENT_ID
+        assertThat(frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID]).isEqualTo(INVALID_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.INPUT_EVENT_ID]).isEqualTo(139)
         assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(
                 FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED)
         assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isGreaterThan(mTimeStarted)
diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/tests/net/common/java/android/net/CaptivePortalTest.java
index 7a60cc1..15d3398 100644
--- a/tests/net/common/java/android/net/CaptivePortalTest.java
+++ b/tests/net/common/java/android/net/CaptivePortalTest.java
@@ -24,8 +24,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 
 import org.junit.Rule;
@@ -55,7 +55,7 @@
             mCode = request;
         }
 
-        @Override
+        // This is only @Override on R-
         public void logEvent(int eventId, String packageName) throws RemoteException {
             mCode = eventId;
             mPackageName = packageName;
@@ -98,12 +98,24 @@
         assertEquals(result.mCode, CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED);
     }
 
+    @IgnoreUpTo(Build.VERSION_CODES.R)
     @Test
     public void testLogEvent() {
+        /**
+        * From S testLogEvent is expected to do nothing but shouldn't crash (the API
+        * logEvent has been deprecated).
+        */
         final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent(
-                MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY,
+                0,
                 TEST_PACKAGE_NAME));
-        assertEquals(result.mCode, MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY);
+    }
+
+    @IgnoreAfter(Build.VERSION_CODES.R)
+    @Test
+    public void testLogEvent_UntilR() {
+        final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent(
+                42, TEST_PACKAGE_NAME));
+        assertEquals(result.mCode, 42);
         assertEquals(result.mPackageName, TEST_PACKAGE_NAME);
     }
 }
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index c10c573..2a2dc56 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.net.integrationtests
 
+import android.app.usage.NetworkStatsManager
 import android.content.ComponentName
 import android.content.Context
 import android.content.Context.BIND_AUTO_CREATE
@@ -25,7 +26,6 @@
 import android.net.ConnectivityManager
 import android.net.IDnsResolver
 import android.net.INetd
-import android.net.INetworkStatsService
 import android.net.LinkProperties
 import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
 import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
@@ -37,7 +37,6 @@
 import android.net.metrics.IpConnectivityLog
 import android.os.ConditionVariable
 import android.os.IBinder
-import android.os.INetworkManagementService
 import android.os.SystemConfigManager
 import android.os.UserHandle
 import android.testing.TestableContext
@@ -87,9 +86,7 @@
     // lateinit used here for mocks as they need to be reinitialized between each test and the test
     // should crash if they are used before being initialized.
     @Mock
-    private lateinit var netManager: INetworkManagementService
-    @Mock
-    private lateinit var statsService: INetworkStatsService
+    private lateinit var statsManager: NetworkStatsManager
     @Mock
     private lateinit var log: IpConnectivityLog
     @Mock
@@ -172,12 +169,13 @@
         service = TestConnectivityService(makeDependencies())
         cm = ConnectivityManager(context, service)
         context.addMockSystemService(Context.CONNECTIVITY_SERVICE, cm)
+        context.addMockSystemService(Context.NETWORK_STATS_SERVICE, statsManager)
 
         service.systemReadyInternal()
     }
 
     private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService(
-            context, statsService, dnsResolver, log, netd, deps)
+            context, dnsResolver, log, netd, deps)
 
     private fun makeDependencies(): ConnectivityService.Dependencies {
         val deps = spy(ConnectivityService.Dependencies())
diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
index 9b0cfa9..c1315f6 100644
--- a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
+++ b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
@@ -21,7 +21,7 @@
 import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER
 import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE
 import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY
-import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdChangedListener
+import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener
 import android.provider.Settings
 import android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI
 import android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE
@@ -120,9 +120,9 @@
                 MULTIPATH_PREFERENCE_PERFORMANCE.toString())
 
         val listenerCaptor = ArgumentCaptor.forClass(
-                ActiveDataSubscriptionIdChangedListener::class.java)
+                ActiveDataSubscriptionIdListener::class.java)
         verify(telephonyManager, times(1))
-                .registerPhoneStateListener(any(), listenerCaptor.capture())
+                .registerTelephonyCallback(any(), listenerCaptor.capture())
         val listener = listenerCaptor.value
         listener.onActiveDataSubscriptionIdChanged(testSubId)
 
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c6e2bc7..d725171 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -103,6 +103,7 @@
 import static com.android.testutils.ConcurrentUtils.durationOf;
 import static com.android.testutils.ExceptionUtils.ignoreExceptions;
 import static com.android.testutils.HandlerUtils.waitForIdleSerialExecutor;
+import static com.android.testutils.MiscAsserts.assertContainsAll;
 import static com.android.testutils.MiscAsserts.assertContainsExactly;
 import static com.android.testutils.MiscAsserts.assertEmpty;
 import static com.android.testutils.MiscAsserts.assertLength;
@@ -149,6 +150,7 @@
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.usage.NetworkStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -180,7 +182,6 @@
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkPolicyListener;
-import android.net.INetworkStatsService;
 import android.net.IOnSetOemNetworkPreferenceListener;
 import android.net.IQosCallback;
 import android.net.InetAddresses;
@@ -416,6 +417,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;
@@ -423,7 +426,7 @@
 
     @Mock DeviceIdleInternal mDeviceIdleInternal;
     @Mock INetworkManagementService mNetworkManagementService;
-    @Mock INetworkStatsService mStatsService;
+    @Mock NetworkStatsManager mStatsManager;
     @Mock IBatteryStats mBatteryStatsService;
     @Mock IDnsResolver mMockDnsResolver;
     @Mock INetd mMockNetd;
@@ -539,6 +542,7 @@
             if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager;
             if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager;
             if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager;
+            if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
             return super.getSystemService(name);
         }
 
@@ -1081,9 +1085,11 @@
         }
     }
 
-    private Set<UidRange> uidRangesForUid(int uid) {
+    private Set<UidRange> uidRangesForUids(int... uids) {
         final ArraySet<UidRange> ranges = new ArraySet<>();
-        ranges.add(new UidRange(uid, uid));
+        for (final int uid : uids) {
+            ranges.add(new UidRange(uid, uid));
+        }
         return ranges;
     }
 
@@ -1213,13 +1219,13 @@
 
         public void establishForMyUid(LinkProperties lp) throws Exception {
             final int uid = Process.myUid();
-            establish(lp, uid, uidRangesForUid(uid), true, true, false);
+            establish(lp, uid, uidRangesForUids(uid), true, true, false);
         }
 
         public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode)
                 throws Exception {
             final int uid = Process.myUid();
-            establish(makeLinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet,
+            establish(makeLinkProperties(), uid, uidRangesForUids(uid), validated, hasInternet,
                     isStrictMode);
         }
 
@@ -1328,7 +1334,7 @@
 
     }
 
-    private void processBroadcastForVpn(Intent intent) {
+    private void processBroadcast(Intent intent) {
         mServiceContext.sendBroadcast(intent);
         HandlerUtils.waitForIdle(mVMSHandlerThread, TIMEOUT_MS);
         waitForIdle();
@@ -1419,6 +1425,7 @@
     private static final int VPN_UID = UserHandle.getUid(PRIMARY_USER, 10043);
     private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "",
             UserInfo.FLAG_PRIMARY);
+    private static final UserHandle PRIMARY_USER_HANDLE = new UserHandle(PRIMARY_USER);
 
     private static final int RESTRICTED_USER = 1;
     private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "",
@@ -1436,6 +1443,8 @@
         MockitoAnnotations.initMocks(this);
 
         when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO));
+        when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+                Arrays.asList(PRIMARY_USER_HANDLE));
         when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO);
         // canHaveRestrictedProfile does not take a userId. It applies to the userId of the context
         // it was started from, i.e., PRIMARY_USER.
@@ -1472,7 +1481,6 @@
         mDeps = makeDependencies();
         returnRealCallingUid();
         mService = new ConnectivityService(mServiceContext,
-                mStatsService,
                 mMockDnsResolver,
                 mock(IpConnectivityLog.class),
                 mMockNetd,
@@ -1556,6 +1564,7 @@
 
     @After
     public void tearDown() throws Exception {
+        unregisterDefaultNetworkCallbacks();
         setAlwaysOnNetworks(false);
         if (mCellNetworkAgent != null) {
             mCellNetworkAgent.disconnect();
@@ -1661,6 +1670,7 @@
         assertNull(mCm.getActiveNetworkForUid(Process.myUid()));
         // Test getAllNetworks()
         assertEmpty(mCm.getAllNetworks());
+        assertEmpty(mCm.getAllNetworkStateSnapshot());
     }
 
     /**
@@ -5487,18 +5497,19 @@
         assertEquals(expectedSet, actualSet);
     }
 
-    private void expectForceUpdateIfaces(Network[] networks, String defaultIface,
+    private void expectNetworkStatus(Network[] networks, String defaultIface,
             Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception {
-        ArgumentCaptor<Network[]> networksCaptor = ArgumentCaptor.forClass(Network[].class);
-        ArgumentCaptor<UnderlyingNetworkInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(
-                UnderlyingNetworkInfo[].class);
+        ArgumentCaptor<List<Network>> networksCaptor = ArgumentCaptor.forClass(List.class);
+        ArgumentCaptor<List<UnderlyingNetworkInfo>> vpnInfosCaptor =
+                ArgumentCaptor.forClass(List.class);
 
-        verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(),
-                any(NetworkStateSnapshot[].class), eq(defaultIface), vpnInfosCaptor.capture());
+        verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(networksCaptor.capture(),
+                any(List.class), eq(defaultIface), vpnInfosCaptor.capture());
 
-        assertSameElementsNoDuplicates(networksCaptor.getValue(), networks);
+        assertSameElementsNoDuplicates(networksCaptor.getValue().toArray(), networks);
 
-        UnderlyingNetworkInfo[] infos = vpnInfosCaptor.getValue();
+        UnderlyingNetworkInfo[] infos =
+                vpnInfosCaptor.getValue().toArray(new UnderlyingNetworkInfo[0]);
         if (vpnUid != null) {
             assertEquals("Should have exactly one VPN:", 1, infos.length);
             UnderlyingNetworkInfo info = infos[0];
@@ -5512,8 +5523,9 @@
         }
     }
 
-    private void expectForceUpdateIfaces(Network[] networks, String defaultIface) throws Exception {
-        expectForceUpdateIfaces(networks, defaultIface, null, null, new String[0]);
+    private void expectNetworkStatus(
+            Network[] networks, String defaultIface) throws Exception {
+        expectNetworkStatus(networks, defaultIface, null, null, new String[0]);
     }
 
     @Test
@@ -5533,46 +5545,46 @@
         mCellNetworkAgent.connect(false);
         mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
-        expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
-        reset(mStatsService);
+        expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+        reset(mStatsManager);
 
         // Default network switch should update ifaces.
         mWiFiNetworkAgent.connect(false);
         mWiFiNetworkAgent.sendLinkProperties(wifiLp);
         waitForIdle();
         assertEquals(wifiLp, mService.getActiveLinkProperties());
-        expectForceUpdateIfaces(onlyWifi, WIFI_IFNAME);
-        reset(mStatsService);
+        expectNetworkStatus(onlyWifi, WIFI_IFNAME);
+        reset(mStatsManager);
 
         // Disconnect should update ifaces.
         mWiFiNetworkAgent.disconnect();
         waitForIdle();
-        expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
-        reset(mStatsService);
+        expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+        reset(mStatsManager);
 
         // Metered change should update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         waitForIdle();
-        expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
-        reset(mStatsService);
+        expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+        reset(mStatsManager);
 
         mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         waitForIdle();
-        expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
-        reset(mStatsService);
+        expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+        reset(mStatsManager);
 
         // Temp metered change shouldn't update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
         waitForIdle();
-        verify(mStatsService, never()).forceUpdateIfaces(eq(onlyCell), any(
-                NetworkStateSnapshot[].class), eq(MOBILE_IFNAME), eq(new UnderlyingNetworkInfo[0]));
-        reset(mStatsService);
+        verify(mStatsManager, never()).notifyNetworkStatus(eq(Arrays.asList(onlyCell)),
+                any(List.class), eq(MOBILE_IFNAME), any(List.class));
+        reset(mStatsManager);
 
         // Roaming change should update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
         waitForIdle();
-        expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
-        reset(mStatsService);
+        expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+        reset(mStatsManager);
 
         // Test VPNs.
         final LinkProperties lp = new LinkProperties();
@@ -5585,7 +5597,7 @@
                 mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
 
         // A VPN with default (null) underlying networks sets the underlying network's interfaces...
-        expectForceUpdateIfaces(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+        expectNetworkStatus(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{MOBILE_IFNAME});
 
         // ...and updates them as the default network switches.
@@ -5602,9 +5614,9 @@
 
         waitForIdle();
         assertEquals(wifiLp, mService.getActiveLinkProperties());
-        expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+        expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{WIFI_IFNAME});
-        reset(mStatsService);
+        reset(mStatsManager);
 
         // A VPN that sets its underlying networks passes the underlying interfaces, and influences
         // the default interface sent to NetworkStatsService by virtue of applying to the system
@@ -5614,22 +5626,22 @@
         // applies to the system server UID should not have any bearing on network stats.
         mMockVpn.setUnderlyingNetworks(onlyCell);
         waitForIdle();
-        expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+        expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{MOBILE_IFNAME});
-        reset(mStatsService);
+        reset(mStatsManager);
 
         mMockVpn.setUnderlyingNetworks(cellAndWifi);
         waitForIdle();
-        expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+        expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{MOBILE_IFNAME, WIFI_IFNAME});
-        reset(mStatsService);
+        reset(mStatsManager);
 
         // Null underlying networks are ignored.
         mMockVpn.setUnderlyingNetworks(cellNullAndWifi);
         waitForIdle();
-        expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+        expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{MOBILE_IFNAME, WIFI_IFNAME});
-        reset(mStatsService);
+        reset(mStatsManager);
 
         // If an underlying network disconnects, that interface should no longer be underlying.
         // This doesn't actually work because disconnectAndDestroyNetwork only notifies
@@ -5641,17 +5653,17 @@
         mCellNetworkAgent.disconnect();
         waitForIdle();
         assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork()));
-        expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+        expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{MOBILE_IFNAME, WIFI_IFNAME});
 
         // Confirm that we never tell NetworkStatsService that cell is no longer the underlying
         // network for the VPN...
-        verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class),
-                any(NetworkStateSnapshot[].class), any() /* anyString() doesn't match null */,
-                argThat(infos -> infos[0].underlyingIfaces.size() == 1
-                        && WIFI_IFNAME.equals(infos[0].underlyingIfaces.get(0))));
-        verifyNoMoreInteractions(mStatsService);
-        reset(mStatsService);
+        verify(mStatsManager, never()).notifyNetworkStatus(any(List.class),
+                any(List.class), any() /* anyString() doesn't match null */,
+                argThat(infos -> infos.get(0).underlyingIfaces.size() == 1
+                        && WIFI_IFNAME.equals(infos.get(0).underlyingIfaces.get(0))));
+        verifyNoMoreInteractions(mStatsManager);
+        reset(mStatsManager);
 
         // ... but if something else happens that causes notifyIfacesChangedForNetworkStats to be
         // called again, it does. For example, connect Ethernet, but with a low score, such that it
@@ -5660,13 +5672,13 @@
         mEthernetNetworkAgent.adjustScore(-40);
         mEthernetNetworkAgent.connect(false);
         waitForIdle();
-        verify(mStatsService).forceUpdateIfaces(any(Network[].class),
-                any(NetworkStateSnapshot[].class), any() /* anyString() doesn't match null */,
-                argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.size() == 1
-                        && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces.get(0))));
+        verify(mStatsManager).notifyNetworkStatus(any(List.class),
+                any(List.class), any() /* anyString() doesn't match null */,
+                argThat(vpnInfos -> vpnInfos.get(0).underlyingIfaces.size() == 1
+                        && WIFI_IFNAME.equals(vpnInfos.get(0).underlyingIfaces.get(0))));
         mEthernetNetworkAgent.disconnect();
         waitForIdle();
-        reset(mStatsService);
+        reset(mStatsManager);
 
         // When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo
         // does not return the VPN, so CS does not pass it to NetworkStatsService. This causes
@@ -5676,27 +5688,27 @@
         // Also, for the same reason as above, the active interface passed in is null.
         mMockVpn.setUnderlyingNetworks(new Network[0]);
         waitForIdle();
-        expectForceUpdateIfaces(wifiAndVpn, null);
-        reset(mStatsService);
+        expectNetworkStatus(wifiAndVpn, null);
+        reset(mStatsManager);
 
         // Specifying only a null underlying network is the same as no networks.
         mMockVpn.setUnderlyingNetworks(onlyNull);
         waitForIdle();
-        expectForceUpdateIfaces(wifiAndVpn, null);
-        reset(mStatsService);
+        expectNetworkStatus(wifiAndVpn, null);
+        reset(mStatsManager);
 
         // Specifying networks that are all disconnected is the same as specifying no networks.
         mMockVpn.setUnderlyingNetworks(onlyCell);
         waitForIdle();
-        expectForceUpdateIfaces(wifiAndVpn, null);
-        reset(mStatsService);
+        expectNetworkStatus(wifiAndVpn, null);
+        reset(mStatsManager);
 
         // Passing in null again means follow the default network again.
         mMockVpn.setUnderlyingNetworks(null);
         waitForIdle();
-        expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+        expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
                 new String[]{WIFI_IFNAME});
-        reset(mStatsService);
+        reset(mStatsManager);
     }
 
     @Test
@@ -6383,7 +6395,7 @@
         vpnNetworkCallback.assertNoCallback();
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        final Set<UidRange> ranges = uidRangesForUid(uid);
+        final Set<UidRange> ranges = uidRangesForUids(uid);
         mMockVpn.registerAgent(ranges);
         mMockVpn.setUnderlyingNetworks(new Network[0]);
 
@@ -6855,7 +6867,7 @@
         final int uid = Process.myUid();
         NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         assertNotNull("nc=" + nc, nc.getUids());
-        assertEquals(nc.getUids(), uidRangesForUid(uid));
+        assertEquals(nc.getUids(), uidRangesForUids(uid));
         assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE);
 
         // Set an underlying network and expect to see the VPN transports change.
@@ -6876,7 +6888,7 @@
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
 
         // Send a USER_ADDED broadcast for it.
-        processBroadcastForVpn(addedIntent);
+        processBroadcast(addedIntent);
 
         // Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added
         // restricted user.
@@ -6901,7 +6913,7 @@
         final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
         removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
         removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
-        processBroadcastForVpn(removedIntent);
+        processBroadcast(removedIntent);
 
         // Expect that the VPN gains the UID range for the restricted user, and that the capability
         // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
@@ -6959,7 +6971,7 @@
         final Intent addedIntent = new Intent(ACTION_USER_ADDED);
         addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
-        processBroadcastForVpn(addedIntent);
+        processBroadcast(addedIntent);
         assertNull(mCm.getActiveNetworkForUid(uid));
         assertNull(mCm.getActiveNetworkForUid(restrictedUid));
 
@@ -6970,7 +6982,7 @@
         final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
         removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
         removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
-        processBroadcastForVpn(removedIntent);
+        processBroadcast(removedIntent);
         assertNull(mCm.getActiveNetworkForUid(uid));
         assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
 
@@ -7125,7 +7137,7 @@
         assertFalse(mCm.isActiveNetworkMetered());
 
         // Connect VPN network.
-        mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUid(Process.myUid()),
+        mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUids(Process.myUid()),
                 new LinkProperties());
         mMockVpn.connect(true);
         waitForIdle();
@@ -7564,7 +7576,7 @@
         final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
         addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-        processBroadcastForVpn(addedIntent);
+        processBroadcast(addedIntent);
 
         // Lockdown VPN disables teardown and enables lockdown.
         assertFalse(mMockVpn.getEnableTeardown());
@@ -9283,7 +9295,7 @@
 
     private void assertUidRangesUpdatedForMyUid(boolean add) throws Exception {
         final int uid = Process.myUid();
-        assertVpnUidRangesUpdated(add, uidRangesForUid(uid), uid);
+        assertVpnUidRangesUpdated(add, uidRangesForUids(uid), uid);
     }
 
     private void assertVpnUidRangesUpdated(boolean add, Set<UidRange> vpnRanges, int exemptUid)
@@ -9494,6 +9506,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();
@@ -9668,7 +9684,7 @@
     }
 
     @Test
-    public void testOemNetworkRequestFactoryCorrectlySetsUids()
+    public void testOemNetworkRequestFactoryMultiplePrefsCorrectlySetsUids()
             throws Exception {
         // Arrange PackageManager mocks
         final String testPackageName2 = "com.google.apps.dialer";
@@ -9699,6 +9715,46 @@
     }
 
     @Test
+    public void testOemNetworkRequestFactoryMultipleUsersCorrectlySetsUids()
+            throws Exception {
+        // Arrange users
+        final int secondUser = 10;
+        final UserHandle secondUserHandle = new UserHandle(secondUser);
+        when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+                Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle));
+
+        // Arrange PackageManager mocks
+        mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID);
+
+        // Build OemNetworkPreferences object
+        final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID;
+        final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
+                .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref)
+                .build();
+
+        // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
+        final List<ConnectivityService.NetworkRequestInfo> nris =
+                new ArrayList<>(
+                        mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(
+                                pref));
+
+        // UIDs for all users and all managed packages should be present.
+        // Two users each with two packages.
+        final int expectedUidSize = 2;
+        final List<UidRange> uids =
+                new ArrayList<>(nris.get(0).mRequests.get(0).networkCapabilities.getUids());
+        assertEquals(expectedUidSize, uids.size());
+
+        // Sort by uid to access nris by index
+        uids.sort(Comparator.comparingInt(uid -> uid.start));
+        final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
+        assertEquals(TEST_PACKAGE_UID, uids.get(0).start);
+        assertEquals(TEST_PACKAGE_UID, uids.get(0).stop);
+        assertEquals(secondUserTestPackageUid, uids.get(1).start);
+        assertEquals(secondUserTestPackageUid, uids.get(1).stop);
+    }
+
+    @Test
     public void testOemNetworkRequestFactoryAddsPackagesToCorrectPreference()
             throws Exception {
         // Expectations
@@ -9828,6 +9884,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(
@@ -9855,7 +9959,7 @@
         assertEquals(1, mService.mDefaultNetworkRequests.size());
 
         final UidRangeParcel[] uidRanges =
-                toUidRangeStableParcels(uidRangesForUid(testPackageUid));
+                toUidRangeStableParcels(uidRangesForUids(testPackageUid));
         setupSetOemNetworkPreferenceForPreferenceTest(
                 networkPrefToSetup, uidRanges, testPackageName);
     }
@@ -9876,12 +9980,11 @@
                 .build();
 
         // Act on ConnectivityService.setOemNetworkPreference()
-        final TestOemListenerCallback mOnSetOemNetworkPreferenceTestListener =
-                new TestOemListenerCallback();
-        mService.setOemNetworkPreference(pref, mOnSetOemNetworkPreferenceTestListener);
+        final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback();
+        mService.setOemNetworkPreference(pref, oemPrefListener);
 
         // Verify call returned successfully
-        mOnSetOemNetworkPreferenceTestListener.expectOnComplete();
+        oemPrefListener.expectOnComplete();
     }
 
     private static class TestOemListenerCallback implements IOnSetOemNetworkPreferenceListener {
@@ -9911,6 +10014,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);
@@ -9925,6 +10029,7 @@
 
         // Verify that the active network is correct
         verifyActiveNetwork(TRANSPORT_ETHERNET);
+        // default NCs will be unregistered in tearDown
     }
 
     @Test
@@ -9932,6 +10037,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);
@@ -9952,6 +10058,7 @@
                 mEthernetNetworkAgent.getNetwork());
 
         assertFalse(mCm.isActiveNetworkMetered());
+        // default NCs will be unregistered in tearDown
     }
 
     @Test
@@ -10082,6 +10189,10 @@
         mCm.unregisterNetworkCallback(defaultNetworkCallback);
     }
 
+    /**
+     * This method assumes that the same uidRanges input will be used to verify that dependencies
+     * are called as expected.
+     */
     private void verifySetOemNetworkPreferenceForPreference(
             @NonNull final UidRangeParcel[] uidRanges,
             final int addUidRangesNetId,
@@ -10089,16 +10200,30 @@
             final int removeUidRangesNetId,
             final int removeUidRangesTimes,
             final boolean shouldDestroyNetwork) throws RemoteException {
+        verifySetOemNetworkPreferenceForPreference(uidRanges, uidRanges,
+                addUidRangesNetId, addUidRangesTimes, removeUidRangesNetId, removeUidRangesTimes,
+                shouldDestroyNetwork);
+    }
+
+    private void verifySetOemNetworkPreferenceForPreference(
+            @NonNull final UidRangeParcel[] addedUidRanges,
+            @NonNull final UidRangeParcel[] removedUidRanges,
+            final int addUidRangesNetId,
+            final int addUidRangesTimes,
+            final int removeUidRangesNetId,
+            final int removeUidRangesTimes,
+            final boolean shouldDestroyNetwork) throws RemoteException {
         final boolean useAnyIdForAdd = OEM_PREF_ANY_NET_ID == addUidRangesNetId;
         final boolean useAnyIdForRemove = OEM_PREF_ANY_NET_ID == removeUidRangesNetId;
 
         // Validate netd.
         verify(mMockNetd, times(addUidRangesTimes))
                 .networkAddUidRanges(
-                        (useAnyIdForAdd ? anyInt() : eq(addUidRangesNetId)), eq(uidRanges));
+                        (useAnyIdForAdd ? anyInt() : eq(addUidRangesNetId)), eq(addedUidRanges));
         verify(mMockNetd, times(removeUidRangesTimes))
                 .networkRemoveUidRanges(
-                        (useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)), eq(uidRanges));
+                        (useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)),
+                        eq(removedUidRanges));
         if (shouldDestroyNetwork) {
             verify(mMockNetd, times(1))
                     .networkDestroy((useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)));
@@ -10108,7 +10233,6 @@
 
     /**
      * Test the tracked default requests clear previous OEM requests on setOemNetworkPreference().
-     * @throws Exception
      */
     @Test
     public void testSetOemNetworkPreferenceClearPreviousOemValues() throws Exception {
@@ -10117,7 +10241,7 @@
         final int testPackageUid = 123;
         final String testPackageName = "com.google.apps.contacts";
         final UidRangeParcel[] uidRanges =
-                toUidRangeStableParcels(uidRangesForUid(testPackageUid));
+                toUidRangeStableParcels(uidRangesForUids(testPackageUid));
 
         // Validate the starting requests only includes the fallback request.
         assertEquals(1, mService.mDefaultNetworkRequests.size());
@@ -10136,9 +10260,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()
@@ -10147,9 +10270,8 @@
                 OEM_NETWORK_PREFERENCE_OEM_PAID;
 
         // Arrange PackageManager mocks
-        final int testPackageNameUid = 123;
         final UidRangeParcel[] uidRanges =
-                toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+                toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
         setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
 
         // Verify the starting state. No networks should be connected.
@@ -10204,9 +10326,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()
@@ -10215,9 +10336,8 @@
                 OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
 
         // Arrange PackageManager mocks
-        final int testPackageNameUid = 123;
         final UidRangeParcel[] uidRanges =
-                toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+                toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
         setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
 
         // Verify the starting state. This preference doesn't support using the fallback network
@@ -10267,10 +10387,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()
@@ -10279,9 +10398,8 @@
                 OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
 
         // Arrange PackageManager mocks
-        final int testPackageNameUid = 123;
         final UidRangeParcel[] uidRanges =
-                toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+                toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
         setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
 
         // Verify the starting state. This preference doesn't support using the fallback network
@@ -10321,10 +10439,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()
@@ -10333,9 +10450,8 @@
                 OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
 
         // Arrange PackageManager mocks
-        final int testPackageNameUid = 123;
         final UidRangeParcel[] uidRanges =
-                toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+                toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
         setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
 
         // Verify the starting state. This preference doesn't support using the fallback network
@@ -10374,7 +10490,417 @@
                 true /* shouldDestroyNetwork */);
     }
 
-    private UidRange createUidRange(int userId) {
-        return UidRange.createForUser(UserHandle.of(userId));
+    @Test
+    public void testMultilayerForMultipleUsersEvaluatesCorrectly()
+            throws Exception {
+        @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+                OEM_NETWORK_PREFERENCE_OEM_PAID;
+
+        // Arrange users
+        final int secondUser = 10;
+        final UserHandle secondUserHandle = new UserHandle(secondUser);
+        when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+                Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle));
+
+        // Arrange PackageManager mocks
+        final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
+        final UidRangeParcel[] uidRanges =
+                toUidRangeStableParcels(
+                        uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid));
+        setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
+
+        // Verify the starting state. No networks should be connected.
+        verifySetOemNetworkPreferenceForPreference(uidRanges,
+                OEM_PREF_ANY_NET_ID, 0 /* times */,
+                OEM_PREF_ANY_NET_ID, 0 /* times */,
+                false /* shouldDestroyNetwork */);
+
+        // Test that we correctly add the expected values for multiple users.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+        verifySetOemNetworkPreferenceForPreference(uidRanges,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                OEM_PREF_ANY_NET_ID, 0 /* times */,
+                false /* shouldDestroyNetwork */);
+
+        // Test that we correctly remove the expected values for multiple users.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+        verifySetOemNetworkPreferenceForPreference(uidRanges,
+                OEM_PREF_ANY_NET_ID, 0 /* times */,
+                mCellNetworkAgent.getNetwork().netId, 0 /* times */,
+                true /* shouldDestroyNetwork */);
+    }
+
+    @Test
+    public void testMultilayerForBroadcastedUsersEvaluatesCorrectly()
+            throws Exception {
+        @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+                OEM_NETWORK_PREFERENCE_OEM_PAID;
+
+        // Arrange users
+        final int secondUser = 10;
+        final UserHandle secondUserHandle = new UserHandle(secondUser);
+        when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+                Arrays.asList(PRIMARY_USER_HANDLE));
+
+        // Arrange PackageManager mocks
+        final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
+        final UidRangeParcel[] uidRangesSingleUser =
+                toUidRangeStableParcels(
+                        uidRangesForUids(TEST_PACKAGE_UID));
+        final UidRangeParcel[] uidRangesBothUsers =
+                toUidRangeStableParcels(
+                        uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid));
+        setupSetOemNetworkPreferenceForPreferenceTest(
+                networkPref, uidRangesSingleUser, TEST_PACKAGE_NAME);
+
+        // Verify the starting state. No networks should be connected.
+        verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser,
+                OEM_PREF_ANY_NET_ID, 0 /* times */,
+                OEM_PREF_ANY_NET_ID, 0 /* times */,
+                false /* shouldDestroyNetwork */);
+
+        // Test that we correctly add the expected values for multiple users.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+        verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                OEM_PREF_ANY_NET_ID, 0 /* times */,
+                false /* shouldDestroyNetwork */);
+
+        // Send a broadcast indicating a user was added.
+        when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+                Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle));
+        final Intent addedIntent = new Intent(ACTION_USER_ADDED);
+        addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser));
+        processBroadcast(addedIntent);
+
+        // Test that we correctly add values for all users and remove for the single user.
+        verifySetOemNetworkPreferenceForPreference(uidRangesBothUsers, uidRangesSingleUser,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                false /* shouldDestroyNetwork */);
+
+        // Send a broadcast indicating a user was removed.
+        when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+                Arrays.asList(PRIMARY_USER_HANDLE));
+        final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
+        removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser));
+        processBroadcast(removedIntent);
+
+        // Test that we correctly add values for the single user and remove for the all users.
+        verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser, uidRangesBothUsers,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                false /* 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
+    }
+
+    @Test
+    public void testGetAllNetworkStateSnapshot() throws Exception {
+        verifyNoNetwork();
+
+        // Setup test cellular network with specified LinkProperties and NetworkCapabilities,
+        // verify the content of the snapshot matches.
+        final LinkProperties cellLp = new LinkProperties();
+        final LinkAddress myIpv4Addr = new LinkAddress(InetAddress.getByName("192.0.2.129"), 25);
+        final LinkAddress myIpv6Addr = new LinkAddress(InetAddress.getByName("2001:db8::1"), 64);
+        cellLp.setInterfaceName("test01");
+        cellLp.addLinkAddress(myIpv4Addr);
+        cellLp.addLinkAddress(myIpv6Addr);
+        cellLp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234")));
+        cellLp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
+        cellLp.addRoute(new RouteInfo(myIpv4Addr, null));
+        cellLp.addRoute(new RouteInfo(myIpv6Addr, null));
+        final NetworkCapabilities cellNcTemplate = new NetworkCapabilities.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).addCapability(NET_CAPABILITY_MMS).build();
+
+        final TestNetworkCallback cellCb = new TestNetworkCallback();
+        mCm.requestNetwork(new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(),
+                cellCb);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp, cellNcTemplate);
+        mCellNetworkAgent.connect(true);
+        cellCb.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+        List<NetworkStateSnapshot> snapshots = mCm.getAllNetworkStateSnapshot();
+        assertLength(1, snapshots);
+
+        // Compose the expected cellular snapshot for verification.
+        final NetworkCapabilities cellNc =
+                mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork());
+        final NetworkStateSnapshot cellSnapshot = new NetworkStateSnapshot(
+                mCellNetworkAgent.getNetwork(), cellNc, cellLp,
+                null, ConnectivityManager.TYPE_MOBILE);
+        assertEquals(cellSnapshot, snapshots.get(0));
+
+        // Connect wifi and verify the snapshots.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        waitForIdle();
+        // Compose the expected wifi snapshot for verification.
+        final NetworkCapabilities wifiNc =
+                mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork());
+        final NetworkStateSnapshot wifiSnapshot = new NetworkStateSnapshot(
+                mWiFiNetworkAgent.getNetwork(), wifiNc, new LinkProperties(), null,
+                ConnectivityManager.TYPE_WIFI);
+
+        snapshots = mCm.getAllNetworkStateSnapshot();
+        assertLength(2, snapshots);
+        assertContainsAll(snapshots, cellSnapshot, wifiSnapshot);
+
+        // Set cellular as suspended, verify the snapshots will not contain suspended networks.
+        // TODO: Consider include SUSPENDED networks, which should be considered as
+        //  temporary shortage of connectivity of a connected network.
+        mCellNetworkAgent.suspend();
+        waitForIdle();
+        snapshots = mCm.getAllNetworkStateSnapshot();
+        assertLength(1, snapshots);
+        assertEquals(wifiSnapshot, snapshots.get(0));
+
+        // Disconnect wifi, verify the snapshots contain nothing.
+        mWiFiNetworkAgent.disconnect();
+        waitForIdle();
+        snapshots = mCm.getAllNetworkStateSnapshot();
+        assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertLength(0, snapshots);
+
+        mCellNetworkAgent.resume();
+        waitForIdle();
+        snapshots = mCm.getAllNetworkStateSnapshot();
+        assertLength(1, snapshots);
+        assertEquals(cellSnapshot, snapshots.get(0));
+
+        mCellNetworkAgent.disconnect();
+        waitForIdle();
+        verifyNoNetwork();
+        mCm.unregisterNetworkCallback(cellCb);
     }
 }
diff --git a/tests/vcn/assets/self-signed-ca.pem b/tests/vcn/assets/self-signed-ca.pem
new file mode 100644
index 0000000..5135ea7
--- /dev/null
+++ b/tests/vcn/assets/self-signed-ca.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPjCCAiagAwIBAgIICrKLpR7LxlowDQYJKoZIhvcNAQELBQAwPTELMAkGA1UE
+BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxHDAaBgNVBAMTE2NhLnRlc3QuYW5kcm9p
+ZC5uZXQwHhcNMTkwNzE2MTcxNTUyWhcNMjkwNzEzMTcxNTUyWjA9MQswCQYDVQQG
+EwJVUzEQMA4GA1UEChMHQW5kcm9pZDEcMBoGA1UEAxMTY2EudGVzdC5hbmRyb2lk
+Lm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANsvTwad2Nie0VOy
+Xb1VtHL0R760Jm4vr14JWMcX4oiE6jUdTNdXQ0CGb65wvulP2aEeukFH0D/cvBMR
+Bv9+haEwo9/grIXg9ALNKp+GfuZYw/dfnUMHFn3g2+SUgP6BoMZc4lkHktjkDKxp
+99Q6h4NP/ip1labkhBeB9+Z6l78LTixKRKspNITWASJed9bjzshYxKHi6dJy3maQ
+1LwYKmK7PEGRpoDoT8yZhFbxsVDUojGnJKH1RLXVOn/psG6dI/+IsbTipAttj5zc
+g2VAD56PZG2Jd+vsup+g4Dy72hyy242x5c/H2LKZn4X0B0B+IXyii/ZVc+DJldQ5
+JqplOL8CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
+HQYDVR0OBBYEFGYUzuvZUaVJl8mcxejuFiUNGcTfMA0GCSqGSIb3DQEBCwUAA4IB
+AQDQYeqjvHsK2ZqSqxakDp0nu36Plbj48Wvx1ru7GW2faz7i0w/Zkxh06zniILCb
+QJRjDebSTHc5SSbCFrRTvqagaLDhbH42/hQncWqIoJqW+pmznJET4JiBO0sqzm05
+yQWsLI/h9Ir28Y2g5N+XPBU0VVVejQqH4iI0iwQx7y7ABssQ0Xa/K73VPbeGaKd6
+Prt4wjJvTlIL2yE2+0MggJ3F2rNptL5SDpg3g+4/YQ6wVRBFil95kUqplEsCtU4P
+t+8RghiEmsRx/8CywKfZ5Hex87ODhsSDmDApcefbd5gxoWVkqxZUkPcKwYv1ucm8
+u4r44fj4/9W0Zeooav5Yoh1q
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index 66590c9..7515971 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -203,9 +203,6 @@
         IVcnStatusCallback cbBinder =
                 new VcnStatusCallbackBinder(INLINE_EXECUTOR, mMockStatusCallback);
 
-        cbBinder.onEnteredSafeMode();
-        verify(mMockStatusCallback).onEnteredSafeMode();
-
         cbBinder.onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
         verify(mMockStatusCallback).onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
 
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java
new file mode 100644
index 0000000..bc8e9d3
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.net.vcn.persistablebundleutils;
+
+import static android.telephony.TelephonyManager.APPTYPE_USIM;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.eap.EapSessionConfig;
+import android.os.PersistableBundle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class EapSessionConfigUtilsTest {
+    private static final byte[] EAP_ID = "test@android.net".getBytes(StandardCharsets.US_ASCII);
+    private static final String USERNAME = "username";
+    private static final String PASSWORD = "password";
+    private static final int SUB_ID = 1;
+    private static final String NETWORK_NAME = "android.net";
+    private static final boolean ALLOW_MISMATCHED_NETWORK_NAMES = true;
+
+    private EapSessionConfig.Builder createBuilderWithId() {
+        return new EapSessionConfig.Builder().setEapIdentity(EAP_ID);
+    }
+
+    private static void verifyPersistableBundleEncodeDecodeIsLossless(EapSessionConfig config) {
+        final PersistableBundle bundle = EapSessionConfigUtils.toPersistableBundle(config);
+        final EapSessionConfig resultConfig = EapSessionConfigUtils.fromPersistableBundle(bundle);
+
+        assertEquals(config, resultConfig);
+    }
+
+    @Test
+    public void testSetEapMsChapV2EncodeDecodeIsLossless() throws Exception {
+        final EapSessionConfig config =
+                createBuilderWithId().setEapMsChapV2Config(USERNAME, PASSWORD).build();
+
+        verifyPersistableBundleEncodeDecodeIsLossless(config);
+    }
+
+    @Test
+    public void testSetEapSimEncodeDecodeIsLossless() throws Exception {
+        final EapSessionConfig config =
+                createBuilderWithId().setEapSimConfig(SUB_ID, APPTYPE_USIM).build();
+
+        verifyPersistableBundleEncodeDecodeIsLossless(config);
+    }
+
+    @Test
+    public void testSetEapAkaEncodeDecodeIsLossless() throws Exception {
+        final EapSessionConfig config =
+                createBuilderWithId().setEapAkaConfig(SUB_ID, APPTYPE_USIM).build();
+
+        verifyPersistableBundleEncodeDecodeIsLossless(config);
+    }
+
+    @Test
+    public void testSetEapAkaPrimeEncodeDecodeIsLossless() throws Exception {
+        final EapSessionConfig config =
+                createBuilderWithId()
+                        .setEapAkaPrimeConfig(
+                                SUB_ID, APPTYPE_USIM, NETWORK_NAME, ALLOW_MISMATCHED_NETWORK_NAMES)
+                        .build();
+
+        verifyPersistableBundleEncodeDecodeIsLossless(config);
+    }
+
+    @Test
+    public void testSetEapTtlsEncodeDecodeIsLossless() throws Exception {
+        final InputStream inputStream =
+                InstrumentationRegistry.getContext()
+                        .getResources()
+                        .getAssets()
+                        .open("self-signed-ca.pem");
+        final CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        final X509Certificate trustedCa =
+                (X509Certificate) factory.generateCertificate(inputStream);
+
+        final EapSessionConfig innerConfig =
+                new EapSessionConfig.Builder().setEapMsChapV2Config(USERNAME, PASSWORD).build();
+
+        final EapSessionConfig config =
+                new EapSessionConfig.Builder().setEapTtlsConfig(trustedCa, innerConfig).build();
+
+        verifyPersistableBundleEncodeDecodeIsLossless(config);
+    }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java
new file mode 100644
index 0000000..4f3930f
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.net.vcn.persistablebundleutils;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.ipsec.ike.IkeDerAsn1DnIdentification;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeRfc822AddrIdentification;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+import javax.security.auth.x500.X500Principal;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IkeIdentificationUtilsTest {
+    private static void verifyPersistableBundleEncodeDecodeIsLossless(IkeIdentification id) {
+        final PersistableBundle bundle = IkeIdentificationUtils.toPersistableBundle(id);
+        final IkeIdentification result = IkeIdentificationUtils.fromPersistableBundle(bundle);
+
+        assertEquals(result, id);
+    }
+
+    @Test
+    public void testPersistableBundleEncodeDecodeIpv4AddressId() throws Exception {
+        final Inet4Address ipv4Address = (Inet4Address) InetAddress.getByName("192.0.2.100");
+        verifyPersistableBundleEncodeDecodeIsLossless(new IkeIpv4AddrIdentification(ipv4Address));
+    }
+
+    @Test
+    public void testPersistableBundleEncodeDecodeIpv6AddressId() throws Exception {
+        final Inet6Address ipv6Address = (Inet6Address) InetAddress.getByName("2001:db8:2::100");
+        verifyPersistableBundleEncodeDecodeIsLossless(new IkeIpv6AddrIdentification(ipv6Address));
+    }
+
+    @Test
+    public void testPersistableBundleEncodeDecodeRfc822AddrId() throws Exception {
+        verifyPersistableBundleEncodeDecodeIsLossless(new IkeFqdnIdentification("ike.android.net"));
+    }
+
+    @Test
+    public void testPersistableBundleEncodeDecodeFqdnId() throws Exception {
+        verifyPersistableBundleEncodeDecodeIsLossless(
+                new IkeRfc822AddrIdentification("androidike@example.com"));
+    }
+
+    @Test
+    public void testPersistableBundleEncodeDecodeKeyId() throws Exception {
+        verifyPersistableBundleEncodeDecodeIsLossless(
+                new IkeKeyIdIdentification("androidIkeKeyId".getBytes()));
+    }
+
+    @Test
+    public void testPersistableBundleEncodeDecodeDerAsn1DnId() throws Exception {
+        verifyPersistableBundleEncodeDecodeIsLossless(
+                new IkeDerAsn1DnIdentification(
+                        new X500Principal("CN=small.server.test.android.net, O=Android, C=US")));
+    }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
new file mode 100644
index 0000000..8ae8692
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.net.vcn.persistablebundleutils;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.net.ipsec.ike.SaProposal;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SaProposalUtilsTest {
+    @Test
+    public void testPersistableBundleEncodeDecodeIsLosslessIkeProposal() throws Exception {
+        final IkeSaProposal proposal =
+                new IkeSaProposal.Builder()
+                        .addEncryptionAlgorithm(
+                                SaProposal.ENCRYPTION_ALGORITHM_3DES, SaProposal.KEY_LEN_UNUSED)
+                        .addEncryptionAlgorithm(
+                                SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128)
+                        .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96)
+                        .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+                        .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC)
+                        .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256)
+                        .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
+                        .addDhGroup(SaProposal.DH_GROUP_3072_BIT_MODP)
+                        .build();
+
+        final PersistableBundle bundle = IkeSaProposalUtils.toPersistableBundle(proposal);
+        final SaProposal resultProposal = IkeSaProposalUtils.fromPersistableBundle(bundle);
+
+        assertEquals(proposal, resultProposal);
+    }
+
+    /** Package private so that TunnelModeChildSessionParamsUtilsTest can use it */
+    static ChildSaProposal buildTestChildSaProposal() {
+        return new ChildSaProposal.Builder()
+                .addEncryptionAlgorithm(
+                        SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
+                .addEncryptionAlgorithm(
+                        SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_192)
+                .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
+                .addDhGroup(SaProposal.DH_GROUP_4096_BIT_MODP)
+                .build();
+    }
+
+    @Test
+    public void testPersistableBundleEncodeDecodeIsLosslessChildProposal() throws Exception {
+        final ChildSaProposal proposal = buildTestChildSaProposal();
+
+        final PersistableBundle bundle = ChildSaProposalUtils.toPersistableBundle(proposal);
+        final SaProposal resultProposal = ChildSaProposalUtils.fromPersistableBundle(bundle);
+
+        assertEquals(proposal, resultProposal);
+    }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
new file mode 100644
index 0000000..b3cd0ab
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.net.vcn.persistablebundleutils;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.InetAddresses;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TunnelModeChildSessionParamsUtilsTest {
+    private TunnelModeChildSessionParams.Builder createBuilderMinimum() {
+        final ChildSaProposal saProposal = SaProposalUtilsTest.buildTestChildSaProposal();
+        return new TunnelModeChildSessionParams.Builder().addSaProposal(saProposal);
+    }
+
+    private static void verifyPersistableBundleEncodeDecodeIsLossless(
+            TunnelModeChildSessionParams params) {
+        final PersistableBundle bundle =
+                TunnelModeChildSessionParamsUtils.toPersistableBundle(params);
+        final TunnelModeChildSessionParams result =
+                TunnelModeChildSessionParamsUtils.fromPersistableBundle(bundle);
+
+        assertEquals(params, result);
+    }
+
+    @Test
+    public void testMinimumParamsEncodeDecodeIsLossless() throws Exception {
+        final TunnelModeChildSessionParams sessionParams = createBuilderMinimum().build();
+        verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+    }
+
+    @Test
+    public void testSetTsEncodeDecodeIsLossless() throws Exception {
+        final IkeTrafficSelector tsInbound =
+                new IkeTrafficSelector(
+                        16,
+                        65520,
+                        InetAddresses.parseNumericAddress("192.0.2.100"),
+                        InetAddresses.parseNumericAddress("192.0.2.101"));
+        final IkeTrafficSelector tsOutbound =
+                new IkeTrafficSelector(
+                        32,
+                        256,
+                        InetAddresses.parseNumericAddress("192.0.2.200"),
+                        InetAddresses.parseNumericAddress("192.0.2.255"));
+
+        final TunnelModeChildSessionParams sessionParams =
+                createBuilderMinimum()
+                        .addInboundTrafficSelectors(tsInbound)
+                        .addOutboundTrafficSelectors(tsOutbound)
+                        .build();
+        verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+    }
+
+    @Test
+    public void testSetLifetimesEncodeDecodeIsLossless() throws Exception {
+        final int hardLifetime = (int) TimeUnit.HOURS.toSeconds(3L);
+        final int softLifetime = (int) TimeUnit.HOURS.toSeconds(1L);
+
+        final TunnelModeChildSessionParams sessionParams =
+                createBuilderMinimum().setLifetimeSeconds(hardLifetime, softLifetime).build();
+        verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+    }
+
+    @Test
+    public void testSetConfigRequestsEncodeDecodeIsLossless() throws Exception {
+        final int ipv6PrefixLen = 64;
+        final Inet4Address ipv4Address =
+                (Inet4Address) InetAddresses.parseNumericAddress("192.0.2.100");
+        final Inet6Address ipv6Address =
+                (Inet6Address) InetAddresses.parseNumericAddress("2001:db8::1");
+
+        final TunnelModeChildSessionParams sessionParams =
+                createBuilderMinimum()
+                        .addInternalAddressRequest(AF_INET)
+                        .addInternalAddressRequest(AF_INET6)
+                        .addInternalAddressRequest(ipv4Address)
+                        .addInternalAddressRequest(ipv6Address, ipv6PrefixLen)
+                        .addInternalDnsServerRequest(AF_INET)
+                        .addInternalDnsServerRequest(AF_INET6)
+                        .addInternalDhcpServerRequest(AF_INET)
+                        .build();
+        verifyPersistableBundleEncodeDecodeIsLossless(sessionParams);
+    }
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 9b500a7..73a6b88 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -100,6 +100,8 @@
 public class VcnManagementServiceTest {
     private static final String TEST_PACKAGE_NAME =
             VcnManagementServiceTest.class.getPackage().getName();
+    private static final String TEST_CB_PACKAGE_NAME =
+            VcnManagementServiceTest.class.getPackage().getName() + ".callback";
     private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0));
     private static final ParcelUuid TEST_UUID_2 = new ParcelUuid(new UUID(1, 1));
     private static final VcnConfig TEST_VCN_CONFIG;
@@ -288,6 +290,14 @@
 
     private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
             Set<ParcelUuid> activeSubscriptionGroups, Map<Integer, ParcelUuid> subIdToGroupMap) {
+        return triggerSubscriptionTrackerCbAndGetSnapshot(
+                activeSubscriptionGroups, subIdToGroupMap, true /* hasCarrierPrivileges */);
+    }
+
+    private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
+            Set<ParcelUuid> activeSubscriptionGroups,
+            Map<Integer, ParcelUuid> subIdToGroupMap,
+            boolean hasCarrierPrivileges) {
         final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
         doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups();
 
@@ -295,7 +305,7 @@
                 (activeSubscriptionGroups == null || activeSubscriptionGroups.isEmpty())
                         ? Collections.emptySet()
                         : Collections.singleton(TEST_PACKAGE_NAME);
-        doReturn(true)
+        doReturn(hasCarrierPrivileges)
                 .when(snapshot)
                 .packageHasPermissionsForSubscriptionGroup(
                         argThat(val -> activeSubscriptionGroups.contains(val)),
@@ -549,13 +559,6 @@
         mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
     }
 
-    private void setUpVcnSubscription(int subId, ParcelUuid subGroup) {
-        mVcnMgmtSvc.setVcnConfig(subGroup, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
-
-        triggerSubscriptionTrackerCbAndGetSnapshot(
-                Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
-    }
-
     private void verifyMergedNetworkCapabilities(
             NetworkCapabilities mergedCapabilities,
             @Transport int transportType,
@@ -573,9 +576,23 @@
     }
 
     private void setupSubscriptionAndStartVcn(int subId, ParcelUuid subGrp, boolean isVcnActive) {
-        setUpVcnSubscription(subId, subGrp);
+        setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive, true /* hasCarrierPrivileges */);
+    }
+
+    private void setupSubscriptionAndStartVcn(
+            int subId, ParcelUuid subGrp, boolean isVcnActive, boolean hasCarrierPrivileges) {
+        mVcnMgmtSvc.systemReady();
+        triggerSubscriptionTrackerCbAndGetSnapshot(
+                Collections.singleton(subGrp),
+                Collections.singletonMap(subId, subGrp),
+                hasCarrierPrivileges);
+
         final Vcn vcn = startAndGetVcnInstance(subGrp);
         doReturn(isVcnActive).when(vcn).isActive();
+
+        doReturn(true)
+                .when(mLocationPermissionChecker)
+                .checkLocationPermission(eq(TEST_PACKAGE_NAME), any(), eq(TEST_UID), any());
     }
 
     private VcnUnderlyingNetworkPolicy startVcnAndGetPolicyForTransport(
@@ -721,7 +738,7 @@
         verify(mMockPolicyListener).onPolicyChanged();
     }
 
-    private void verifyVcnCallback(
+    private void triggerVcnSafeMode(
             @NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot)
             throws Exception {
         verify(mMockDeps)
@@ -732,20 +749,20 @@
                         eq(snapshot),
                         mVcnCallbackCaptor.capture());
 
-        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
-
         VcnCallback vcnCallback = mVcnCallbackCaptor.getValue();
         vcnCallback.onEnteredSafeMode();
-
-        verify(mMockPolicyListener).onPolicyChanged();
     }
 
     @Test
-    public void testVcnCallbackOnEnteredSafeMode() throws Exception {
+    public void testVcnEnteringSafeModeNotifiesPolicyListeners() throws Exception {
         TelephonySubscriptionSnapshot snapshot =
                 triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
 
-        verifyVcnCallback(TEST_UUID_1, snapshot);
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        triggerVcnSafeMode(TEST_UUID_1, snapshot);
+
+        verify(mMockPolicyListener).onPolicyChanged();
     }
 
     private void triggerVcnStatusCallbackOnEnteredSafeMode(
@@ -758,6 +775,9 @@
         TelephonySubscriptionSnapshot snapshot =
                 triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(subGroup));
 
+        setupSubscriptionAndStartVcn(
+                TEST_SUBSCRIPTION_ID, subGroup, true /* isActive */, hasPermissionsforSubGroup);
+
         doReturn(hasPermissionsforSubGroup)
                 .when(snapshot)
                 .packageHasPermissionsForSubscriptionGroup(eq(subGroup), eq(pkgName));
@@ -768,10 +788,7 @@
 
         mVcnMgmtSvc.registerVcnStatusCallback(subGroup, mMockStatusCallback, pkgName);
 
-        // Trigger systemReady() to set up LocationPermissionChecker
-        mVcnMgmtSvc.systemReady();
-
-        verifyVcnCallback(subGroup, snapshot);
+        triggerVcnSafeMode(subGroup, snapshot);
     }
 
     @Test
@@ -825,6 +842,83 @@
         assertEquals(TEST_PACKAGE_NAME, cbInfo.mPkgName);
         assertEquals(TEST_UID, cbInfo.mUid);
         verify(mMockIBinder).linkToDeath(eq(cbInfo), anyInt());
+
+        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED);
+    }
+
+    @Test
+    public void testRegisterVcnStatusCallback_MissingPermission() throws Exception {
+        setupSubscriptionAndStartVcn(
+                TEST_SUBSCRIPTION_ID,
+                TEST_UUID_1,
+                true /* isActive */,
+                false /* hasCarrierPrivileges */);
+
+        mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+
+        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED);
+    }
+
+    @Test
+    public void testRegisterVcnStatusCallback_VcnInactive() throws Exception {
+        setupSubscriptionAndStartVcn(
+                TEST_SUBSCRIPTION_ID,
+                TEST_UUID_1,
+                true /* isActive */,
+                true /* hasCarrierPrivileges */);
+
+        // VCN is currently active. Lose carrier privileges for TEST_PACKAGE and hit teardown
+        // timeout so the VCN goes inactive.
+        final TelephonySubscriptionSnapshot snapshot =
+                triggerSubscriptionTrackerCbAndGetSnapshot(
+                        Collections.singleton(TEST_UUID_1),
+                        Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_1),
+                        false /* hasCarrierPrivileges */);
+        mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+        mTestLooper.dispatchAll();
+
+        // Giving TEST_PACKAGE privileges again will restart the VCN (which will indicate ACTIVE
+        // when the status callback is registered). Instead, setup permissions for TEST_CB_PACKAGE
+        // so that it's permissioned to receive INACTIVE (instead of NOT_CONFIGURED) without
+        // reactivating the VCN.
+        doReturn(true)
+                .when(snapshot)
+                .packageHasPermissionsForSubscriptionGroup(
+                        eq(TEST_UUID_1), eq(TEST_CB_PACKAGE_NAME));
+        doReturn(true)
+                .when(mLocationPermissionChecker)
+                .checkLocationPermission(eq(TEST_CB_PACKAGE_NAME), any(), eq(TEST_UID), any());
+
+        mVcnMgmtSvc.registerVcnStatusCallback(
+                TEST_UUID_1, mMockStatusCallback, TEST_CB_PACKAGE_NAME);
+
+        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_INACTIVE);
+    }
+
+    @Test
+    public void testRegisterVcnStatusCallback_VcnActive() throws Exception {
+        setupSubscriptionAndStartVcn(
+                TEST_SUBSCRIPTION_ID,
+                TEST_UUID_1,
+                true /* isActive */,
+                true /* hasCarrierPrivileges */);
+
+        mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+
+        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_ACTIVE);
+    }
+
+    @Test
+    public void testRegisterVcnStatusCallback_VcnSafeMode() throws Exception {
+        setupSubscriptionAndStartVcn(
+                TEST_SUBSCRIPTION_ID,
+                TEST_UUID_1,
+                false /* isActive */,
+                true /* hasCarrierPrivileges */);
+
+        mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+
+        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
     }
 
     @Test(expected = IllegalStateException.class)
diff --git a/tools/bit/make.cpp b/tools/bit/make.cpp
index c39f494..2a88732 100644
--- a/tools/bit/make.cpp
+++ b/tools/bit/make.cpp
@@ -90,8 +90,7 @@
 
     Json::Value json;
     Json::CharReaderBuilder builder;
-    std::string errorMessage;
-    if (!Json::parseFromStream(builder, stream, &json, &errorMessage)) {
+    if (!Json::parseFromStream(builder, stream, &json, /* errorMessage = */ nullptr)) {
         return;
     }
 
@@ -215,8 +214,7 @@
 
     Json::Value json;
     Json::CharReaderBuilder builder;
-    std::string errorMessage;
-    if (!Json::parseFromStream(builder, stream, &json, &errorMessage)) {
+    if (!Json::parseFromStream(builder, stream, &json, /* errorMessage = */ nullptr)) {
         json_error(filename, "can't parse json format", quiet);
         return;
     }